From d3da67c03dc7d19782d60d82fdf8fc7c7895280e Mon Sep 17 00:00:00 2001 From: Andy Clement Date: Fri, 25 Jan 2019 15:14:30 -0800 Subject: mavenizing weaver - wip --- weaver/.classpath | 61 +- weaver/.cvsignore | 6 - weaver/.isJava5 | 1 - weaver/.project | 11 +- weaver/.settings/org.eclipse.core.resources.prefs | 5 + weaver/.settings/org.eclipse.jdt.apt.core.prefs | 2 + weaver/.settings/org.eclipse.jdt.core.prefs | 16 +- weaver/.settings/org.eclipse.jdt.ui.prefs | 3 - weaver/.settings/org.eclipse.m2e.core.prefs | 4 + weaver/build.xml | 6 - weaver/pom.xml | 85 + weaver/src/aspectj_1_5_0.dtd | 149 - .../org/aspectj/weaver/IClassFileProvider.java | 44 + .../weaver/bcel/AnnotationAccessFieldVar.java | 186 ++ .../aspectj/weaver/bcel/AnnotationAccessVar.java | 272 ++ .../org/aspectj/weaver/bcel/AspectInstanceVar.java | 119 + .../org/aspectj/weaver/bcel/AtAjAttributes.java | 2046 ++++++++++++ .../weaver/bcel/BcelAccessForInlineMunger.java | 386 +++ .../java/org/aspectj/weaver/bcel/BcelAdvice.java | 801 +++++ .../org/aspectj/weaver/bcel/BcelAnnotation.java | 151 + .../aspectj/weaver/bcel/BcelCflowAccessVar.java | 87 + .../weaver/bcel/BcelCflowCounterFieldAdder.java | 88 + .../weaver/bcel/BcelCflowStackFieldAdder.java | 77 + .../org/aspectj/weaver/bcel/BcelClassWeaver.java | 3377 +++++++++++++++++++ .../weaver/bcel/BcelConstantPoolReader.java | 34 + .../weaver/bcel/BcelConstantPoolWriter.java | 34 + .../java/org/aspectj/weaver/bcel/BcelField.java | 307 ++ .../java/org/aspectj/weaver/bcel/BcelFieldRef.java | 100 + .../bcel/BcelGenericSignatureToTypeXConverter.java | 278 ++ .../java/org/aspectj/weaver/bcel/BcelMethod.java | 714 ++++ .../org/aspectj/weaver/bcel/BcelObjectType.java | 1023 ++++++ .../weaver/bcel/BcelPerClauseAspectAdder.java | 560 ++++ .../java/org/aspectj/weaver/bcel/BcelRenderer.java | 270 ++ .../java/org/aspectj/weaver/bcel/BcelShadow.java | 3425 ++++++++++++++++++++ .../org/aspectj/weaver/bcel/BcelTypeMunger.java | 2116 ++++++++++++ .../main/java/org/aspectj/weaver/bcel/BcelVar.java | 117 + .../weaver/bcel/BcelWeakClassLoaderReference.java | 49 + .../java/org/aspectj/weaver/bcel/BcelWeaver.java | 2043 ++++++++++++ .../aspectj/weaver/bcel/BcelWeavingSupport.java | 71 + .../java/org/aspectj/weaver/bcel/BcelWorld.java | 1329 ++++++++ .../org/aspectj/weaver/bcel/ClassPathManager.java | 578 ++++ .../org/aspectj/weaver/bcel/ExceptionRange.java | 151 + .../weaver/bcel/ExtensibleURLClassLoader.java | 113 + .../org/aspectj/weaver/bcel/FakeAnnotation.java | 80 + .../java/org/aspectj/weaver/bcel/IfFinder.java | 56 + .../java/org/aspectj/weaver/bcel/LazyClassGen.java | 1940 +++++++++++ .../org/aspectj/weaver/bcel/LazyMethodGen.java | 1870 +++++++++++ .../main/java/org/aspectj/weaver/bcel/Range.java | 243 ++ .../java/org/aspectj/weaver/bcel/ShadowRange.java | 244 ++ .../weaver/bcel/TypeAnnotationAccessVar.java | 80 + .../aspectj/weaver/bcel/TypeDelegateResolver.java | 28 + .../org/aspectj/weaver/bcel/UnwovenClassFile.java | 200 ++ ...ovenClassFileWithThirdPartyManagedBytecode.java | 36 + .../main/java/org/aspectj/weaver/bcel/Utility.java | 719 ++++ .../org/aspectj/weaver/bcel/asm/AsmDetector.java | 36 + .../org/aspectj/weaver/bcel/asm/StackMapAdder.java | 121 + .../aspectj/weaver/loadtime/IWeavingContext.java | 92 + .../weaver/loadtime/definition/Definition.java | 222 ++ .../weaver/loadtime/definition/DocumentParser.java | 382 +++ .../weaver/loadtime/definition/LightXMLParser.java | 471 +++ .../loadtime/definition/SimpleAOPParser.java | 265 ++ .../main/java/org/aspectj/weaver/ltw/LTWWorld.java | 294 ++ .../weaver/model/AsmRelationshipProvider.java | 1133 +++++++ .../aspectj/weaver/model/AsmRelationshipUtils.java | 79 + .../org/aspectj/weaver/reflect/ArgNameFinder.java | 26 + .../DeferredResolvedPointcutDefinition.java | 35 + .../reflect/InternalUseOnlyPointcutParser.java | 43 + .../weaver/reflect/Java15AnnotationFinder.java | 386 +++ .../Java15GenericSignatureInformationProvider.java | 103 + ...Java15ReflectionBasedReferenceTypeDelegate.java | 389 +++ .../JavaLangTypeToResolvedTypeConverter.java | 134 + .../java/org/aspectj/weaver/tools/Jdk14Trace.java | 127 + .../aspectj/weaver/tools/Jdk14TraceFactory.java | 20 + .../org/aspectj/weaver/tools/WeavingAdaptor.java | 951 ++++++ .../weaver/tools/cache/AbstractCacheBacking.java | 45 + .../tools/cache/AbstractFileCacheBacking.java | 86 + .../cache/AbstractIndexedFileCacheBacking.java | 262 ++ .../tools/cache/AsynchronousFileCacheBacking.java | 424 +++ .../aspectj/weaver/tools/cache/CacheBacking.java | 63 + .../aspectj/weaver/tools/cache/CacheFactory.java | 25 + .../weaver/tools/cache/CacheKeyResolver.java | 88 + .../weaver/tools/cache/CacheStatistics.java | 107 + .../weaver/tools/cache/CachedClassEntry.java | 90 + .../weaver/tools/cache/CachedClassReference.java | 85 + .../weaver/tools/cache/DefaultCacheFactory.java | 26 + .../tools/cache/DefaultCacheKeyResolver.java | 117 + .../tools/cache/DefaultFileCacheBacking.java | 289 ++ .../weaver/tools/cache/FlatFileCacheBacking.java | 139 + .../tools/cache/GeneratedCachedClassHandler.java | 39 + .../aspectj/weaver/tools/cache/SimpleCache.java | 377 +++ .../weaver/tools/cache/SimpleCacheFactory.java | 104 + .../weaver/tools/cache/WeavedClassCache.java | 278 ++ .../weaver/tools/cache/ZippedFileCacheBacking.java | 321 ++ weaver/src/main/resources/aspectj_1_5_0.dtd | 149 + .../src/org/aspectj/weaver/IClassFileProvider.java | 44 - .../weaver/bcel/AnnotationAccessFieldVar.java | 186 -- .../aspectj/weaver/bcel/AnnotationAccessVar.java | 272 -- .../org/aspectj/weaver/bcel/AspectInstanceVar.java | 119 - .../org/aspectj/weaver/bcel/AtAjAttributes.java | 2046 ------------ .../weaver/bcel/BcelAccessForInlineMunger.java | 386 --- weaver/src/org/aspectj/weaver/bcel/BcelAdvice.java | 801 ----- .../org/aspectj/weaver/bcel/BcelAnnotation.java | 151 - .../aspectj/weaver/bcel/BcelCflowAccessVar.java | 87 - .../weaver/bcel/BcelCflowCounterFieldAdder.java | 88 - .../weaver/bcel/BcelCflowStackFieldAdder.java | 77 - .../org/aspectj/weaver/bcel/BcelClassWeaver.java | 3377 ------------------- .../weaver/bcel/BcelConstantPoolReader.java | 34 - .../weaver/bcel/BcelConstantPoolWriter.java | 34 - weaver/src/org/aspectj/weaver/bcel/BcelField.java | 307 -- .../src/org/aspectj/weaver/bcel/BcelFieldRef.java | 100 - .../bcel/BcelGenericSignatureToTypeXConverter.java | 278 -- weaver/src/org/aspectj/weaver/bcel/BcelMethod.java | 714 ---- .../org/aspectj/weaver/bcel/BcelObjectType.java | 1023 ------ .../weaver/bcel/BcelPerClauseAspectAdder.java | 560 ---- .../src/org/aspectj/weaver/bcel/BcelRenderer.java | 270 -- weaver/src/org/aspectj/weaver/bcel/BcelShadow.java | 3425 -------------------- .../org/aspectj/weaver/bcel/BcelTypeMunger.java | 2116 ------------ weaver/src/org/aspectj/weaver/bcel/BcelVar.java | 117 - .../weaver/bcel/BcelWeakClassLoaderReference.java | 49 - weaver/src/org/aspectj/weaver/bcel/BcelWeaver.java | 2043 ------------ .../aspectj/weaver/bcel/BcelWeavingSupport.java | 71 - weaver/src/org/aspectj/weaver/bcel/BcelWorld.java | 1329 -------- .../org/aspectj/weaver/bcel/ClassPathManager.java | 579 ---- .../org/aspectj/weaver/bcel/ExceptionRange.java | 151 - .../weaver/bcel/ExtensibleURLClassLoader.java | 113 - .../org/aspectj/weaver/bcel/FakeAnnotation.java | 80 - weaver/src/org/aspectj/weaver/bcel/IfFinder.java | 56 - .../src/org/aspectj/weaver/bcel/LazyClassGen.java | 1940 ----------- .../src/org/aspectj/weaver/bcel/LazyMethodGen.java | 1870 ----------- weaver/src/org/aspectj/weaver/bcel/Range.java | 243 -- .../src/org/aspectj/weaver/bcel/ShadowRange.java | 244 -- .../weaver/bcel/TypeAnnotationAccessVar.java | 80 - .../aspectj/weaver/bcel/TypeDelegateResolver.java | 28 - .../org/aspectj/weaver/bcel/UnwovenClassFile.java | 200 -- ...ovenClassFileWithThirdPartyManagedBytecode.java | 36 - weaver/src/org/aspectj/weaver/bcel/Utility.java | 719 ---- .../org/aspectj/weaver/bcel/asm/AsmDetector.java | 36 - .../org/aspectj/weaver/bcel/asm/StackMapAdder.java | 121 - .../aspectj/weaver/loadtime/IWeavingContext.java | 92 - .../weaver/loadtime/definition/Definition.java | 222 -- .../weaver/loadtime/definition/DocumentParser.java | 382 --- .../weaver/loadtime/definition/LightXMLParser.java | 471 --- .../loadtime/definition/SimpleAOPParser.java | 265 -- weaver/src/org/aspectj/weaver/ltw/LTWWorld.java | 294 -- .../weaver/model/AsmRelationshipProvider.java | 1133 ------- .../aspectj/weaver/model/AsmRelationshipUtils.java | 79 - .../org/aspectj/weaver/tools/WeavingAdaptor.java | 951 ------ .../weaver/tools/cache/AbstractCacheBacking.java | 45 - .../tools/cache/AbstractFileCacheBacking.java | 86 - .../cache/AbstractIndexedFileCacheBacking.java | 262 -- .../tools/cache/AsynchronousFileCacheBacking.java | 424 --- .../aspectj/weaver/tools/cache/CacheBacking.java | 63 - .../aspectj/weaver/tools/cache/CacheFactory.java | 25 - .../weaver/tools/cache/CacheKeyResolver.java | 88 - .../weaver/tools/cache/CacheStatistics.java | 107 - .../weaver/tools/cache/CachedClassEntry.java | 90 - .../weaver/tools/cache/CachedClassReference.java | 85 - .../weaver/tools/cache/DefaultCacheFactory.java | 26 - .../tools/cache/DefaultCacheKeyResolver.java | 117 - .../tools/cache/DefaultFileCacheBacking.java | 289 -- .../weaver/tools/cache/FlatFileCacheBacking.java | 139 - .../tools/cache/GeneratedCachedClassHandler.java | 39 - .../aspectj/weaver/tools/cache/SimpleCache.java | 377 --- .../weaver/tools/cache/SimpleCacheFactory.java | 104 - .../weaver/tools/cache/WeavedClassCache.java | 278 -- .../weaver/tools/cache/ZippedFileCacheBacking.java | 321 -- weaver/src/test/java/$Proxy1.java | 28 + weaver/src/test/java/CounterAspect.java | 52 + weaver/src/test/java/GenericService.java | 17 + weaver/src/test/java/MA.java | 15 + weaver/src/test/java/MB.java | 15 + weaver/src/test/java/MC.java | 15 + weaver/src/test/java/MD.java | 15 + weaver/src/test/java/MessageService.java | 13 + weaver/src/test/java/fluffy/Aspect.java | 29 + weaver/src/test/java/fluffy/Base.java | 18 + weaver/src/test/java/fluffy/Derived.java | 20 + ...lectionWorldAdvancedPointcutExpressionTest.java | 30 + .../java/org/aspectj/weaver/AbstractTraceTest.java | 153 + .../java/org/aspectj/weaver/AllWeaver5Tests.java | 40 + .../org/aspectj/weaver/BcweaverModule15Test.java | 41 + .../weaver/BoundedReferenceTypeTestCase.java | 106 + .../aspectj/weaver/CommonReferenceTypeTests.java | 91 + .../aspectj/weaver/CommonsTraceFactoryTest.java | 26 + .../java/org/aspectj/weaver/CommonsTraceTest.java | 35 + .../aspectj/weaver/DefaultTraceFactoryTest.java | 30 + .../java/org/aspectj/weaver/DefaultTraceTest.java | 38 + .../test/java/org/aspectj/weaver/DumpTestCase.java | 147 + .../aspectj/weaver/GenericSignatureParserTest.java | 63 + ...a5ReflectionBasedReferenceTypeDelegateTest.java | 154 + .../org/aspectj/weaver/Jdk14TraceFactoryTest.java | 30 + .../java/org/aspectj/weaver/Jdk14TraceTest.java | 34 + .../weaver/JoinPointSignatureIteratorTest.java | 107 + .../test/java/org/aspectj/weaver/LocaleTest.java | 53 + .../test/java/org/aspectj/weaver/Member15Test.java | 155 + .../java/org/aspectj/weaver/Member15TestCase.java | 81 + .../java/org/aspectj/weaver/MemberTestCase.java | 183 ++ .../weaver/ParameterizedReferenceTypeTestCase.java | 84 + .../org/aspectj/weaver/ReferenceTypeTestCase.java | 854 +++++ .../weaver/ResolvedMemberSignatures15TestCase.java | 281 ++ .../test/java/org/aspectj/weaver/TestShadow.java | 131 + .../java/org/aspectj/weaver/TraceFactoryTest.java | 31 + .../weaver/TypeVariableReferenceTypeTestCase.java | 81 + .../org/aspectj/weaver/TypeVariableTestCase.java | 110 + .../java/org/aspectj/weaver/TypeXTestCase.java | 219 ++ .../org/aspectj/weaver/Weaver5ModuleTests.java | 37 + .../org/aspectj/weaver/WeaverMessagesTestCase.java | 47 + .../java/org/aspectj/weaver/WeaverTestCase.java | 50 + .../weaver/bcel/AfterReturningWeaveTestCase.java | 58 + .../weaver/bcel/AfterThrowingWeaveTestCase.java | 45 + .../aspectj/weaver/bcel/AfterWeaveTestCase.java | 31 + .../org/aspectj/weaver/bcel/ArgsWeaveTestCase.java | 119 + .../weaver/bcel/AroundArgsWeaveTestCase.java | 41 + .../aspectj/weaver/bcel/AroundWeaveTestCase.java | 100 + .../bcel/BcelGenericSignatureToTypeXTestCase.java | 68 + .../org/aspectj/weaver/bcel/BcelTestUtils.java | 65 + .../weaver/bcel/BcelWorldReferenceTypeTest.java | 23 + .../aspectj/weaver/bcel/BeforeWeaveTestCase.java | 31 + .../org/aspectj/weaver/bcel/CheckerTestCase.java | 48 + .../weaver/bcel/ClassLoaderRepositoryTest.java | 213 ++ .../org/aspectj/weaver/bcel/FieldSetTestCase.java | 34 + .../weaver/bcel/HierarchyDependsTestCase.java | 63 + .../org/aspectj/weaver/bcel/IdWeaveTestCase.java | 101 + .../org/aspectj/weaver/bcel/JImageTestCase.java | 139 + .../org/aspectj/weaver/bcel/MegaZipTestCase.java | 107 + .../weaver/bcel/MoveInstructionsWeaveTestCase.java | 81 + .../weaver/bcel/NonstaticWeaveTestCase.java | 83 + .../aspectj/weaver/bcel/PatternWeaveTestCase.java | 122 + .../weaver/bcel/PointcutResidueTestCase.java | 189 ++ .../org/aspectj/weaver/bcel/TjpWeaveTestCase.java | 99 + .../aspectj/weaver/bcel/TraceJarWeaveTestCase.java | 40 + .../org/aspectj/weaver/bcel/UtilityTestCase.java | 52 + .../aspectj/weaver/bcel/WeaveOrderTestCase.java | 149 + .../org/aspectj/weaver/bcel/WeaveTestCase.java | 318 ++ .../org/aspectj/weaver/bcel/WorldTestCase.java | 163 + .../org/aspectj/weaver/bcel/ZipFileWeaver.java | 39 + .../java/org/aspectj/weaver/bcel/ZipTestCase.java | 121 + .../AnnotationPatternMatchingTestCase.java | 252 ++ .../weaver/patterns/AnnotationPatternTestCase.java | 382 +++ .../weaver/patterns/ConcretizationTestCase.java | 116 + .../WildTypePatternResolutionTestCase.java | 422 +++ .../weaver/patterns/bcel/BcelAndOrNotTestCase.java | 24 + .../weaver/patterns/bcel/BcelBindingTestCase.java | 24 + .../bcel/BcelModifiersPatternTestCase.java | 24 + .../weaver/patterns/bcel/BcelParserTestCase.java | 25 + .../bcel/BcelSignaturePatternTestCase.java | 24 + .../patterns/bcel/BcelTypePatternListTestCase.java | 23 + .../patterns/bcel/BcelTypePatternTestCase.java | 24 + .../weaver/patterns/bcel/BcelWithinTestCase.java | 23 + .../ReflectionBasedReferenceTypeDelegateTest.java | 316 ++ .../reflect/ReflectionWorldReferenceTypeTest.java | 27 + .../weaver/reflect/ReflectionWorldTest.java | 290 ++ .../weaver/tools/Java15PointcutExpressionTest.java | 746 +++++ .../tools/PointcutDesignatorHandlerTest.java | 251 ++ .../weaver/tools/PointcutExpressionTest.java | 596 ++++ .../aspectj/weaver/tools/PointcutParserTest.java | 391 +++ .../weaver/tools/ReadingAttributesTest.java | 66 + .../weaver/tools/TypePatternMatcherTest.java | 55 + .../cache/AbstractCacheBackingTestSupport.java | 379 +++ .../AsynchronousFileCacheBackingTestSupport.java | 193 ++ .../tools/cache/DefaultCacheKeyResolverTest.java | 95 + .../tools/cache/DefaultFileCacheBackingTest.java | 179 + .../tools/cache/FlatFileCacheBackingTest.java | 147 + .../weaver/tools/cache/SimpleClassCacheTest.java | 71 + .../weaver/tools/cache/WeavedClassCacheTest.java | 152 + .../tools/cache/ZippedFileCacheBackingTest.java | 154 + weaver/src/test/java/reflect/tests/C.java | 33 + weaver/src/test/java/test/A.java | 27 + weaver/src/test/java/test/A1.java | 20 + weaver/src/test/java/test/A1AnnotatedType.java | 17 + weaver/src/test/java/test/A2.java | 20 + weaver/src/test/java/test/A2AnnotatedType.java | 17 + weaver/src/test/java/test/A3.java | 19 + weaver/src/test/java/test/AnnoValues.java | 20 + weaver/src/test/java/test/Color.java | 14 + weaver/testdata/logging.properties | 60 + weaver/testsrc/fluffy/Aspect.java | 29 - weaver/testsrc/fluffy/Base.java | 18 - weaver/testsrc/fluffy/Derived.java | 22 - .../org/aspectj/weaver/AbstractTraceTest.java | 153 - .../org/aspectj/weaver/AllTracingTests.java | 30 - .../org/aspectj/weaver/BcweaverModuleTests.java | 41 - .../testsrc/org/aspectj/weaver/BcweaverTests.java | 64 - .../weaver/BoundedReferenceTypeTestCase.java | 106 - .../aspectj/weaver/CommonsTraceFactoryTest.java | 26 - .../org/aspectj/weaver/CommonsTraceTest.java | 35 - .../aspectj/weaver/DefaultTraceFactoryTest.java | 30 - .../org/aspectj/weaver/DefaultTraceTest.java | 38 - .../testsrc/org/aspectj/weaver/DumpTestCase.java | 147 - .../aspectj/weaver/GenericSignatureParserTest.java | 63 - weaver/testsrc/org/aspectj/weaver/LocaleTest.java | 53 - .../testsrc/org/aspectj/weaver/MemberTestCase.java | 183 -- .../org/aspectj/weaver/MemberTestCase15.java | 81 - .../weaver/ParameterizedReferenceTypeTestCase.java | 81 - .../weaver/ResolvedMemberSignaturesTestCase15.java | 286 -- weaver/testsrc/org/aspectj/weaver/TestShadow.java | 131 - .../org/aspectj/weaver/TraceFactoryTest.java | 31 - .../weaver/TypeVariableReferenceTypeTestCase.java | 50 - .../org/aspectj/weaver/TypeVariableTestCase.java | 104 - .../testsrc/org/aspectj/weaver/TypeXTestCase.java | 219 -- .../org/aspectj/weaver/WeaverMessagesTestCase.java | 47 - .../weaver/bcel/AfterReturningWeaveTestCase.java | 58 - .../weaver/bcel/AfterThrowingWeaveTestCase.java | 45 - .../aspectj/weaver/bcel/AfterWeaveTestCase.java | 31 - .../org/aspectj/weaver/bcel/ArgsWeaveTestCase.java | 119 - .../weaver/bcel/AroundArgsWeaveTestCase.java | 41 - .../aspectj/weaver/bcel/AroundWeaveTestCase.java | 100 - .../bcel/BcelGenericSignatureToTypeXTestCase.java | 87 - .../org/aspectj/weaver/bcel/BcelTestUtils.java | 65 - .../testsrc/org/aspectj/weaver/bcel/BcelTests.java | 57 - .../aspectj/weaver/bcel/BeforeWeaveTestCase.java | 31 - .../org/aspectj/weaver/bcel/CheckerTestCase.java | 48 - .../weaver/bcel/ClassLoaderRepositoryTests.java | 213 -- .../org/aspectj/weaver/bcel/FieldSetTestCase.java | 34 - .../weaver/bcel/HierarchyDependsTestCase.java | 63 - .../org/aspectj/weaver/bcel/IdWeaveTestCase.java | 101 - .../org/aspectj/weaver/bcel/JImageTestCase.java | 135 - .../org/aspectj/weaver/bcel/MegaZipTestCase.java | 107 - .../weaver/bcel/MoveInstructionsWeaveTestCase.java | 81 - .../weaver/bcel/NonstaticWeaveTestCase.java | 83 - .../aspectj/weaver/bcel/PatternWeaveTestCase.java | 122 - .../weaver/bcel/PointcutResidueTestCase.java | 189 -- .../org/aspectj/weaver/bcel/TjpWeaveTestCase.java | 99 - .../aspectj/weaver/bcel/TraceJarWeaveTestCase.java | 40 - .../org/aspectj/weaver/bcel/UtilityTestCase.java | 52 - .../aspectj/weaver/bcel/WeaveOrderTestCase.java | 149 - .../org/aspectj/weaver/bcel/WeaveTestCase.java | 318 -- .../org/aspectj/weaver/bcel/WorldTestCase.java | 163 - .../org/aspectj/weaver/bcel/ZipFileWeaver.java | 39 - .../org/aspectj/weaver/bcel/ZipTestCase.java | 121 - .../AnnotationPatternMatchingTestCase.java | 252 -- .../weaver/patterns/AnnotationPatternTestCase.java | 382 --- .../weaver/patterns/ConcretizationTestCase.java | 116 - .../WildTypePatternResolutionTestCase.java | 423 --- .../weaver/patterns/bcel/BcelAndOrNotTestCase.java | 24 - .../weaver/patterns/bcel/BcelBindingTestCase.java | 24 - .../bcel/BcelModifiersPatternTestCase.java | 24 - .../weaver/patterns/bcel/BcelParserTestCase.java | 25 - .../weaver/patterns/bcel/BcelPatternsTests.java | 46 - .../bcel/BcelSignaturePatternTestCase.java | 24 - .../patterns/bcel/BcelTypePatternListTestCase.java | 23 - .../patterns/bcel/BcelTypePatternTestCase.java | 24 - .../weaver/patterns/bcel/BcelWithinTestCase.java | 23 - .../ReflectionBasedReferenceTypeDelegateTest.java | 316 -- .../tools/PointcutDesignatorHandlerTests.java | 251 -- .../weaver/tools/PointcutExpressionTest.java | 596 ---- .../aspectj/weaver/tools/PointcutParserTest.java | 391 --- .../aspectj/weaver/tools/ReadingAttributes.java | 64 - .../org/aspectj/weaver/tools/ToolsTests.java | 34 - .../weaver/tools/TypePatternMatcherTest.java | 55 - .../cache/AbstractCacheBackingTestSupport.java | 379 --- .../AsynchronousFileCacheBackingTestSupport.java | 193 -- .../org/aspectj/weaver/tools/cache/CacheTests.java | 31 - .../tools/cache/DefaultCacheKeyResolverTest.java | 95 - .../tools/cache/DefaultFileCacheBackingTest.java | 179 - .../tools/cache/FlatFileCacheBackingTest.java | 154 - .../weaver/tools/cache/SimpleClassCacheTest.java | 71 - .../weaver/tools/cache/WeavedClassCacheTest.java | 152 - .../tools/cache/ZippedFileCacheBackingTest.java | 154 - weaver/testsrc/reflect/tests/C.java | 33 - 360 files changed, 47238 insertions(+), 43066 deletions(-) delete mode 100644 weaver/.cvsignore delete mode 100644 weaver/.isJava5 create mode 100644 weaver/.settings/org.eclipse.core.resources.prefs create mode 100644 weaver/.settings/org.eclipse.jdt.apt.core.prefs delete mode 100644 weaver/.settings/org.eclipse.jdt.ui.prefs create mode 100644 weaver/.settings/org.eclipse.m2e.core.prefs delete mode 100644 weaver/build.xml create mode 100644 weaver/pom.xml delete mode 100644 weaver/src/aspectj_1_5_0.dtd create mode 100644 weaver/src/main/java/org/aspectj/weaver/IClassFileProvider.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/AnnotationAccessFieldVar.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/AnnotationAccessVar.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/AspectInstanceVar.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/AtAjAttributes.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/BcelAccessForInlineMunger.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/BcelAdvice.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/BcelAnnotation.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/BcelCflowAccessVar.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/BcelCflowCounterFieldAdder.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/BcelCflowStackFieldAdder.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/BcelClassWeaver.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/BcelConstantPoolReader.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/BcelConstantPoolWriter.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/BcelField.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/BcelFieldRef.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/BcelGenericSignatureToTypeXConverter.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/BcelMethod.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/BcelObjectType.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/BcelPerClauseAspectAdder.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/BcelRenderer.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/BcelShadow.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/BcelTypeMunger.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/BcelVar.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/BcelWeakClassLoaderReference.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/BcelWeaver.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/BcelWeavingSupport.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/BcelWorld.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/ClassPathManager.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/ExceptionRange.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/ExtensibleURLClassLoader.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/FakeAnnotation.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/IfFinder.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/LazyClassGen.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/LazyMethodGen.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/Range.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/ShadowRange.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/TypeAnnotationAccessVar.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/TypeDelegateResolver.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/UnwovenClassFile.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/UnwovenClassFileWithThirdPartyManagedBytecode.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/Utility.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/asm/AsmDetector.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/bcel/asm/StackMapAdder.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/loadtime/IWeavingContext.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/loadtime/definition/Definition.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/loadtime/definition/DocumentParser.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/loadtime/definition/LightXMLParser.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/loadtime/definition/SimpleAOPParser.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/ltw/LTWWorld.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/model/AsmRelationshipProvider.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/model/AsmRelationshipUtils.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/reflect/ArgNameFinder.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/reflect/DeferredResolvedPointcutDefinition.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/reflect/InternalUseOnlyPointcutParser.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/reflect/Java15AnnotationFinder.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/reflect/Java15GenericSignatureInformationProvider.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/reflect/Java15ReflectionBasedReferenceTypeDelegate.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/reflect/JavaLangTypeToResolvedTypeConverter.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/tools/Jdk14Trace.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/tools/Jdk14TraceFactory.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/tools/WeavingAdaptor.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/tools/cache/AbstractCacheBacking.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/tools/cache/AbstractFileCacheBacking.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/tools/cache/AbstractIndexedFileCacheBacking.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/tools/cache/AsynchronousFileCacheBacking.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/tools/cache/CacheBacking.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/tools/cache/CacheFactory.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/tools/cache/CacheKeyResolver.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/tools/cache/CacheStatistics.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/tools/cache/CachedClassEntry.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/tools/cache/CachedClassReference.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/tools/cache/DefaultCacheFactory.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/tools/cache/DefaultCacheKeyResolver.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/tools/cache/DefaultFileCacheBacking.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/tools/cache/FlatFileCacheBacking.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/tools/cache/GeneratedCachedClassHandler.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/tools/cache/SimpleCache.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/tools/cache/SimpleCacheFactory.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/tools/cache/WeavedClassCache.java create mode 100644 weaver/src/main/java/org/aspectj/weaver/tools/cache/ZippedFileCacheBacking.java create mode 100644 weaver/src/main/resources/aspectj_1_5_0.dtd delete mode 100644 weaver/src/org/aspectj/weaver/IClassFileProvider.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/AnnotationAccessFieldVar.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/AnnotationAccessVar.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/AspectInstanceVar.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/AtAjAttributes.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/BcelAccessForInlineMunger.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/BcelAdvice.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/BcelAnnotation.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/BcelCflowAccessVar.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/BcelCflowCounterFieldAdder.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/BcelCflowStackFieldAdder.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/BcelClassWeaver.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/BcelConstantPoolReader.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/BcelConstantPoolWriter.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/BcelField.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/BcelFieldRef.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/BcelGenericSignatureToTypeXConverter.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/BcelMethod.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/BcelObjectType.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/BcelPerClauseAspectAdder.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/BcelRenderer.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/BcelShadow.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/BcelTypeMunger.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/BcelVar.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/BcelWeakClassLoaderReference.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/BcelWeaver.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/BcelWeavingSupport.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/BcelWorld.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/ClassPathManager.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/ExceptionRange.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/ExtensibleURLClassLoader.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/FakeAnnotation.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/IfFinder.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/LazyClassGen.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/LazyMethodGen.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/Range.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/ShadowRange.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/TypeAnnotationAccessVar.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/TypeDelegateResolver.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/UnwovenClassFile.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/UnwovenClassFileWithThirdPartyManagedBytecode.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/Utility.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/asm/AsmDetector.java delete mode 100644 weaver/src/org/aspectj/weaver/bcel/asm/StackMapAdder.java delete mode 100644 weaver/src/org/aspectj/weaver/loadtime/IWeavingContext.java delete mode 100644 weaver/src/org/aspectj/weaver/loadtime/definition/Definition.java delete mode 100644 weaver/src/org/aspectj/weaver/loadtime/definition/DocumentParser.java delete mode 100644 weaver/src/org/aspectj/weaver/loadtime/definition/LightXMLParser.java delete mode 100644 weaver/src/org/aspectj/weaver/loadtime/definition/SimpleAOPParser.java delete mode 100644 weaver/src/org/aspectj/weaver/ltw/LTWWorld.java delete mode 100644 weaver/src/org/aspectj/weaver/model/AsmRelationshipProvider.java delete mode 100644 weaver/src/org/aspectj/weaver/model/AsmRelationshipUtils.java delete mode 100644 weaver/src/org/aspectj/weaver/tools/WeavingAdaptor.java delete mode 100644 weaver/src/org/aspectj/weaver/tools/cache/AbstractCacheBacking.java delete mode 100644 weaver/src/org/aspectj/weaver/tools/cache/AbstractFileCacheBacking.java delete mode 100644 weaver/src/org/aspectj/weaver/tools/cache/AbstractIndexedFileCacheBacking.java delete mode 100644 weaver/src/org/aspectj/weaver/tools/cache/AsynchronousFileCacheBacking.java delete mode 100644 weaver/src/org/aspectj/weaver/tools/cache/CacheBacking.java delete mode 100644 weaver/src/org/aspectj/weaver/tools/cache/CacheFactory.java delete mode 100644 weaver/src/org/aspectj/weaver/tools/cache/CacheKeyResolver.java delete mode 100644 weaver/src/org/aspectj/weaver/tools/cache/CacheStatistics.java delete mode 100644 weaver/src/org/aspectj/weaver/tools/cache/CachedClassEntry.java delete mode 100644 weaver/src/org/aspectj/weaver/tools/cache/CachedClassReference.java delete mode 100644 weaver/src/org/aspectj/weaver/tools/cache/DefaultCacheFactory.java delete mode 100644 weaver/src/org/aspectj/weaver/tools/cache/DefaultCacheKeyResolver.java delete mode 100644 weaver/src/org/aspectj/weaver/tools/cache/DefaultFileCacheBacking.java delete mode 100644 weaver/src/org/aspectj/weaver/tools/cache/FlatFileCacheBacking.java delete mode 100644 weaver/src/org/aspectj/weaver/tools/cache/GeneratedCachedClassHandler.java delete mode 100644 weaver/src/org/aspectj/weaver/tools/cache/SimpleCache.java delete mode 100644 weaver/src/org/aspectj/weaver/tools/cache/SimpleCacheFactory.java delete mode 100644 weaver/src/org/aspectj/weaver/tools/cache/WeavedClassCache.java delete mode 100644 weaver/src/org/aspectj/weaver/tools/cache/ZippedFileCacheBacking.java create mode 100644 weaver/src/test/java/$Proxy1.java create mode 100644 weaver/src/test/java/CounterAspect.java create mode 100644 weaver/src/test/java/GenericService.java create mode 100644 weaver/src/test/java/MA.java create mode 100644 weaver/src/test/java/MB.java create mode 100644 weaver/src/test/java/MC.java create mode 100644 weaver/src/test/java/MD.java create mode 100644 weaver/src/test/java/MessageService.java create mode 100644 weaver/src/test/java/fluffy/Aspect.java create mode 100644 weaver/src/test/java/fluffy/Base.java create mode 100644 weaver/src/test/java/fluffy/Derived.java create mode 100644 weaver/src/test/java/org/aspectj/matcher/tools/ReflectionWorldAdvancedPointcutExpressionTest.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/AbstractTraceTest.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/AllWeaver5Tests.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/BcweaverModule15Test.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/BoundedReferenceTypeTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/CommonReferenceTypeTests.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/CommonsTraceFactoryTest.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/CommonsTraceTest.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/DefaultTraceFactoryTest.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/DefaultTraceTest.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/DumpTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/GenericSignatureParserTest.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/Java5ReflectionBasedReferenceTypeDelegateTest.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/Jdk14TraceFactoryTest.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/Jdk14TraceTest.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/JoinPointSignatureIteratorTest.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/LocaleTest.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/Member15Test.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/Member15TestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/MemberTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/ParameterizedReferenceTypeTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/ReferenceTypeTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/ResolvedMemberSignatures15TestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/TestShadow.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/TraceFactoryTest.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/TypeVariableReferenceTypeTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/TypeVariableTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/TypeXTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/Weaver5ModuleTests.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/WeaverMessagesTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/WeaverTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/bcel/AfterReturningWeaveTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/bcel/AfterThrowingWeaveTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/bcel/AfterWeaveTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/bcel/ArgsWeaveTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/bcel/AroundArgsWeaveTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/bcel/AroundWeaveTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/bcel/BcelGenericSignatureToTypeXTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/bcel/BcelTestUtils.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/bcel/BcelWorldReferenceTypeTest.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/bcel/BeforeWeaveTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/bcel/CheckerTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/bcel/ClassLoaderRepositoryTest.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/bcel/FieldSetTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/bcel/HierarchyDependsTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/bcel/IdWeaveTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/bcel/JImageTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/bcel/MegaZipTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/bcel/MoveInstructionsWeaveTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/bcel/NonstaticWeaveTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/bcel/PatternWeaveTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/bcel/PointcutResidueTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/bcel/TjpWeaveTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/bcel/TraceJarWeaveTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/bcel/UtilityTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/bcel/WeaveOrderTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/bcel/WeaveTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/bcel/WorldTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/bcel/ZipFileWeaver.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/bcel/ZipTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/patterns/AnnotationPatternMatchingTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/patterns/AnnotationPatternTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/patterns/ConcretizationTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/patterns/WildTypePatternResolutionTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/patterns/bcel/BcelAndOrNotTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/patterns/bcel/BcelBindingTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/patterns/bcel/BcelModifiersPatternTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/patterns/bcel/BcelParserTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/patterns/bcel/BcelSignaturePatternTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/patterns/bcel/BcelTypePatternListTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/patterns/bcel/BcelTypePatternTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/patterns/bcel/BcelWithinTestCase.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/reflect/ReflectionBasedReferenceTypeDelegateTest.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/reflect/ReflectionWorldReferenceTypeTest.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/reflect/ReflectionWorldTest.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/tools/Java15PointcutExpressionTest.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/tools/PointcutDesignatorHandlerTest.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/tools/PointcutExpressionTest.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/tools/PointcutParserTest.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/tools/ReadingAttributesTest.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/tools/TypePatternMatcherTest.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/tools/cache/AbstractCacheBackingTestSupport.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/tools/cache/AsynchronousFileCacheBackingTestSupport.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/tools/cache/DefaultCacheKeyResolverTest.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/tools/cache/DefaultFileCacheBackingTest.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/tools/cache/FlatFileCacheBackingTest.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/tools/cache/SimpleClassCacheTest.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/tools/cache/WeavedClassCacheTest.java create mode 100644 weaver/src/test/java/org/aspectj/weaver/tools/cache/ZippedFileCacheBackingTest.java create mode 100644 weaver/src/test/java/reflect/tests/C.java create mode 100644 weaver/src/test/java/test/A.java create mode 100644 weaver/src/test/java/test/A1.java create mode 100644 weaver/src/test/java/test/A1AnnotatedType.java create mode 100644 weaver/src/test/java/test/A2.java create mode 100644 weaver/src/test/java/test/A2AnnotatedType.java create mode 100644 weaver/src/test/java/test/A3.java create mode 100644 weaver/src/test/java/test/AnnoValues.java create mode 100644 weaver/src/test/java/test/Color.java create mode 100644 weaver/testdata/logging.properties delete mode 100644 weaver/testsrc/fluffy/Aspect.java delete mode 100644 weaver/testsrc/fluffy/Base.java delete mode 100644 weaver/testsrc/fluffy/Derived.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/AbstractTraceTest.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/AllTracingTests.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/BcweaverModuleTests.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/BcweaverTests.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/BoundedReferenceTypeTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/CommonsTraceFactoryTest.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/CommonsTraceTest.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/DefaultTraceFactoryTest.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/DefaultTraceTest.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/DumpTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/GenericSignatureParserTest.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/LocaleTest.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/MemberTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/MemberTestCase15.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/ParameterizedReferenceTypeTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/ResolvedMemberSignaturesTestCase15.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/TestShadow.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/TraceFactoryTest.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/TypeVariableReferenceTypeTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/TypeVariableTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/TypeXTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/WeaverMessagesTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/bcel/AfterReturningWeaveTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/bcel/AfterThrowingWeaveTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/bcel/AfterWeaveTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/bcel/ArgsWeaveTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/bcel/AroundArgsWeaveTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/bcel/AroundWeaveTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/bcel/BcelGenericSignatureToTypeXTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/bcel/BcelTestUtils.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/bcel/BcelTests.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/bcel/BeforeWeaveTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/bcel/CheckerTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/bcel/ClassLoaderRepositoryTests.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/bcel/FieldSetTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/bcel/HierarchyDependsTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/bcel/IdWeaveTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/bcel/JImageTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/bcel/MegaZipTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/bcel/MoveInstructionsWeaveTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/bcel/NonstaticWeaveTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/bcel/PatternWeaveTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/bcel/PointcutResidueTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/bcel/TjpWeaveTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/bcel/TraceJarWeaveTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/bcel/UtilityTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/bcel/WeaveOrderTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/bcel/WeaveTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/bcel/WorldTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/bcel/ZipFileWeaver.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/bcel/ZipTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/patterns/AnnotationPatternMatchingTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/patterns/AnnotationPatternTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/patterns/ConcretizationTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/patterns/WildTypePatternResolutionTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelAndOrNotTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelBindingTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelModifiersPatternTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelParserTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelPatternsTests.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelSignaturePatternTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelTypePatternListTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelTypePatternTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelWithinTestCase.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/reflect/ReflectionBasedReferenceTypeDelegateTest.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/tools/PointcutDesignatorHandlerTests.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/tools/PointcutExpressionTest.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/tools/PointcutParserTest.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/tools/ReadingAttributes.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/tools/ToolsTests.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/tools/TypePatternMatcherTest.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/tools/cache/AbstractCacheBackingTestSupport.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/tools/cache/AsynchronousFileCacheBackingTestSupport.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/tools/cache/CacheTests.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/tools/cache/DefaultCacheKeyResolverTest.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/tools/cache/DefaultFileCacheBackingTest.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/tools/cache/FlatFileCacheBackingTest.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/tools/cache/SimpleClassCacheTest.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/tools/cache/WeavedClassCacheTest.java delete mode 100644 weaver/testsrc/org/aspectj/weaver/tools/cache/ZippedFileCacheBackingTest.java delete mode 100644 weaver/testsrc/reflect/tests/C.java (limited to 'weaver') diff --git a/weaver/.classpath b/weaver/.classpath index f57afd876..39abf1c5e 100644 --- a/weaver/.classpath +++ b/weaver/.classpath @@ -1,18 +1,49 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/weaver/.cvsignore b/weaver/.cvsignore deleted file mode 100644 index e1b17a04f..000000000 --- a/weaver/.cvsignore +++ /dev/null @@ -1,6 +0,0 @@ -out -bin -default.lst -default.ajsym -.clover -bintest diff --git a/weaver/.isJava5 b/weaver/.isJava5 deleted file mode 100644 index 136d06384..000000000 --- a/weaver/.isJava5 +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/weaver/.project b/weaver/.project index 940c812a5..06733878f 100644 --- a/weaver/.project +++ b/weaver/.project @@ -3,11 +3,6 @@ weaver - asm - bridge - runtime - testing-util - util @@ -15,8 +10,14 @@ + + org.eclipse.m2e.core.maven2Builder + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature diff --git a/weaver/.settings/org.eclipse.core.resources.prefs b/weaver/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 000000000..839d647ee --- /dev/null +++ b/weaver/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,5 @@ +eclipse.preferences.version=1 +encoding//src/main/java=UTF-8 +encoding//src/main/resources=UTF-8 +encoding//src/test/java=UTF-8 +encoding/=UTF-8 diff --git a/weaver/.settings/org.eclipse.jdt.apt.core.prefs b/weaver/.settings/org.eclipse.jdt.apt.core.prefs new file mode 100644 index 000000000..d4313d4b2 --- /dev/null +++ b/weaver/.settings/org.eclipse.jdt.apt.core.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.apt.aptEnabled=false diff --git a/weaver/.settings/org.eclipse.jdt.core.prefs b/weaver/.settings/org.eclipse.jdt.core.prefs index 7341ab168..5592a0a1c 100644 --- a/weaver/.settings/org.eclipse.jdt.core.prefs +++ b/weaver/.settings/org.eclipse.jdt.core.prefs @@ -1,11 +1,7 @@ eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.7 -org.eclipse.jdt.core.compiler.debug.lineNumber=generate -org.eclipse.jdt.core.compiler.debug.localVariable=generate -org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.source=1.7 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.processAnnotations=disabled +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/weaver/.settings/org.eclipse.jdt.ui.prefs b/weaver/.settings/org.eclipse.jdt.ui.prefs deleted file mode 100644 index e591fe798..000000000 --- a/weaver/.settings/org.eclipse.jdt.ui.prefs +++ /dev/null @@ -1,3 +0,0 @@ -#Thu Sep 22 08:32:30 PDT 2005 -eclipse.preferences.version=1 -internal.default.compliance=default diff --git a/weaver/.settings/org.eclipse.m2e.core.prefs b/weaver/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 000000000..f897a7f1c --- /dev/null +++ b/weaver/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/weaver/build.xml b/weaver/build.xml deleted file mode 100644 index 90934da23..000000000 --- a/weaver/build.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/weaver/pom.xml b/weaver/pom.xml new file mode 100644 index 000000000..930fcfa5e --- /dev/null +++ b/weaver/pom.xml @@ -0,0 +1,85 @@ + + 4.0.0 + + + org.aspectj + aspectj-parent + 1.9.3.BUILD-SNAPSHOT + .. + + + weaver + jar + weaver + + + + org.aspectj + util + ${project.version} + + + org.aspectj + runtime + ${project.version} + + + org.aspectj + util + ${project.version} + + + org.aspectj + testing-util + ${project.version} + + + org.aspectj + bridge + ${project.version} + + + org.aspectj + asm + ${project.version} + + + org.aspectj + org.aspectj.matcher + ${project.version} + + + org.aspectj + runtime + ${project.version} + + + org.aspectj + org.aspectj.matcher + ${project.version} + test-jar + test + + + org.aspectj + bcel-builder + ${project.version} + + + commons + commons + 1.0 + system + ${project.basedir}/../lib/commons/commons.jar + + + asm + asm + 1.0 + system + ${project.basedir}/../lib/asm/asm-7.0-beta.renamed.jar + + + diff --git a/weaver/src/aspectj_1_5_0.dtd b/weaver/src/aspectj_1_5_0.dtd deleted file mode 100644 index 314290333..000000000 --- a/weaver/src/aspectj_1_5_0.dtd +++ /dev/null @@ -1,149 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/weaver/src/main/java/org/aspectj/weaver/IClassFileProvider.java b/weaver/src/main/java/org/aspectj/weaver/IClassFileProvider.java new file mode 100644 index 000000000..029bba7c6 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/IClassFileProvider.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.aspectj.weaver; + +import java.util.Iterator; + +import org.aspectj.weaver.bcel.UnwovenClassFile; + +/** + * @author colyer + * + * Clients implementing the IClassFileProvider can have a set of class files under their control woven by a weaver, by + * calling the weave(IClassFileProvider source) method. The contract is that a call to getRequestor().acceptResult() is + * providing a result for the class file most recently returned from the getClassFileIterator(). + */ +public interface IClassFileProvider { + + /** + * Answer an iterator that can be used to iterate over a set of UnwovenClassFiles to be woven. During a weave, this method may + * be called multiple times. + * + * @return iterator over UnwovenClassFiles. + */ + Iterator getClassFileIterator(); + + /** + * The client to which the woven results should be returned. + */ + IWeaveRequestor getRequestor(); + + /** + * @return true if weaver should only do some internal munging as the one needed for @AspectJ aspectOf methods creation + */ + boolean isApplyAtAspectJMungersOnly(); + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/AnnotationAccessFieldVar.java b/weaver/src/main/java/org/aspectj/weaver/bcel/AnnotationAccessFieldVar.java new file mode 100644 index 000000000..60aa84125 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/AnnotationAccessFieldVar.java @@ -0,0 +1,186 @@ +/* ******************************************************************* + * Copyright (c) 2008 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement initial implementation + * ******************************************************************/ +package org.aspectj.weaver.bcel; + +import java.util.List; + +import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; +import org.aspectj.apache.bcel.classfile.annotation.ElementValue; +import org.aspectj.apache.bcel.classfile.annotation.EnumElementValue; +import org.aspectj.apache.bcel.classfile.annotation.NameValuePair; +import org.aspectj.apache.bcel.classfile.annotation.SimpleElementValue; +import org.aspectj.apache.bcel.generic.InstructionFactory; +import org.aspectj.apache.bcel.generic.InstructionList; +import org.aspectj.apache.bcel.generic.Type; +import org.aspectj.weaver.AnnotationAJ; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.UnresolvedType; + +/** + * An AnnotationAccessVar represents access to a particular annotation, whilst an AnnotationAccessFieldVar represents access to a + * specific field of that annotation. + * + * @author Andy Clement + */ +class AnnotationAccessFieldVar extends BcelVar { + + private AnnotationAccessVar annoAccessor; + private ResolvedType annoFieldOfInterest; + private String name; + private int elementValueType; + + public AnnotationAccessFieldVar(AnnotationAccessVar aav, ResolvedType annoFieldOfInterest, String name) { + super(annoFieldOfInterest, 0); + this.annoAccessor = aav; + this.name = name; + String sig = annoFieldOfInterest.getSignature(); + if (sig.length() == 1) { + switch (sig.charAt(0)) { + case 'I': + elementValueType = ElementValue.PRIMITIVE_INT; + break; + default: + throw new IllegalStateException(sig); + } + } else if (sig.equals("Ljava/lang/String;")) { + elementValueType = ElementValue.STRING; + } else if (annoFieldOfInterest.isEnum()) { + elementValueType = ElementValue.ENUM_CONSTANT; + } else { + throw new IllegalStateException(sig); + } + this.annoFieldOfInterest = annoFieldOfInterest; + } + + @Override + public void appendLoadAndConvert(InstructionList il, InstructionFactory fact, ResolvedType toType) { + // Only possible to do annotation field value extraction at MethodExecution + if (annoAccessor.getKind() != Shadow.MethodExecution) { + return; + } + String annotationOfInterestSignature = annoAccessor.getType().getSignature(); + // So we have an entity that has an annotation on and within it is the value we want + Member holder = annoAccessor.getMember(); + AnnotationAJ[] annos = holder.getAnnotations(); + for (AnnotationAJ anno : annos) { + AnnotationGen annotation = ((BcelAnnotation) anno).getBcelAnnotation(); + boolean foundValueInAnnotationUsage = false; + if (annotation.getTypeSignature().equals(annotationOfInterestSignature)) { + ResolvedMember[] annotationFields = toType.getWorld() + .resolve(UnresolvedType.forSignature(annotation.getTypeSignature())).getDeclaredMethods(); + // Check how many fields there are of the type we are looking for. If >1 then we'll need + // to use the name to choose the right one + int countOfType = 0; + for (ResolvedMember annotationField : annotationFields) { + if (annotationField.getType().equals(annoFieldOfInterest)) { + countOfType++; + } + } + + // this block deals with an annotation that has actual values (i.e. not falling back to default values) + List nvps = annotation.getValues(); + for (NameValuePair nvp : nvps) { + // If multiple of the same type, match by name + if (countOfType > 1) { + if (!nvp.getNameString().equals(name)) { + continue; + } + } + ElementValue o = nvp.getValue(); + if (o.getElementValueType() != elementValueType) { + continue; + } + if (o instanceof EnumElementValue) { + EnumElementValue v = (EnumElementValue) o; + String s = v.getEnumTypeString(); + ResolvedType rt = toType.getWorld().resolve(UnresolvedType.forSignature(s)); + if (rt.equals(toType)) { + il.append(fact.createGetStatic(rt.getName(), v.getEnumValueString(), Type.getType(rt.getSignature()))); + foundValueInAnnotationUsage = true; + } + } else if (o instanceof SimpleElementValue) { + SimpleElementValue v = (SimpleElementValue) o; + switch (v.getElementValueType()) { + case ElementValue.PRIMITIVE_INT: + il.append(fact.createConstant(v.getValueInt())); + foundValueInAnnotationUsage = true; + break; + case ElementValue.STRING: + il.append(fact.createConstant(v.getValueString())); + foundValueInAnnotationUsage = true; + break; + default: + throw new IllegalStateException("NYI: Unsupported annotation value binding for " + o); + } + } + if (foundValueInAnnotationUsage) { + break; + } + } + // this block deals with default values + if (!foundValueInAnnotationUsage) { + for (ResolvedMember annotationField : annotationFields) { + if (countOfType > 1) { + if (!annotationField.getName().equals(name)) { + continue; + } + } + if (!annotationField.getType().getSignature().equals(annoFieldOfInterest.getSignature())) { + continue; + } + if (annotationField.getType().getSignature().equals("I")) { + int ivalue = Integer.parseInt(annotationField.getAnnotationDefaultValue()); + il.append(fact.createConstant(ivalue)); + foundValueInAnnotationUsage = true; + break; + } else if (annotationField.getType().getSignature().equals("Ljava/lang/String;")) { + String svalue = annotationField.getAnnotationDefaultValue(); + il.append(fact.createConstant(svalue)); + foundValueInAnnotationUsage = true; + break; + } else { + String dvalue = annotationField.getAnnotationDefaultValue(); + // form will be LBLAHBLAHBLAH;X where X is the field within X + String typename = dvalue.substring(0, dvalue.lastIndexOf(';') + 1); + String field = dvalue.substring(dvalue.lastIndexOf(';') + 1); + ResolvedType rt = toType.getWorld().resolve(UnresolvedType.forSignature(typename)); + il.append(fact.createGetStatic(rt.getName(), field, Type.getType(rt.getSignature()))); + foundValueInAnnotationUsage = true; + break; + } + } + } + } + if (foundValueInAnnotationUsage) { + break; + } + } + } + + @Override + public void insertLoad(InstructionList il, InstructionFactory fact) { + // Only possible to do annotation field value extraction at + // MethodExecution + if (annoAccessor.getKind() != Shadow.MethodExecution) { + return; + } + appendLoadAndConvert(il, fact, annoFieldOfInterest); + } + + @Override + public String toString() { + return super.toString(); + } +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/AnnotationAccessVar.java b/weaver/src/main/java/org/aspectj/weaver/bcel/AnnotationAccessVar.java new file mode 100644 index 000000000..88a67d666 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/AnnotationAccessVar.java @@ -0,0 +1,272 @@ +/* ******************************************************************* + * Copyright (c) 2005-2008 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.bcel; + +import org.aspectj.apache.bcel.Constants; +import org.aspectj.apache.bcel.classfile.Field; +import org.aspectj.apache.bcel.generic.Instruction; +import org.aspectj.apache.bcel.generic.InstructionBranch; +import org.aspectj.apache.bcel.generic.InstructionConstants; +import org.aspectj.apache.bcel.generic.InstructionFactory; +import org.aspectj.apache.bcel.generic.InstructionHandle; +import org.aspectj.apache.bcel.generic.InstructionList; +import org.aspectj.apache.bcel.generic.ObjectType; +import org.aspectj.apache.bcel.generic.Type; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.Shadow.Kind; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.ast.Var; + +/** + * Represents access to an annotation on an element, relating to some kinded pointcut. Depending on the kind of pointcut the element + * might be a field or a method and the code generators in here can retrieve the annotation from the element. + */ +public class AnnotationAccessVar extends BcelVar { + + private BcelShadow shadow; + private Kind kind; // What kind of shadow are we at? + private UnresolvedType containingType; // The type upon which we want to ask for 'member' + private Member member; // Holds the member that has the annotations (for method/field join points) + private boolean isWithin; // implies @within() or @withincode(). If false, that implies @annotation() + + public AnnotationAccessVar(BcelShadow shadow, Kind kind, ResolvedType annotationType, UnresolvedType theTargetIsStoredHere, + Member sig, boolean isWithin) { + super(annotationType, 0); + this.shadow = shadow; + this.kind = kind; + this.containingType = theTargetIsStoredHere; + this.member = sig; + this.isWithin = isWithin; + } + + public Kind getKind() { + return kind; + } + + @Override + public String toString() { + return "AnnotationAccessVar(" + getType() + ")"; + } + + @Override + public Instruction createLoad(InstructionFactory fact) { + throw new IllegalStateException("unimplemented"); + } + + @Override + public Instruction createStore(InstructionFactory fact) { + throw new IllegalStateException("unimplemented"); + } + + @Override + public InstructionList createCopyFrom(InstructionFactory fact, int oldSlot) { + throw new IllegalStateException("unimplemented"); + } + + @Override + public void appendLoad(InstructionList il, InstructionFactory fact) { + il.append(createLoadInstructions(getType(), fact)); + } + + @Override + public void appendLoadAndConvert(InstructionList il, InstructionFactory fact, ResolvedType toType) { + il.append(createLoadInstructions(toType, fact)); + } + + @Override + public void insertLoad(InstructionList il, InstructionFactory fact) { + il.insert(createLoadInstructions(getType(), fact)); + } + + private InstructionList createLoadInstructions(ResolvedType toType, InstructionFactory fact) { + + InstructionList il = new InstructionList(); + + Type jlClass = BcelWorld.makeBcelType(UnresolvedType.JL_CLASS); + Type jlString = BcelWorld.makeBcelType(UnresolvedType.JL_STRING); + Type jlClassArray = BcelWorld.makeBcelType(UnresolvedType.JAVA_LANG_CLASS_ARRAY); + Type jlaAnnotation = BcelWorld.makeBcelType(UnresolvedType.JAVA_LANG_ANNOTATION); + + Instruction pushConstant = fact.createConstant(new ObjectType(toType.getName())); + + if (kind == Shadow.MethodCall || kind == Shadow.MethodExecution || kind == Shadow.PreInitialization + || kind == Shadow.Initialization || kind == Shadow.ConstructorCall || kind == Shadow.ConstructorExecution + || kind == Shadow.AdviceExecution || + // annotations for fieldset/fieldget when an ITD is involved are stored against a METHOD + ((kind == Shadow.FieldGet || kind == Shadow.FieldSet) && member.getKind() == Member.METHOD)) { + + Type jlrMethod = BcelWorld.makeBcelType(UnresolvedType.forSignature("Ljava/lang/reflect/Method;")); + Type jlAnnotation = BcelWorld.makeBcelType(UnresolvedType.forSignature("Ljava/lang/annotation/Annotation;")); + Type[] paramTypes = BcelWorld.makeBcelTypes(member.getParameterTypes()); + + // il.append(fact.createConstant(BcelWorld.makeBcelType(containingType))); + + if (kind == Shadow.MethodCall + || kind == Shadow.MethodExecution + || kind == Shadow.AdviceExecution + || + // annotations for fieldset/fieldget when an ITD is involved are stored against a METHOD + ((kind == Shadow.FieldGet || kind == Shadow.FieldSet) && member.getKind() == Member.METHOD) + || ((kind == Shadow.ConstructorCall || kind == Shadow.ConstructorExecution) && member.getKind() == Member.METHOD)) { + + // Need to look at the cached annotation before going to fetch it again + Field annotationCachingField = shadow.getEnclosingClass().getAnnotationCachingField(shadow, toType, isWithin); + + // Basic idea here is to check if the cached field is null, if it is then initialize it, otherwise use it + il.append(fact.createGetStatic(shadow.getEnclosingClass().getName(), annotationCachingField.getName(), jlAnnotation)); + il.append(InstructionConstants.DUP); + InstructionBranch ifNonNull = InstructionFactory.createBranchInstruction(Constants.IFNONNULL, null); + il.append(ifNonNull); + il.append(InstructionConstants.POP); + il.append(fact.createConstant(BcelWorld.makeBcelType(containingType))); + + il.append(fact.createConstant(member.getName())); + buildArray(il, fact, jlClass, paramTypes, 1); + // OPTIMIZE cache result of getDeclaredMethod? + il.append(fact.createInvoke("java/lang/Class", "getDeclaredMethod", jlrMethod, + new Type[] { jlString, jlClassArray }, Constants.INVOKEVIRTUAL)); + il.append(pushConstant);// fact.createConstant(new ObjectType(toType.getName()))); + il.append(fact.createInvoke("java/lang/reflect/Method", "getAnnotation", jlaAnnotation, new Type[] { jlClass }, + Constants.INVOKEVIRTUAL)); + il.append(InstructionConstants.DUP); + il.append(fact.createPutStatic(shadow.getEnclosingClass().getName(), annotationCachingField.getName(), jlAnnotation)); + InstructionHandle ifNullElse = il.append(InstructionConstants.NOP); + ifNonNull.setTarget(ifNullElse); + + } else { // init/preinit/ctor-call/ctor-exec + il.append(fact.createConstant(BcelWorld.makeBcelType(containingType))); + buildArray(il, fact, jlClass, paramTypes, 1); + Type jlrCtor = BcelWorld.makeBcelType(UnresolvedType.JAVA_LANG_REFLECT_CONSTRUCTOR); + // OPTIMIZE cache result of getDeclaredConstructor and getAnnotation? Might be able to use it again if someone else + // needs the same annotations? + il.append(fact.createInvoke("java/lang/Class", "getDeclaredConstructor", jlrCtor, new Type[] { jlClassArray }, + Constants.INVOKEVIRTUAL)); + il.append(pushConstant); + il.append(fact.createInvoke("java/lang/reflect/Constructor", "getAnnotation", jlaAnnotation, + new Type[] { jlClass }, Constants.INVOKEVIRTUAL)); + } + } else if (kind == Shadow.FieldSet || kind == Shadow.FieldGet) { + generateBytecodeToAccessAnnotationAtFieldGetSetShadow(toType, fact, il, pushConstant); + } else if (kind == Shadow.StaticInitialization || kind == Shadow.ExceptionHandler) { + il.append(fact.createConstant(BcelWorld.makeBcelType(containingType))); + il.append(pushConstant); + il.append(fact.createInvoke("java/lang/Class", "getAnnotation", jlaAnnotation, new Type[] { jlClass }, + Constants.INVOKEVIRTUAL)); + } else { + throw new RuntimeException("Don't understand this kind " + kind); + } + il.append(Utility.createConversion(fact, jlaAnnotation, BcelWorld.makeBcelType(toType))); + return il; + } + + /** + * At a FieldGet or FieldSet shadow, generate the bytecode to access the annotation for that field. The annotation is cached so + * the code checks that cached value before proceeding. + */ + private void generateBytecodeToAccessAnnotationAtFieldGetSetShadow(ResolvedType toType, InstructionFactory fact, + InstructionList il, Instruction pushConstantAnnotationType) { + Type jlClass = BcelWorld.makeBcelType(UnresolvedType.JL_CLASS); + Type jlString = BcelWorld.makeBcelType(UnresolvedType.JL_STRING); + Type jlaAnnotation = BcelWorld.makeBcelType(UnresolvedType.JAVA_LANG_ANNOTATION); + Type jlrField = BcelWorld.makeBcelType(UnresolvedType.JAVA_LANG_REFLECT_FIELD); + + LazyClassGen shadowEnclosingClass = shadow.getEnclosingClass(); + + // The annotation for the field of interest is cached, check cached value before fetching it + Field annotationCachingField = shadowEnclosingClass.getAnnotationCachingField(shadow, toType, isWithin); + String annotationCachingFieldName = annotationCachingField.getName(); + + // Basic idea here is to check if the cached field is null, if it is then initialize it, otherwise use it + il.append(fact.createGetStatic(shadowEnclosingClass.getName(), annotationCachingFieldName, jlaAnnotation)); + il.appendDUP(); + InstructionBranch ifNonNull = new InstructionBranch(Constants.IFNONNULL, null); + il.append(ifNonNull); + il.appendPOP(); + + // get the field of interest + il.append(fact.createConstant(BcelWorld.makeBcelType(containingType))); + il.append(fact.createConstant(member.getName())); + il.append(fact.createInvoke("java/lang/Class", "getDeclaredField", jlrField, new Type[] { jlString }, + Constants.INVOKEVIRTUAL)); + il.append(pushConstantAnnotationType); + il.append(fact.createInvoke("java/lang/reflect/Field", "getAnnotation", jlaAnnotation, new Type[] { jlClass }, + Constants.INVOKEVIRTUAL)); + il.appendDUP(); + il.append(fact.createPutStatic(shadowEnclosingClass.getName(), annotationCachingFieldName, jlaAnnotation)); + InstructionHandle ifNullElse = il.appendNOP(); + ifNonNull.setTarget(ifNullElse); + } + + private void buildArray(InstructionList il, InstructionFactory fact, Type arrayElementType, Type[] arrayEntries, int dim) { + il.append(fact.createConstant(Integer.valueOf(arrayEntries == null ? 0 : arrayEntries.length))); + il.append(fact.createNewArray(arrayElementType, (short) dim)); + if (arrayEntries == null) { + return; + } + for (int i = 0; i < arrayEntries.length; i++) { + il.append(InstructionFactory.createDup(1)); + il.append(fact.createConstant(Integer.valueOf(i))); + switch (arrayEntries[i].getType()) { + case Constants.T_ARRAY: + il.append(fact.createConstant(new ObjectType(arrayEntries[i].getSignature()))); // FIXME should be getName() and not + // getSignature()? + break; + case Constants.T_BOOLEAN: + il.append(fact.createGetStatic("java/lang/Boolean", "TYPE", arrayElementType)); + break; + case Constants.T_BYTE: + il.append(fact.createGetStatic("java/lang/Byte", "TYPE", arrayElementType)); + break; + case Constants.T_CHAR: + il.append(fact.createGetStatic("java/lang/Character", "TYPE", arrayElementType)); + break; + case Constants.T_INT: + il.append(fact.createGetStatic("java/lang/Integer", "TYPE", arrayElementType)); + break; + case Constants.T_LONG: + il.append(fact.createGetStatic("java/lang/Long", "TYPE", arrayElementType)); + break; + case Constants.T_DOUBLE: + il.append(fact.createGetStatic("java/lang/Double", "TYPE", arrayElementType)); + break; + case Constants.T_FLOAT: + il.append(fact.createGetStatic("java/lang/Float", "TYPE", arrayElementType)); + break; + case Constants.T_SHORT: + il.append(fact.createGetStatic("java/lang/Short", "TYPE", arrayElementType)); + break; + default: + il.append(fact.createConstant(arrayEntries[i])); + } + il.append(InstructionConstants.AASTORE); + } + } + + public Member getMember() { + return member; + } + + /** + * Return an object that can access a particular value of this annotation. + * + * @param valueType The type from the annotation that is of interest + * @param the formal name expressed in the pointcut, can be used to disambiguate + * @return a variable that represents access to that annotation value + */ + @Override + public Var getAccessorForValue(ResolvedType valueType, String formalName) { + return new AnnotationAccessFieldVar(this, valueType, formalName); + } +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/AspectInstanceVar.java b/weaver/src/main/java/org/aspectj/weaver/bcel/AspectInstanceVar.java new file mode 100644 index 000000000..37633b71f --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/AspectInstanceVar.java @@ -0,0 +1,119 @@ +/* ******************************************************************* + * Copyright (c) 2011 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement - SpringSource/vmware + * ******************************************************************/ +package org.aspectj.weaver.bcel; + +import org.aspectj.apache.bcel.Constants; +import org.aspectj.apache.bcel.generic.Instruction; +import org.aspectj.apache.bcel.generic.InstructionFactory; +import org.aspectj.apache.bcel.generic.InstructionList; +import org.aspectj.weaver.ResolvedType; + +/** + * Used to represent a variable reference to an aspect instance. This is used to support the if pointcut usage of + * 'thisAspectInstance'. This variable does not have a slot, instead on requesting a reference we call aspectOf() on the aspect in + * question to retrieve it. For now it only works with singleton aspects. + */ +public class AspectInstanceVar extends BcelVar { + + public AspectInstanceVar(ResolvedType type) { + super(type, -1); + } + + // fact is used in the subtypes + public Instruction createLoad(InstructionFactory fact) { + + throw new IllegalStateException(); + // return InstructionFactory.createLoad(BcelWorld.makeBcelType(getType()), slot); + } + + public Instruction createStore(InstructionFactory fact) { + throw new IllegalStateException(); + // return InstructionFactory.createStore(BcelWorld.makeBcelType(getType()), slot); + } + + public void appendStore(InstructionList il, InstructionFactory fact) { + throw new IllegalStateException(); + // il.append(createStore(fact)); + } + + public void appendLoad(InstructionList il, InstructionFactory fact) { + throw new IllegalStateException(); + // il.append(createLoad(fact)); + } + + public void appendLoadAndConvert(InstructionList il, InstructionFactory fact, ResolvedType toType) { + throw new IllegalStateException(); + // il.append(createLoad(fact)); + // Utility.appendConversion(il, fact, getType(), toType); + } + + public void insertLoad(InstructionList il, InstructionFactory fact) { + InstructionList loadInstructions = new InstructionList(); + loadInstructions.append(fact.createInvoke(getType().getName(), "aspectOf", "()" + getType().getSignature(), + Constants.INVOKESTATIC)); + il.insert(loadInstructions); + // throw new IllegalStateException(); + // il.insert(createLoad(fact)); + } + + public InstructionList createCopyFrom(InstructionFactory fact, int oldSlot) { + throw new IllegalStateException(); + // InstructionList il = new InstructionList(); + // il.append(InstructionFactory.createLoad(BcelWorld.makeBcelType(getType()), oldSlot)); + // il.append(createStore(fact)); + // return il; + } + + // this is an array var + void appendConvertableArrayLoad(InstructionList il, InstructionFactory fact, int index, ResolvedType convertTo) { + throw new IllegalStateException(); + // ResolvedType convertFromType = getType().getResolvedComponentType(); + // appendLoad(il, fact); + // il.append(Utility.createConstant(fact, index)); + // il.append(InstructionFactory.createArrayLoad(BcelWorld.makeBcelType(convertFromType))); + // Utility.appendConversion(il, fact, convertFromType, convertTo); + } + + void appendConvertableArrayStore(InstructionList il, InstructionFactory fact, int index, BcelVar storee) { + throw new IllegalStateException(); + // ResolvedType convertToType = getType().getResolvedComponentType(); + // appendLoad(il, fact); + // il.append(Utility.createConstant(fact, index)); + // storee.appendLoad(il, fact); + // Utility.appendConversion(il, fact, storee.getType(), convertToType); + // il.append(InstructionFactory.createArrayStore(BcelWorld.makeBcelType(convertToType))); + } + + InstructionList createConvertableArrayStore(InstructionFactory fact, int index, BcelVar storee) { + throw new IllegalStateException(); + // InstructionList il = new InstructionList(); + // appendConvertableArrayStore(il, fact, index, storee); + // return il; + } + + InstructionList createConvertableArrayLoad(InstructionFactory fact, int index, ResolvedType convertTo) { + throw new IllegalStateException(); + // InstructionList il = new InstructionList(); + // appendConvertableArrayLoad(il, fact, index, convertTo); + // return il; + } + + public int getPositionInAroundState() { + throw new IllegalStateException(); + // return positionInAroundState; + } + + public void setPositionInAroundState(int positionInAroundState) { + throw new IllegalStateException(); + // this.positionInAroundState = positionInAroundState; + } +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/AtAjAttributes.java b/weaver/src/main/java/org/aspectj/weaver/bcel/AtAjAttributes.java new file mode 100644 index 000000000..2da044c11 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/AtAjAttributes.java @@ -0,0 +1,2046 @@ +/******************************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * initial implementation Alexandre Vasseur + *******************************************************************************/ +package org.aspectj.weaver.bcel; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.StringTokenizer; + +import org.aspectj.apache.bcel.Constants; +import org.aspectj.apache.bcel.classfile.Attribute; +import org.aspectj.apache.bcel.classfile.Constant; +import org.aspectj.apache.bcel.classfile.ConstantUtf8; +import org.aspectj.apache.bcel.classfile.Field; +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.classfile.LocalVariable; +import org.aspectj.apache.bcel.classfile.LocalVariableTable; +import org.aspectj.apache.bcel.classfile.Method; +import org.aspectj.apache.bcel.classfile.Unknown; +import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; +import org.aspectj.apache.bcel.classfile.annotation.ArrayElementValue; +import org.aspectj.apache.bcel.classfile.annotation.ClassElementValue; +import org.aspectj.apache.bcel.classfile.annotation.ElementValue; +import org.aspectj.apache.bcel.classfile.annotation.NameValuePair; +import org.aspectj.apache.bcel.classfile.annotation.RuntimeAnnos; +import org.aspectj.apache.bcel.classfile.annotation.RuntimeVisAnnos; +import org.aspectj.apache.bcel.generic.Type; +import org.aspectj.asm.AsmManager; +import org.aspectj.asm.IHierarchy; +import org.aspectj.asm.IProgramElement; +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.IMessageHandler; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.Message; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.weaver.Advice; +import org.aspectj.weaver.AdviceKind; +import org.aspectj.weaver.AjAttribute; +import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; +import org.aspectj.weaver.AjcMemberMaker; +import org.aspectj.weaver.BindingScope; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.MethodDelegateTypeMunger; +import org.aspectj.weaver.NameMangler; +import org.aspectj.weaver.ReferenceType; +import org.aspectj.weaver.ReferenceTypeDelegate; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedPointcutDefinition; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; +import org.aspectj.weaver.patterns.DeclareErrorOrWarning; +import org.aspectj.weaver.patterns.DeclareParents; +import org.aspectj.weaver.patterns.DeclareParentsMixin; +import org.aspectj.weaver.patterns.DeclarePrecedence; +import org.aspectj.weaver.patterns.FormalBinding; +import org.aspectj.weaver.patterns.IScope; +import org.aspectj.weaver.patterns.ParserException; +import org.aspectj.weaver.patterns.PatternParser; +import org.aspectj.weaver.patterns.PerCflow; +import org.aspectj.weaver.patterns.PerClause; +import org.aspectj.weaver.patterns.PerFromSuper; +import org.aspectj.weaver.patterns.PerObject; +import org.aspectj.weaver.patterns.PerSingleton; +import org.aspectj.weaver.patterns.PerTypeWithin; +import org.aspectj.weaver.patterns.Pointcut; +import org.aspectj.weaver.patterns.TypePattern; + +/** + * Annotation defined aspect reader. Reads the Java 5 annotations and turns them into AjAttributes + * + * @author Alexandre Vasseur + */ +public class AtAjAttributes { + + private final static List NO_ATTRIBUTES = Collections.emptyList(); + private final static String[] EMPTY_STRINGS = new String[0]; + private final static String VALUE = "value"; + private final static String ARGNAMES = "argNames"; + private final static String POINTCUT = "pointcut"; + private final static String THROWING = "throwing"; + private final static String RETURNING = "returning"; + private final static String STRING_DESC = "Ljava/lang/String;"; + private final static String ASPECTJ_ANNOTATION_PACKAGE = "org.aspectj.lang.annotation"; + private final static char PACKAGE_INITIAL_CHAR = ASPECTJ_ANNOTATION_PACKAGE.charAt(0); + + /** + * A struct that allows to add extra arguments without always breaking the API + */ + private static class AjAttributeStruct { + + /** + * The list of AjAttribute.XXX that we are populating from the @AJ read + */ + List ajAttributes = new ArrayList(); + + /** + * The resolved type (class) for which we are reading @AJ for (be it class, method, field annotations) + */ + final ResolvedType enclosingType; + + final ISourceContext context; + final IMessageHandler handler; + + public AjAttributeStruct(ResolvedType type, ISourceContext sourceContext, IMessageHandler messageHandler) { + enclosingType = type; + context = sourceContext; + handler = messageHandler; + } + } + + /** + * A struct when we read @AJ on method + * + * @author Alexandre Vasseur + */ + private static class AjAttributeMethodStruct extends AjAttributeStruct { + + // argument names used for formal binding + private String[] m_argumentNamesLazy = null; + public String unparsedArgumentNames = null; // Set only if discovered as + // argNames attribute of + // annotation + + final Method method; + final BcelMethod bMethod; + + public AjAttributeMethodStruct(Method method, BcelMethod bMethod, ResolvedType type, ISourceContext sourceContext, + IMessageHandler messageHandler) { + super(type, sourceContext, messageHandler); + this.method = method; + this.bMethod = bMethod; + } + + public String[] getArgumentNames() { + if (m_argumentNamesLazy == null) { + m_argumentNamesLazy = getMethodArgumentNames(method, unparsedArgumentNames, this); + } + return m_argumentNamesLazy; + } + } + + /** + * A struct when we read @AJ on field + */ + private static class AjAttributeFieldStruct extends AjAttributeStruct { + + final Field field; + + // final BcelField bField; + + public AjAttributeFieldStruct(Field field, BcelField bField, ResolvedType type, ISourceContext sourceContext, + IMessageHandler messageHandler) { + super(type, sourceContext, messageHandler); + this.field = field; + // this.bField = bField; + } + } + + /** + * Annotations are RuntimeVisible only. This allow us to not visit RuntimeInvisible ones. + * + * @param attribute + * @return true if runtime visible annotation + */ + public static boolean acceptAttribute(Attribute attribute) { + return (attribute instanceof RuntimeVisAnnos); + } + + /** + * Extract class level annotations and turn them into AjAttributes. + * + * @param javaClass + * @param type + * @param context + * @param msgHandler + * @return list of AjAttributes + */ + public static List readAj5ClassAttributes(AsmManager model, JavaClass javaClass, ReferenceType type, + ISourceContext context, IMessageHandler msgHandler, boolean isCodeStyleAspect) { + boolean ignoreThisClass = javaClass.getClassName().charAt(0) == PACKAGE_INITIAL_CHAR + && javaClass.getClassName().startsWith(ASPECTJ_ANNOTATION_PACKAGE); + if (ignoreThisClass) { + return NO_ATTRIBUTES; + } + boolean containsPointcut = false; + boolean containsAnnotationClassReference = false; + Constant[] cpool = javaClass.getConstantPool().getConstantPool(); + for (int i = 0; i < cpool.length; i++) { + Constant constant = cpool[i]; + if (constant != null && constant.getTag() == Constants.CONSTANT_Utf8) { + String constantValue = ((ConstantUtf8) constant).getValue(); + if (constantValue.length() > 28 && constantValue.charAt(1) == PACKAGE_INITIAL_CHAR) { + if (constantValue.startsWith("Lorg/aspectj/lang/annotation")) { + containsAnnotationClassReference = true; + if ("Lorg/aspectj/lang/annotation/DeclareAnnotation;".equals(constantValue)) { + msgHandler.handleMessage(new Message( + "Found @DeclareAnnotation while current release does not support it (see '" + type.getName() + + "')", IMessage.WARNING, null, type.getSourceLocation())); + } + if ("Lorg/aspectj/lang/annotation/Pointcut;".equals(constantValue)) { + containsPointcut = true; + } + } + + } + } + } + if (!containsAnnotationClassReference) { + return NO_ATTRIBUTES; + } + + AjAttributeStruct struct = new AjAttributeStruct(type, context, msgHandler); + Attribute[] attributes = javaClass.getAttributes(); + boolean hasAtAspectAnnotation = false; + boolean hasAtPrecedenceAnnotation = false; + + WeaverVersionInfo wvinfo = null; + for (int i = 0; i < attributes.length; i++) { + Attribute attribute = attributes[i]; + if (acceptAttribute(attribute)) { + RuntimeAnnos rvs = (RuntimeAnnos) attribute; + // we don't need to look for several attribute occurrences since + // it cannot happen as per JSR175 + if (!isCodeStyleAspect && !javaClass.isInterface()) { + hasAtAspectAnnotation = handleAspectAnnotation(rvs, struct); + // TODO AV - if put outside the if isCodeStyleAspect then we + // would enable mix style + hasAtPrecedenceAnnotation = handlePrecedenceAnnotation(rvs, struct); + } + // there can only be one RuntimeVisible bytecode attribute + break; + } + } + for (int i = attributes.length - 1; i >= 0; i--) { + Attribute attribute = attributes[i]; + if (attribute.getName().equals(WeaverVersionInfo.AttributeName)) { + try { + VersionedDataInputStream s = new VersionedDataInputStream(new ByteArrayInputStream( + ((Unknown) attribute).getBytes()), null); + wvinfo = WeaverVersionInfo.read(s); + struct.ajAttributes.add(0, wvinfo); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + } + if (wvinfo == null) { + // If we are in here due to a resetState() call (presumably because of reweavable state processing), the + // original type delegate will have been set with a version but that version will be missing from + // the new set of attributes (looks like a bug where the version attribute was not included in the + // data compressed into the attribute). So rather than 'defaulting' to current, we should use one + // if it set on the delegate for the type. + ReferenceTypeDelegate delegate = type.getDelegate(); + if (delegate instanceof BcelObjectType) { + wvinfo = ((BcelObjectType) delegate).getWeaverVersionAttribute(); + if (wvinfo != null) { + if (wvinfo.getMajorVersion() != WeaverVersionInfo.WEAVER_VERSION_MAJOR_UNKNOWN) { + // use this one + struct.ajAttributes.add(0, wvinfo); + } else { + wvinfo = null; + } + } + } + if (wvinfo == null) { + struct.ajAttributes.add(0, wvinfo = new AjAttribute.WeaverVersionInfo()); + } + } + + // basic semantic check + if (hasAtPrecedenceAnnotation && !hasAtAspectAnnotation) { + msgHandler.handleMessage(new Message("Found @DeclarePrecedence on a non @Aspect type '" + type.getName() + "'", + IMessage.WARNING, null, type.getSourceLocation())); + // bypass what we have read + return NO_ATTRIBUTES; + } + + // the following block will not detect @Pointcut in non @Aspect types + // for optimization purpose + if (!(hasAtAspectAnnotation || isCodeStyleAspect) && !containsPointcut) { + return NO_ATTRIBUTES; + } + + // FIXME AV - turn on when ajcMightHaveAspect + // if (hasAtAspectAnnotation && type.isInterface()) { + // msgHandler.handleMessage( + // new Message( + // "Found @Aspect on an interface type '" + type.getName() + "'", + // IMessage.WARNING, + // null, + // type.getSourceLocation() + // ) + // ); + // // bypass what we have read + // return EMPTY_LIST; + // } + + // semantic check: @Aspect must be public + // FIXME AV - do we really want to enforce that? + // if (hasAtAspectAnnotation && !javaClass.isPublic()) { + // msgHandler.handleMessage( + // new Message( + // "Found @Aspect annotation on a non public class '" + + // javaClass.getClassName() + "'", + // IMessage.ERROR, + // null, + // type.getSourceLocation() + // ) + // ); + // return EMPTY_LIST; + // } + + // code style pointcuts are class attributes + // we need to gather the @AJ pointcut right now and not at method level + // annotation extraction time + // in order to be able to resolve the pointcut references later on + // we don't need to look in super class, the pointcut reference in the + // grammar will do it + + for (int i = 0; i < javaClass.getMethods().length; i++) { + Method method = javaClass.getMethods()[i]; + if (method.getName().startsWith(NameMangler.PREFIX)) { + continue; // already dealt with by ajc... + } + // FIXME alex optimize, this method struct will gets recreated for + // advice extraction + AjAttributeMethodStruct mstruct = null; + boolean processedPointcut = false; + Attribute[] mattributes = method.getAttributes(); + for (int j = 0; j < mattributes.length; j++) { + Attribute mattribute = mattributes[j]; + if (acceptAttribute(mattribute)) { + // TODO speed all this nonsense up rather than looking + // through all the annotations every time + // same for fields + mstruct = new AjAttributeMethodStruct(method, null, type, context, msgHandler); + processedPointcut = handlePointcutAnnotation((RuntimeAnnos) mattribute, mstruct); + if (!processedPointcut) { + processedPointcut = handleDeclareMixinAnnotation((RuntimeAnnos) mattribute, mstruct); + } + // there can only be one RuntimeVisible bytecode attribute + break; + } + } + if (processedPointcut) { + struct.ajAttributes.addAll(mstruct.ajAttributes); + } + } + + // code style declare error / warning / implements / parents are field + // attributes + Field[] fs = javaClass.getFields(); + for (int i = 0; i < fs.length; i++) { + Field field = fs[i]; + if (field.getName().startsWith(NameMangler.PREFIX)) { + continue; // already dealt with by ajc... + } + // FIXME alex optimize, this method struct will gets recreated for + // advice extraction + AjAttributeFieldStruct fstruct = new AjAttributeFieldStruct(field, null, type, context, msgHandler); + Attribute[] fattributes = field.getAttributes(); + + for (int j = 0; j < fattributes.length; j++) { + Attribute fattribute = fattributes[j]; + if (acceptAttribute(fattribute)) { + RuntimeAnnos frvs = (RuntimeAnnos) fattribute; + if (handleDeclareErrorOrWarningAnnotation(model, frvs, fstruct) + || handleDeclareParentsAnnotation(frvs, fstruct)) { + // semantic check - must be in an @Aspect [remove if + // previous block bypassed in advance] + if (!type.isAnnotationStyleAspect() && !isCodeStyleAspect) { + msgHandler.handleMessage(new Message("Found @AspectJ annotations in a non @Aspect type '" + + type.getName() + "'", IMessage.WARNING, null, type.getSourceLocation())); + // go ahead + } + } + // there can only be one RuntimeVisible bytecode attribute + break; + } + } + struct.ajAttributes.addAll(fstruct.ajAttributes); + } + + return struct.ajAttributes; + } + + /** + * Extract method level annotations and turn them into AjAttributes. + * + * @param method + * @param type + * @param context + * @param msgHandler + * @return list of AjAttributes + */ + public static List readAj5MethodAttributes(Method method, BcelMethod bMethod, ResolvedType type, + ResolvedPointcutDefinition preResolvedPointcut, ISourceContext context, IMessageHandler msgHandler) { + if (method.getName().startsWith(NameMangler.PREFIX)) { + return Collections.emptyList(); // already dealt with by ajc... + } + + AjAttributeMethodStruct struct = new AjAttributeMethodStruct(method, bMethod, type, context, msgHandler); + Attribute[] attributes = method.getAttributes(); + + // we remember if we found one @AJ annotation for minimal semantic error + // reporting + // the real reporting beeing done thru AJDT and the compiler mapping @AJ + // to AjAtttribute + // or thru APT + // + // Note: we could actually skip the whole thing if type is not itself an + // @Aspect + // but then we would not see any warning. We do bypass for pointcut but + // not for advice since it would + // be too silent. + boolean hasAtAspectJAnnotation = false; + boolean hasAtAspectJAnnotationMustReturnVoid = false; + for (int i = 0; i < attributes.length; i++) { + Attribute attribute = attributes[i]; + try { + if (acceptAttribute(attribute)) { + RuntimeAnnos rvs = (RuntimeAnnos) attribute; + hasAtAspectJAnnotationMustReturnVoid = hasAtAspectJAnnotationMustReturnVoid + || handleBeforeAnnotation(rvs, struct, preResolvedPointcut); + hasAtAspectJAnnotationMustReturnVoid = hasAtAspectJAnnotationMustReturnVoid + || handleAfterAnnotation(rvs, struct, preResolvedPointcut); + hasAtAspectJAnnotationMustReturnVoid = hasAtAspectJAnnotationMustReturnVoid + || handleAfterReturningAnnotation(rvs, struct, preResolvedPointcut, bMethod); + hasAtAspectJAnnotationMustReturnVoid = hasAtAspectJAnnotationMustReturnVoid + || handleAfterThrowingAnnotation(rvs, struct, preResolvedPointcut, bMethod); + hasAtAspectJAnnotation = hasAtAspectJAnnotation || handleAroundAnnotation(rvs, struct, preResolvedPointcut); + // there can only be one RuntimeVisible bytecode attribute + break; + } + } catch (ReturningFormalNotDeclaredInAdviceSignatureException e) { + msgHandler.handleMessage(new Message(WeaverMessages.format(WeaverMessages.RETURNING_FORMAL_NOT_DECLARED_IN_ADVICE, + e.getFormalName()), IMessage.ERROR, null, bMethod.getSourceLocation())); + } catch (ThrownFormalNotDeclaredInAdviceSignatureException e) { + msgHandler.handleMessage(new Message(WeaverMessages.format(WeaverMessages.THROWN_FORMAL_NOT_DECLARED_IN_ADVICE, + e.getFormalName()), IMessage.ERROR, null, bMethod.getSourceLocation())); + } + } + hasAtAspectJAnnotation = hasAtAspectJAnnotation || hasAtAspectJAnnotationMustReturnVoid; + + // semantic check - must be in an @Aspect [remove if previous block + // bypassed in advance] + if (hasAtAspectJAnnotation && !type.isAspect()) { // isAnnotationStyleAspect()) + // { + msgHandler.handleMessage(new Message("Found @AspectJ annotations in a non @Aspect type '" + type.getName() + "'", + IMessage.WARNING, null, type.getSourceLocation())); + // go ahead + } + // semantic check - advice must be public + if (hasAtAspectJAnnotation && !struct.method.isPublic()) { + msgHandler.handleMessage(new Message("Found @AspectJ annotation on a non public advice '" + + methodToString(struct.method) + "'", IMessage.ERROR, null, type.getSourceLocation())); + // go ahead + } + + // semantic check - advice must not be static + if (hasAtAspectJAnnotation && struct.method.isStatic()) { + msgHandler.handleMessage(MessageUtil.error("Advice cannot be declared static '" + methodToString(struct.method) + "'", + type.getSourceLocation())); + // new Message( + // "Advice cannot be declared static '" + + // methodToString(struct.method) + "'", + // IMessage.ERROR, + // null, + // type.getSourceLocation() + // ) + // ); + // go ahead + } + + // semantic check for non around advice must return void + if (hasAtAspectJAnnotationMustReturnVoid && !Type.VOID.equals(struct.method.getReturnType())) { + msgHandler.handleMessage(new Message("Found @AspectJ annotation on a non around advice not returning void '" + + methodToString(struct.method) + "'", IMessage.ERROR, null, type.getSourceLocation())); + // go ahead + } + + return struct.ajAttributes; + } + + /** + * Extract field level annotations and turn them into AjAttributes. + * + * @param field + * @param type + * @param context + * @param msgHandler + * @return list of AjAttributes, always empty for now + */ + public static List readAj5FieldAttributes(Field field, BcelField bField, ResolvedType type, + ISourceContext context, IMessageHandler msgHandler) { + // Note: field annotation are for ITD and DEOW - processed at class + // level directly + return Collections.emptyList(); + } + + /** + * Read @Aspect + * + * @param runtimeAnnotations + * @param struct + * @return true if found + */ + private static boolean handleAspectAnnotation(RuntimeAnnos runtimeAnnotations, AjAttributeStruct struct) { + AnnotationGen aspect = getAnnotation(runtimeAnnotations, AjcMemberMaker.ASPECT_ANNOTATION); + if (aspect != null) { + // semantic check for inheritance (only one level up) + boolean extendsAspect = false; + if (!"java.lang.Object".equals(struct.enclosingType.getSuperclass().getName())) { + if (!struct.enclosingType.getSuperclass().isAbstract() && struct.enclosingType.getSuperclass().isAspect()) { + reportError("cannot extend a concrete aspect", struct); + return false; + } + extendsAspect = struct.enclosingType.getSuperclass().isAspect(); + } + + NameValuePair aspectPerClause = getAnnotationElement(aspect, VALUE); + final PerClause perClause; + if (aspectPerClause == null) { + // empty value means singleton unless inherited + if (!extendsAspect) { + perClause = new PerSingleton(); + } else { + perClause = new PerFromSuper(struct.enclosingType.getSuperclass().getPerClause().getKind()); + } + } else { + String perX = aspectPerClause.getValue().stringifyValue(); + if (perX == null || perX.length() <= 0) { + perClause = new PerSingleton(); + } else { + perClause = parsePerClausePointcut(perX, struct); + } + } + if (perClause == null) { + // could not parse it, ignore the aspect + return false; + } else { + perClause.setLocation(struct.context, -1, -1);// struct.context.getOffset(), + // struct.context.getOffset()+1);//FIXME + // AVASM + // Not setting version here + // struct.ajAttributes.add(new AjAttribute.WeaverVersionInfo()); + AjAttribute.Aspect aspectAttribute = new AjAttribute.Aspect(perClause); + struct.ajAttributes.add(aspectAttribute); + FormalBinding[] bindings = new org.aspectj.weaver.patterns.FormalBinding[0]; + final IScope binding; + binding = new BindingScope(struct.enclosingType, struct.context, bindings); + + // // we can't resolve here since the perclause typically refers + // to pointcuts + // // defined in the aspect that we haven't told the + // BcelObjectType about yet. + // + // perClause.resolve(binding); + + // so we prepare to do it later... + aspectAttribute.setResolutionScope(binding); + return true; + } + } + return false; + } + + /** + * Read a perClause, returns null on failure and issue messages + * + * @param perClauseString like "pertarget(.....)" + * @param struct for which we are parsing the per clause + * @return a PerClause instance + */ + private static PerClause parsePerClausePointcut(String perClauseString, AjAttributeStruct struct) { + final String pointcutString; + Pointcut pointcut = null; + TypePattern typePattern = null; + final PerClause perClause; + if (perClauseString.startsWith(PerClause.KindAnnotationPrefix.PERCFLOW.getName())) { + pointcutString = PerClause.KindAnnotationPrefix.PERCFLOW.extractPointcut(perClauseString); + pointcut = parsePointcut(pointcutString, struct, false); + perClause = new PerCflow(pointcut, false); + } else if (perClauseString.startsWith(PerClause.KindAnnotationPrefix.PERCFLOWBELOW.getName())) { + pointcutString = PerClause.KindAnnotationPrefix.PERCFLOWBELOW.extractPointcut(perClauseString); + pointcut = parsePointcut(pointcutString, struct, false); + perClause = new PerCflow(pointcut, true); + } else if (perClauseString.startsWith(PerClause.KindAnnotationPrefix.PERTARGET.getName())) { + pointcutString = PerClause.KindAnnotationPrefix.PERTARGET.extractPointcut(perClauseString); + pointcut = parsePointcut(pointcutString, struct, false); + perClause = new PerObject(pointcut, false); + } else if (perClauseString.startsWith(PerClause.KindAnnotationPrefix.PERTHIS.getName())) { + pointcutString = PerClause.KindAnnotationPrefix.PERTHIS.extractPointcut(perClauseString); + pointcut = parsePointcut(pointcutString, struct, false); + perClause = new PerObject(pointcut, true); + } else if (perClauseString.startsWith(PerClause.KindAnnotationPrefix.PERTYPEWITHIN.getName())) { + pointcutString = PerClause.KindAnnotationPrefix.PERTYPEWITHIN.extractPointcut(perClauseString); + typePattern = parseTypePattern(pointcutString, struct); + perClause = new PerTypeWithin(typePattern); + } else if (perClauseString.equalsIgnoreCase(PerClause.SINGLETON.getName() + "()")) { + perClause = new PerSingleton(); + } else { + // could not parse the @AJ perclause - fallback to singleton and + // issue an error + reportError("@Aspect per clause cannot be read '" + perClauseString + "'", struct); + return null; + } + + if (!PerClause.SINGLETON.equals(perClause.getKind()) && !PerClause.PERTYPEWITHIN.equals(perClause.getKind()) + && pointcut == null) { + // we could not parse the pointcut + return null; + } + if (PerClause.PERTYPEWITHIN.equals(perClause.getKind()) && typePattern == null) { + // we could not parse the type pattern + return null; + } + return perClause; + } + + /** + * Read @DeclarePrecedence + * + * @param runtimeAnnotations + * @param struct + * @return true if found + */ + private static boolean handlePrecedenceAnnotation(RuntimeAnnos runtimeAnnotations, AjAttributeStruct struct) { + AnnotationGen aspect = getAnnotation(runtimeAnnotations, AjcMemberMaker.DECLAREPRECEDENCE_ANNOTATION); + if (aspect != null) { + NameValuePair precedence = getAnnotationElement(aspect, VALUE); + if (precedence != null) { + String precedencePattern = precedence.getValue().stringifyValue(); + PatternParser parser = new PatternParser(precedencePattern); + DeclarePrecedence ajPrecedence = parser.parseDominates(); + struct.ajAttributes.add(new AjAttribute.DeclareAttribute(ajPrecedence)); + return true; + } + } + return false; + } + + // /** + // * Read @DeclareImplements + // * + // * @param runtimeAnnotations + // * @param struct + // * @return true if found + // */ + // private static boolean + // handleDeclareImplementsAnnotation(RuntimeAnnotations runtimeAnnotations, + // AjAttributeFieldStruct + // struct) {//, ResolvedPointcutDefinition preResolvedPointcut) { + // Annotation deci = getAnnotation(runtimeAnnotations, + // AjcMemberMaker.DECLAREIMPLEMENTS_ANNOTATION); + // if (deci != null) { + // ElementNameValuePairGen deciPatternNVP = getAnnotationElement(deci, + // VALUE); + // String deciPattern = deciPatternNVP.getValue().stringifyValue(); + // if (deciPattern != null) { + // TypePattern typePattern = parseTypePattern(deciPattern, struct); + // ResolvedType fieldType = + // UnresolvedType.forSignature(struct.field.getSignature()).resolve(struct.enclosingType.getWorld()); + // if (fieldType.isPrimitiveType()) { + // return false; + // } else if (fieldType.isInterface()) { + // TypePattern parent = new + // ExactTypePattern(UnresolvedType.forSignature(struct.field.getSignature()), + // false, false); + // parent.resolve(struct.enclosingType.getWorld()); + // List parents = new ArrayList(1); + // parents.add(parent); + // //TODO kick ISourceLocation sl = struct.bField.getSourceLocation(); ?? + // struct.ajAttributes.add( + // new AjAttribute.DeclareAttribute( + // new DeclareParents( + // typePattern, + // parents, + // false + // ) + // ) + // ); + // return true; + // } else { + // reportError("@DeclareImplements: can only be used on field whose type is an interface", + // struct); + // return false; + // } + // } + // } + // return false; + // } + + /** + * Read @DeclareParents + * + * @param runtimeAnnotations + * @param struct + * @return true if found + */ + private static boolean handleDeclareParentsAnnotation(RuntimeAnnos runtimeAnnotations, AjAttributeFieldStruct struct) {// , + // ResolvedPointcutDefinition + // preResolvedPointcut) + // { + AnnotationGen decp = getAnnotation(runtimeAnnotations, AjcMemberMaker.DECLAREPARENTS_ANNOTATION); + if (decp != null) { + NameValuePair decpPatternNVP = getAnnotationElement(decp, VALUE); + String decpPattern = decpPatternNVP.getValue().stringifyValue(); + if (decpPattern != null) { + TypePattern typePattern = parseTypePattern(decpPattern, struct); + ResolvedType fieldType = UnresolvedType.forSignature(struct.field.getSignature()).resolve( + struct.enclosingType.getWorld()); + if (fieldType.isParameterizedOrRawType()) { + fieldType = fieldType.getGenericType(); + } + if (fieldType.isInterface()) { + TypePattern parent = parseTypePattern(fieldType.getName(), struct); + FormalBinding[] bindings = new org.aspectj.weaver.patterns.FormalBinding[0]; + IScope binding = new BindingScope(struct.enclosingType, struct.context, bindings); + // first add the declare implements like + List parents = new ArrayList(1); + parents.add(parent); + DeclareParents dp = new DeclareParents(typePattern, parents, false); + dp.resolve(binding); // resolves the parent and child parts of the decp + + // resolve this so that we can use it for the + // MethodDelegateMungers below. + // eg. '@Coloured *' will change from a WildTypePattern to + // an 'AnyWithAnnotationTypePattern' after this resolution + typePattern = dp.getChild(); // this retrieves the resolved version + // TODO kick ISourceLocation sl = + // struct.bField.getSourceLocation(); ?? + // dp.setLocation(dp.getDeclaringType().getSourceContext(), + // dp.getDeclaringType().getSourceLocation().getOffset(), + // dp.getDeclaringType().getSourceLocation().getOffset()); + dp.setLocation(struct.context, -1, -1); // not ideal... + struct.ajAttributes.add(new AjAttribute.DeclareAttribute(dp)); + + // do we have a defaultImpl=xxx.class (ie implementation) + String defaultImplClassName = null; + NameValuePair defaultImplNVP = getAnnotationElement(decp, "defaultImpl"); + if (defaultImplNVP != null) { + ClassElementValue defaultImpl = (ClassElementValue) defaultImplNVP.getValue(); + defaultImplClassName = UnresolvedType.forSignature(defaultImpl.getClassString()).getName(); + if (defaultImplClassName.equals("org.aspectj.lang.annotation.DeclareParents")) { + defaultImplClassName = null; + } else { + // check public no arg ctor + ResolvedType impl = struct.enclosingType.getWorld().resolve(defaultImplClassName, false); + ResolvedMember[] mm = impl.getDeclaredMethods(); + int implModifiers = impl.getModifiers(); + boolean defaultVisibilityImpl = !(Modifier.isPrivate(implModifiers) + || Modifier.isProtected(implModifiers) || Modifier.isPublic(implModifiers)); + boolean hasNoCtorOrANoArgOne = true; + ResolvedMember foundOneOfIncorrectVisibility = null; + for (int i = 0; i < mm.length; i++) { + ResolvedMember resolvedMember = mm[i]; + if (resolvedMember.getName().equals("")) { + hasNoCtorOrANoArgOne = false; + + if (resolvedMember.getParameterTypes().length == 0) { + if (defaultVisibilityImpl) { // default visibility implementation + if (resolvedMember.isPublic() || resolvedMember.isDefault()) { + hasNoCtorOrANoArgOne = true; + } else { + foundOneOfIncorrectVisibility = resolvedMember; + } + } else if (Modifier.isPublic(implModifiers)) { // public + // implementation + if (resolvedMember.isPublic()) { + hasNoCtorOrANoArgOne = true; + } else { + foundOneOfIncorrectVisibility = resolvedMember; + } + } + } + } + if (hasNoCtorOrANoArgOne) { + break; + } + } + if (!hasNoCtorOrANoArgOne) { + if (foundOneOfIncorrectVisibility != null) { + reportError( + "@DeclareParents: defaultImpl=\"" + + defaultImplClassName + + "\" has a no argument constructor, but it is of incorrect visibility. It must be at least as visible as the type.", + struct); + } else { + reportError("@DeclareParents: defaultImpl=\"" + defaultImplClassName + + "\" has no public no-arg constructor", struct); + } + } + if (!fieldType.isAssignableFrom(impl)) { + reportError("@DeclareParents: defaultImpl=\"" + defaultImplClassName + + "\" does not implement the interface '" + fieldType.toString() + "'", struct); + } + } + + } + + // then iterate on field interface hierarchy (not object) + boolean hasAtLeastOneMethod = false; + Iterator methodIterator = fieldType.getMethodsIncludingIntertypeDeclarations(false, true); + while (methodIterator.hasNext()) { + ResolvedMember method = methodIterator.next(); + if (method.isAbstract()) { + // moved to be detected at weave time if the target + // doesnt implement the methods + // if (defaultImplClassName == null) { + // // non marker interface with no default impl + // provided + // reportError("@DeclareParents: used with a non marker interface and no defaultImpl=\"...\" provided", + // struct); + // return false; + // } + hasAtLeastOneMethod = true; + // What we are saying here: + // We have this method 'method' and we want to put a + // forwarding method into a type that matches + // typePattern that should delegate to the version + // of the method in 'defaultImplClassName' + + // Now the method may be from a supertype but the + // declaring type of the method we pass into the + // type + // munger is what is used to determine the type of + // the field that hosts the delegate instance. + // So here we create a modified method with an + // alternative declaring type so that we lookup + // the right field. See pr164016. + MethodDelegateTypeMunger mdtm = new MethodDelegateTypeMunger(method, struct.enclosingType, defaultImplClassName, typePattern); + mdtm.setFieldType(fieldType); + mdtm.setSourceLocation(struct.enclosingType.getSourceLocation()); + struct.ajAttributes.add(new AjAttribute.TypeMunger(mdtm)); + } + } + // successful so far, we thus need a bcel type munger to have + // a field hosting the mixin in the target type + if (hasAtLeastOneMethod && defaultImplClassName != null) { + ResolvedMember fieldHost = AjcMemberMaker.itdAtDeclareParentsField(null, fieldType, struct.enclosingType); + struct.ajAttributes.add(new AjAttribute.TypeMunger(new MethodDelegateTypeMunger.FieldHostTypeMunger( + fieldHost, struct.enclosingType, typePattern))); + } + return true; + } else { + reportError("@DeclareParents: can only be used on a field whose type is an interface", struct); + return false; + } + } + } + return false; + } + + /** + * Return a nicely formatted method string, for example: int X.foo(java.lang.String) + */ + public static String getMethodForMessage(AjAttributeMethodStruct methodstructure) { + StringBuffer sb = new StringBuffer(); + sb.append("Method '"); + sb.append(methodstructure.method.getReturnType().toString()); + sb.append(" ").append(methodstructure.enclosingType).append(".").append(methodstructure.method.getName()); + sb.append("("); + Type[] args = methodstructure.method.getArgumentTypes(); + if (args != null) { + for (int t = 0; t < args.length; t++) { + if (t > 0) { + sb.append(","); + } + sb.append(args[t].toString()); + } + } + sb.append(")'"); + return sb.toString(); + } + + /** + * Process any @DeclareMixin annotation. + * + * Example Declaration
+ * + * @DeclareMixin("Foo+") public I createImpl(Object o) { return new Impl(o); } + * + *
+ * @param runtimeAnnotations + * @param struct + * @return true if found + */ + private static boolean handleDeclareMixinAnnotation(RuntimeAnnos runtimeAnnotations, AjAttributeMethodStruct struct) { + AnnotationGen declareMixinAnnotation = getAnnotation(runtimeAnnotations, AjcMemberMaker.DECLAREMIXIN_ANNOTATION); + if (declareMixinAnnotation == null) { + // No annotation found + return false; + } + Method annotatedMethod = struct.method; + World world = struct.enclosingType.getWorld(); + NameValuePair declareMixinPatternNameValuePair = getAnnotationElement(declareMixinAnnotation, VALUE); + + // declareMixinPattern could be of the form "Bar*" or "A || B" or "Foo+" + String declareMixinPattern = declareMixinPatternNameValuePair.getValue().stringifyValue(); + TypePattern targetTypePattern = parseTypePattern(declareMixinPattern, struct); + + // Return value of the annotated method is the interface or class that the mixin delegate should have + ResolvedType methodReturnType = UnresolvedType.forSignature(annotatedMethod.getReturnType().getSignature()).resolve(world); + if (methodReturnType.isParameterizedOrRawType()) { + methodReturnType = methodReturnType.getGenericType(); + } + if (methodReturnType.isPrimitiveType()) { + reportError(getMethodForMessage(struct) + ": factory methods for a mixin cannot return void or a primitive type", + struct); + return false; + } + + if (annotatedMethod.getArgumentTypes().length > 1) { + reportError(getMethodForMessage(struct) + ": factory methods for a mixin can take a maximum of one parameter", struct); + return false; + } + + // The set of interfaces to be mixed in is either: + // supplied as a list in the 'Class[] interfaces' value in the annotation value + // supplied as just the interface return value of the annotated method + // supplied as just the class return value of the annotated method + NameValuePair interfaceListSpecified = getAnnotationElement(declareMixinAnnotation, "interfaces"); + + List newParents = new ArrayList(1); + List newInterfaceTypes = new ArrayList(1); + if (interfaceListSpecified != null) { + ArrayElementValue arrayOfInterfaceTypes = (ArrayElementValue) interfaceListSpecified.getValue(); + int numberOfTypes = arrayOfInterfaceTypes.getElementValuesArraySize(); + ElementValue[] theTypes = arrayOfInterfaceTypes.getElementValuesArray(); + for (int i = 0; i < numberOfTypes; i++) { + ClassElementValue interfaceType = (ClassElementValue) theTypes[i]; + // Check: needs to be resolvable + // TODO crappy replace required + ResolvedType ajInterfaceType = UnresolvedType.forSignature(interfaceType.getClassString().replace("/", ".")) + .resolve(world); + if (ajInterfaceType.isMissing() || !ajInterfaceType.isInterface()) { + reportError( + "Types listed in the 'interfaces' DeclareMixin annotation value must be valid interfaces. This is invalid: " + + ajInterfaceType.getName(), struct); // TODO better error location, use the method position + return false; + } + if (!ajInterfaceType.isAssignableFrom(methodReturnType)) { + reportError(getMethodForMessage(struct) + ": factory method does not return something that implements '" + + ajInterfaceType.getName() + "'", struct); + return false; + } + newInterfaceTypes.add(ajInterfaceType); + // Checking that it is a superinterface of the methods return value is done at weave time + TypePattern newParent = parseTypePattern(ajInterfaceType.getName(), struct); + newParents.add(newParent); + } + } else { + if (methodReturnType.isClass()) { + reportError( + getMethodForMessage(struct) + + ": factory methods for a mixin must either return an interface type or specify interfaces in the annotation and return a class", + struct); + return false; + } + // Use the method return type: this might be a class or an interface + TypePattern newParent = parseTypePattern(methodReturnType.getName(), struct); + newInterfaceTypes.add(methodReturnType); + newParents.add(newParent); + } + if (newParents.size() == 0) { + // Warning: did they foolishly put @DeclareMixin(value="Bar+",interfaces={}) + // TODO output warning + return false; + } + + // Create the declare parents that will add the interfaces to matching targets + FormalBinding[] bindings = new org.aspectj.weaver.patterns.FormalBinding[0]; + IScope binding = new BindingScope(struct.enclosingType, struct.context, bindings); + // how do we mark this as a decp due to decmixin? + DeclareParents dp = new DeclareParentsMixin(targetTypePattern, newParents); + dp.resolve(binding); + targetTypePattern = dp.getChild(); + + dp.setLocation(struct.context, -1, -1); // not ideal... + struct.ajAttributes.add(new AjAttribute.DeclareAttribute(dp)); + + // The factory method for building the implementation is the + // one attached to the annotation: + // Method implementationFactory = struct.method; + + boolean hasAtLeastOneMethod = false; + + for (Iterator iterator = newInterfaceTypes.iterator(); iterator.hasNext();) { + ResolvedType typeForDelegation = iterator.next(); + // TODO check for overlapping interfaces. Eg. A implements I, I extends J - if they specify interfaces={I,J} we dont + // want to do any methods twice + ResolvedMember[] methods = typeForDelegation.getMethodsWithoutIterator(true, false, false).toArray( + new ResolvedMember[0]); + for (int i = 0; i < methods.length; i++) { + ResolvedMember method = methods[i]; + if (method.isAbstract()) { + hasAtLeastOneMethod = true; + if (method.hasBackingGenericMember()) { + method = method.getBackingGenericMember(); + } + MethodDelegateTypeMunger mdtm = new MethodDelegateTypeMunger(method, struct.enclosingType, "", + targetTypePattern, struct.method.getName(), struct.method.getSignature()); + mdtm.setFieldType(methodReturnType); + mdtm.setSourceLocation(struct.enclosingType.getSourceLocation()); + struct.ajAttributes.add(new AjAttribute.TypeMunger(mdtm)); + } + } + } + // if any method delegate was created then a field to hold the delegate instance must also be added + if (hasAtLeastOneMethod) { + ResolvedMember fieldHost = AjcMemberMaker.itdAtDeclareParentsField(null, methodReturnType, struct.enclosingType); + struct.ajAttributes.add(new AjAttribute.TypeMunger(new MethodDelegateTypeMunger.FieldHostTypeMunger(fieldHost, + struct.enclosingType, targetTypePattern))); + } + return true; + } + + /** + * Read @Before + * + * @param runtimeAnnotations + * @param struct + * @return true if found + */ + private static boolean handleBeforeAnnotation(RuntimeAnnos runtimeAnnotations, AjAttributeMethodStruct struct, + ResolvedPointcutDefinition preResolvedPointcut) { + AnnotationGen before = getAnnotation(runtimeAnnotations, AjcMemberMaker.BEFORE_ANNOTATION); + if (before != null) { + NameValuePair beforeAdvice = getAnnotationElement(before, VALUE); + if (beforeAdvice != null) { + // this/target/args binding + String argumentNames = getArgNamesValue(before); + if (argumentNames != null) { + struct.unparsedArgumentNames = argumentNames; + } + FormalBinding[] bindings = new org.aspectj.weaver.patterns.FormalBinding[0]; + try { + bindings = extractBindings(struct); + } catch (UnreadableDebugInfoException unreadableDebugInfoException) { + return false; + } + IScope binding = new BindingScope(struct.enclosingType, struct.context, bindings); + + // joinpoint, staticJoinpoint binding + int extraArgument = extractExtraArgument(struct.method); + + Pointcut pc = null; + if (preResolvedPointcut != null) { + pc = preResolvedPointcut.getPointcut(); + // pc.resolve(binding); + } else { + pc = parsePointcut(beforeAdvice.getValue().stringifyValue(), struct, false); + if (pc == null) { + return false;// parse error + } + pc = pc.resolve(binding); + } + setIgnoreUnboundBindingNames(pc, bindings); + + ISourceLocation sl = struct.context.makeSourceLocation(struct.bMethod.getDeclarationLineNumber(), + struct.bMethod.getDeclarationOffset()); + struct.ajAttributes.add(new AjAttribute.AdviceAttribute(AdviceKind.Before, pc, extraArgument, sl.getOffset(), sl + .getOffset() + 1,// FIXME AVASM + struct.context)); + return true; + } + } + return false; + } + + /** + * Read @After + * + * @param runtimeAnnotations + * @param struct + * @return true if found + */ + private static boolean handleAfterAnnotation(RuntimeAnnos runtimeAnnotations, AjAttributeMethodStruct struct, + ResolvedPointcutDefinition preResolvedPointcut) { + AnnotationGen after = getAnnotation(runtimeAnnotations, AjcMemberMaker.AFTER_ANNOTATION); + if (after != null) { + NameValuePair afterAdvice = getAnnotationElement(after, VALUE); + if (afterAdvice != null) { + // this/target/args binding + FormalBinding[] bindings = new org.aspectj.weaver.patterns.FormalBinding[0]; + String argumentNames = getArgNamesValue(after); + if (argumentNames != null) { + struct.unparsedArgumentNames = argumentNames; + } + try { + bindings = extractBindings(struct); + } catch (UnreadableDebugInfoException unreadableDebugInfoException) { + return false; + } + IScope binding = new BindingScope(struct.enclosingType, struct.context, bindings); + + // joinpoint, staticJoinpoint binding + int extraArgument = extractExtraArgument(struct.method); + + Pointcut pc = null; + if (preResolvedPointcut != null) { + pc = preResolvedPointcut.getPointcut(); + } else { + pc = parsePointcut(afterAdvice.getValue().stringifyValue(), struct, false); + if (pc == null) { + return false;// parse error + } + pc.resolve(binding); + } + setIgnoreUnboundBindingNames(pc, bindings); + + ISourceLocation sl = struct.context.makeSourceLocation(struct.bMethod.getDeclarationLineNumber(), + struct.bMethod.getDeclarationOffset()); + struct.ajAttributes.add(new AjAttribute.AdviceAttribute(AdviceKind.After, pc, extraArgument, sl.getOffset(), sl + .getOffset() + 1,// FIXME AVASM + struct.context)); + return true; + } + } + return false; + } + + /** + * Read @AfterReturning + * + * @param runtimeAnnotations + * @param struct + * @return true if found + */ + private static boolean handleAfterReturningAnnotation(RuntimeAnnos runtimeAnnotations, AjAttributeMethodStruct struct, + ResolvedPointcutDefinition preResolvedPointcut, BcelMethod owningMethod) + throws ReturningFormalNotDeclaredInAdviceSignatureException { + AnnotationGen after = getAnnotation(runtimeAnnotations, AjcMemberMaker.AFTERRETURNING_ANNOTATION); + if (after != null) { + NameValuePair annValue = getAnnotationElement(after, VALUE); + NameValuePair annPointcut = getAnnotationElement(after, POINTCUT); + NameValuePair annReturned = getAnnotationElement(after, RETURNING); + + // extract the pointcut and returned type/binding - do some checks + String pointcut = null; + String returned = null; + if ((annValue != null && annPointcut != null) || (annValue == null && annPointcut == null)) { + reportError("@AfterReturning: either 'value' or 'poincut' must be provided, not both", struct); + return false; + } + if (annValue != null) { + pointcut = annValue.getValue().stringifyValue(); + } else { + pointcut = annPointcut.getValue().stringifyValue(); + } + if (isNullOrEmpty(pointcut)) { + reportError("@AfterReturning: either 'value' or 'poincut' must be provided, not both", struct); + return false; + } + if (annReturned != null) { + returned = annReturned.getValue().stringifyValue(); + if (isNullOrEmpty(returned)) { + returned = null; + } else { + // check that thrownFormal exists as the last parameter in + // the advice + String[] pNames = owningMethod.getParameterNames(); + if (pNames == null || pNames.length == 0 || !Arrays.asList(pNames).contains(returned)) { + throw new ReturningFormalNotDeclaredInAdviceSignatureException(returned); + } + } + } + String argumentNames = getArgNamesValue(after); + if (argumentNames != null) { + struct.unparsedArgumentNames = argumentNames; + } + // this/target/args binding + // exclude the return binding from the pointcut binding since it is + // an extraArg binding + FormalBinding[] bindings = new org.aspectj.weaver.patterns.FormalBinding[0]; + try { + bindings = (returned == null ? extractBindings(struct) : extractBindings(struct, returned)); + } catch (UnreadableDebugInfoException unreadableDebugInfoException) { + return false; + } + IScope binding = new BindingScope(struct.enclosingType, struct.context, bindings); + + // joinpoint, staticJoinpoint binding + int extraArgument = extractExtraArgument(struct.method); + + // return binding + if (returned != null) { + extraArgument |= Advice.ExtraArgument; + } + + Pointcut pc = null; + if (preResolvedPointcut != null) { + pc = preResolvedPointcut.getPointcut(); + } else { + pc = parsePointcut(pointcut, struct, false); + if (pc == null) { + return false;// parse error + } + pc.resolve(binding); + } + setIgnoreUnboundBindingNames(pc, bindings); + + ISourceLocation sl = struct.context.makeSourceLocation(struct.bMethod.getDeclarationLineNumber(), + struct.bMethod.getDeclarationOffset()); + struct.ajAttributes.add(new AjAttribute.AdviceAttribute(AdviceKind.AfterReturning, pc, extraArgument, sl.getOffset(), + sl.getOffset() + 1,// FIXME AVASM + struct.context)); + return true; + } + return false; + } + + /** + * Read @AfterThrowing + * + * @param runtimeAnnotations + * @param struct + * @return true if found + */ + private static boolean handleAfterThrowingAnnotation(RuntimeAnnos runtimeAnnotations, AjAttributeMethodStruct struct, + ResolvedPointcutDefinition preResolvedPointcut, BcelMethod owningMethod) + throws ThrownFormalNotDeclaredInAdviceSignatureException { + AnnotationGen after = getAnnotation(runtimeAnnotations, AjcMemberMaker.AFTERTHROWING_ANNOTATION); + if (after != null) { + NameValuePair annValue = getAnnotationElement(after, VALUE); + NameValuePair annPointcut = getAnnotationElement(after, POINTCUT); + NameValuePair annThrown = getAnnotationElement(after, THROWING); + + // extract the pointcut and throwned type/binding - do some checks + String pointcut = null; + String thrownFormal = null; + if ((annValue != null && annPointcut != null) || (annValue == null && annPointcut == null)) { + reportError("@AfterThrowing: either 'value' or 'poincut' must be provided, not both", struct); + return false; + } + if (annValue != null) { + pointcut = annValue.getValue().stringifyValue(); + } else { + pointcut = annPointcut.getValue().stringifyValue(); + } + if (isNullOrEmpty(pointcut)) { + reportError("@AfterThrowing: either 'value' or 'poincut' must be provided, not both", struct); + return false; + } + if (annThrown != null) { + thrownFormal = annThrown.getValue().stringifyValue(); + if (isNullOrEmpty(thrownFormal)) { + thrownFormal = null; + } else { + // check that thrownFormal exists as the last parameter in + // the advice + String[] pNames = owningMethod.getParameterNames(); + if (pNames == null || pNames.length == 0 || !Arrays.asList(pNames).contains(thrownFormal)) { + throw new ThrownFormalNotDeclaredInAdviceSignatureException(thrownFormal); + } + } + } + String argumentNames = getArgNamesValue(after); + if (argumentNames != null) { + struct.unparsedArgumentNames = argumentNames; + } + // this/target/args binding + // exclude the throwned binding from the pointcut binding since it + // is an extraArg binding + FormalBinding[] bindings = new org.aspectj.weaver.patterns.FormalBinding[0]; + try { + bindings = (thrownFormal == null ? extractBindings(struct) : extractBindings(struct, thrownFormal)); + } catch (UnreadableDebugInfoException unreadableDebugInfoException) { + return false; + } + IScope binding = new BindingScope(struct.enclosingType, struct.context, bindings); + + // joinpoint, staticJoinpoint binding + int extraArgument = extractExtraArgument(struct.method); + + // return binding + if (thrownFormal != null) { + extraArgument |= Advice.ExtraArgument; + } + + Pointcut pc = null; + if (preResolvedPointcut != null) { + pc = preResolvedPointcut.getPointcut(); + } else { + pc = parsePointcut(pointcut, struct, false); + if (pc == null) { + return false;// parse error + } + pc.resolve(binding); + } + setIgnoreUnboundBindingNames(pc, bindings); + + ISourceLocation sl = struct.context.makeSourceLocation(struct.bMethod.getDeclarationLineNumber(), + struct.bMethod.getDeclarationOffset()); + struct.ajAttributes.add(new AjAttribute.AdviceAttribute(AdviceKind.AfterThrowing, pc, extraArgument, sl.getOffset(), sl + .getOffset() + 1, struct.context)); + return true; + } + return false; + } + + /** + * Read @Around + * + * @param runtimeAnnotations + * @param struct + * @return true if found + */ + private static boolean handleAroundAnnotation(RuntimeAnnos runtimeAnnotations, AjAttributeMethodStruct struct, + ResolvedPointcutDefinition preResolvedPointcut) { + AnnotationGen around = getAnnotation(runtimeAnnotations, AjcMemberMaker.AROUND_ANNOTATION); + if (around != null) { + NameValuePair aroundAdvice = getAnnotationElement(around, VALUE); + if (aroundAdvice != null) { + // this/target/args binding + String argumentNames = getArgNamesValue(around); + if (argumentNames != null) { + struct.unparsedArgumentNames = argumentNames; + } + FormalBinding[] bindings = new org.aspectj.weaver.patterns.FormalBinding[0]; + try { + bindings = extractBindings(struct); + } catch (UnreadableDebugInfoException unreadableDebugInfoException) { + return false; + } + IScope binding = new BindingScope(struct.enclosingType, struct.context, bindings); + + // joinpoint, staticJoinpoint binding + int extraArgument = extractExtraArgument(struct.method); + + Pointcut pc = null; + if (preResolvedPointcut != null) { + pc = preResolvedPointcut.getPointcut(); + } else { + pc = parsePointcut(aroundAdvice.getValue().stringifyValue(), struct, false); + if (pc == null) { + return false;// parse error + } + pc.resolve(binding); + } + setIgnoreUnboundBindingNames(pc, bindings); + + ISourceLocation sl = struct.context.makeSourceLocation(struct.bMethod.getDeclarationLineNumber(), + struct.bMethod.getDeclarationOffset()); + struct.ajAttributes.add(new AjAttribute.AdviceAttribute(AdviceKind.Around, pc, extraArgument, sl.getOffset(), sl + .getOffset() + 1,// FIXME AVASM + struct.context)); + return true; + } + } + return false; + } + + /** + * Read @Pointcut and handle the resolving in a lazy way to deal with pointcut references + * + * @param runtimeAnnotations + * @param struct + * @return true if a pointcut was handled + */ + private static boolean handlePointcutAnnotation(RuntimeAnnos runtimeAnnotations, AjAttributeMethodStruct struct) { + AnnotationGen pointcut = getAnnotation(runtimeAnnotations, AjcMemberMaker.POINTCUT_ANNOTATION); + if (pointcut == null) { + return false; + } + NameValuePair pointcutExpr = getAnnotationElement(pointcut, VALUE); + + // semantic check: the method must return void, or be + // "public static boolean" for if() support + if (!(Type.VOID.equals(struct.method.getReturnType()) || (Type.BOOLEAN.equals(struct.method.getReturnType()) + && struct.method.isStatic() && struct.method.isPublic()))) { + reportWarning("Found @Pointcut on a method not returning 'void' or not 'public static boolean'", struct); + // no need to stop + } + + // semantic check: the method must not throw anything + if (struct.method.getExceptionTable() != null) { + reportWarning("Found @Pointcut on a method throwing exception", struct); + // no need to stop + } + + String argumentNames = getArgNamesValue(pointcut); + if (argumentNames != null) { + struct.unparsedArgumentNames = argumentNames; + } + // this/target/args binding + final IScope binding; + try { + if (struct.method.isAbstract()) { + binding = null; + } else { + binding = new BindingScope(struct.enclosingType, struct.context, extractBindings(struct)); + } + } catch (UnreadableDebugInfoException e) { + return false; + } + + UnresolvedType[] argumentTypes = new UnresolvedType[struct.method.getArgumentTypes().length]; + for (int i = 0; i < argumentTypes.length; i++) { + argumentTypes[i] = UnresolvedType.forSignature(struct.method.getArgumentTypes()[i].getSignature()); + } + + Pointcut pc = null; + if (struct.method.isAbstract()) { + if ((pointcutExpr != null && isNullOrEmpty(pointcutExpr.getValue().stringifyValue())) || pointcutExpr == null) { + // abstract pointcut + // leave pc = null + } else { + reportError("Found defined @Pointcut on an abstract method", struct); + return false;// stop + } + } else { + if (pointcutExpr == null || isNullOrEmpty(pointcutExpr.getValue().stringifyValue())) { + // the matches nothing pointcut (125475/125480) - perhaps not as + // cleanly supported as it could be. + } else { + // if (pointcutExpr != null) { + // use a LazyResolvedPointcutDefinition so that the pointcut is + // resolved lazily + // since for it to be resolved, we will need other pointcuts to + // be registered as well + pc = parsePointcut(pointcutExpr.getValue().stringifyValue(), struct, true); + if (pc == null) { + return false;// parse error + } + pc.setLocation(struct.context, -1, -1);// FIXME AVASM !! bMethod + // is null here.. + // } else { + // reportError("Found undefined @Pointcut on a non-abstract method", + // struct); + // return false; + // } + } + } + // do not resolve binding now but lazily + struct.ajAttributes.add(new AjAttribute.PointcutDeclarationAttribute(new LazyResolvedPointcutDefinition( + struct.enclosingType, struct.method.getModifiers(), struct.method.getName(), argumentTypes, UnresolvedType + .forSignature(struct.method.getReturnType().getSignature()), pc,// can + // be + // null + // for + // abstract + // pointcut + binding // can be null for abstract pointcut + ))); + return true; + } + + /** + * Read @DeclareError, @DeclareWarning + * + * @param runtimeAnnotations + * @param struct + * @return true if found + */ + private static boolean handleDeclareErrorOrWarningAnnotation(AsmManager model, RuntimeAnnos runtimeAnnotations, + AjAttributeFieldStruct struct) { + AnnotationGen error = getAnnotation(runtimeAnnotations, AjcMemberMaker.DECLAREERROR_ANNOTATION); + boolean hasError = false; + if (error != null) { + NameValuePair declareError = getAnnotationElement(error, VALUE); + if (declareError != null) { + if (!STRING_DESC.equals(struct.field.getSignature()) || struct.field.getConstantValue() == null) { + reportError("@DeclareError used on a non String constant field", struct); + return false; + } + Pointcut pc = parsePointcut(declareError.getValue().stringifyValue(), struct, false); + if (pc == null) { + hasError = false;// cannot parse pointcut + } else { + DeclareErrorOrWarning deow = new DeclareErrorOrWarning(true, pc, struct.field.getConstantValue().toString()); + setDeclareErrorOrWarningLocation(model, deow, struct); + struct.ajAttributes.add(new AjAttribute.DeclareAttribute(deow)); + hasError = true; + } + } + } + AnnotationGen warning = getAnnotation(runtimeAnnotations, AjcMemberMaker.DECLAREWARNING_ANNOTATION); + boolean hasWarning = false; + if (warning != null) { + NameValuePair declareWarning = getAnnotationElement(warning, VALUE); + if (declareWarning != null) { + if (!STRING_DESC.equals(struct.field.getSignature()) || struct.field.getConstantValue() == null) { + reportError("@DeclareWarning used on a non String constant field", struct); + return false; + } + Pointcut pc = parsePointcut(declareWarning.getValue().stringifyValue(), struct, false); + if (pc == null) { + hasWarning = false;// cannot parse pointcut + } else { + DeclareErrorOrWarning deow = new DeclareErrorOrWarning(false, pc, struct.field.getConstantValue().toString()); + setDeclareErrorOrWarningLocation(model, deow, struct); + struct.ajAttributes.add(new AjAttribute.DeclareAttribute(deow)); + return hasWarning = true; + } + } + } + return hasError || hasWarning; + } + + /** + * Sets the location for the declare error / warning using the corresponding IProgramElement in the structure model. This will + * only fix bug 120356 if compiled with -emacssym, however, it does mean that the cross references view in AJDT will show the + * correct information. + * + * Other possibilities for fix: 1. using the information in ajcDeclareSoft (if this is set correctly) which will fix the problem + * if compiled with ajc but not if compiled with javac. 2. creating an AjAttribute called FieldDeclarationLineNumberAttribute + * (much like MethodDeclarationLineNumberAttribute) which we can ask for the offset. This will again only fix bug 120356 when + * compiled with ajc. + * + * @param deow + * @param struct + */ + private static void setDeclareErrorOrWarningLocation(AsmManager model, DeclareErrorOrWarning deow, AjAttributeFieldStruct struct) { + IHierarchy top = (model == null ? null : model.getHierarchy()); + if (top != null && top.getRoot() != null) { + IProgramElement ipe = top.findElementForLabel(top.getRoot(), IProgramElement.Kind.FIELD, struct.field.getName()); + if (ipe != null && ipe.getSourceLocation() != null) { + ISourceLocation sourceLocation = ipe.getSourceLocation(); + int start = sourceLocation.getOffset(); + int end = start + struct.field.getName().length(); + deow.setLocation(struct.context, start, end); + return; + } + } + deow.setLocation(struct.context, -1, -1); + } + + /** + * Returns a readable representation of a method. Method.toString() is not suitable. + * + * @param method + * @return a readable representation of a method + */ + private static String methodToString(Method method) { + StringBuffer sb = new StringBuffer(); + sb.append(method.getName()); + sb.append(method.getSignature()); + return sb.toString(); + } + + /** + * Build the bindings for a given method (pointcut / advice) + * + * @param struct + * @return null if no debug info is available + */ + private static FormalBinding[] extractBindings(AjAttributeMethodStruct struct) throws UnreadableDebugInfoException { + Method method = struct.method; + String[] argumentNames = struct.getArgumentNames(); + + // assert debug info was here + if (argumentNames.length != method.getArgumentTypes().length) { + reportError( + "Cannot read debug info for @Aspect to handle formal binding in pointcuts (please compile with 'javac -g' or '' in Ant)", + struct); + throw new UnreadableDebugInfoException(); + } + + List bindings = new ArrayList(); + for (int i = 0; i < argumentNames.length; i++) { + String argumentName = argumentNames[i]; + UnresolvedType argumentType = UnresolvedType.forSignature(method.getArgumentTypes()[i].getSignature()); + + // do not bind JoinPoint / StaticJoinPoint / + // EnclosingStaticJoinPoint + // TODO solve me : this means that the JP/SJP/ESJP cannot appear as + // binding + // f.e. when applying advice on advice etc + if ((AjcMemberMaker.TYPEX_JOINPOINT.equals(argumentType) + || AjcMemberMaker.TYPEX_PROCEEDINGJOINPOINT.equals(argumentType) + || AjcMemberMaker.TYPEX_STATICJOINPOINT.equals(argumentType) + || AjcMemberMaker.TYPEX_ENCLOSINGSTATICJOINPOINT.equals(argumentType) || AjcMemberMaker.AROUND_CLOSURE_TYPE + .equals(argumentType))) { + // continue;// skip + bindings.add(new FormalBinding.ImplicitFormalBinding(argumentType, argumentName, i)); + } else { + bindings.add(new FormalBinding(argumentType, argumentName, i)); + } + } + + return bindings.toArray(new FormalBinding[] {}); + } + + // FIXME alex deal with exclude index + private static FormalBinding[] extractBindings(AjAttributeMethodStruct struct, String excludeFormal) + throws UnreadableDebugInfoException { + FormalBinding[] bindings = extractBindings(struct); + // int excludeIndex = -1; + for (int i = 0; i < bindings.length; i++) { + FormalBinding binding = bindings[i]; + if (binding.getName().equals(excludeFormal)) { + // excludeIndex = i; + bindings[i] = new FormalBinding.ImplicitFormalBinding(binding.getType(), binding.getName(), binding.getIndex()); + break; + } + } + return bindings; + // + // if (excludeIndex >= 0) { + // FormalBinding[] bindingsFiltered = new + // FormalBinding[bindings.length-1]; + // int k = 0; + // for (int i = 0; i < bindings.length; i++) { + // if (i == excludeIndex) { + // ; + // } else { + // bindingsFiltered[k] = new FormalBinding(bindings[i].getType(), + // bindings[i].getName(), k); + // k++; + // } + // } + // return bindingsFiltered; + // } else { + // return bindings; + // } + } + + /** + * Compute the flag for the xxxJoinPoint extra argument + * + * @param method + * @return extra arg flag + */ + private static int extractExtraArgument(Method method) { + Type[] methodArgs = method.getArgumentTypes(); + String[] sigs = new String[methodArgs.length]; + for (int i = 0; i < methodArgs.length; i++) { + sigs[i] = methodArgs[i].getSignature(); + } + return extractExtraArgument(sigs); + } + + /** + * Compute the flag for the xxxJoinPoint extra argument + * + * @param argumentSignatures + * @return extra arg flag + */ + public static int extractExtraArgument(String[] argumentSignatures) { + int extraArgument = 0; + for (int i = 0; i < argumentSignatures.length; i++) { + if (AjcMemberMaker.TYPEX_JOINPOINT.getSignature().equals(argumentSignatures[i])) { + extraArgument |= Advice.ThisJoinPoint; + } else if (AjcMemberMaker.TYPEX_PROCEEDINGJOINPOINT.getSignature().equals(argumentSignatures[i])) { + extraArgument |= Advice.ThisJoinPoint; + } else if (AjcMemberMaker.TYPEX_STATICJOINPOINT.getSignature().equals(argumentSignatures[i])) { + extraArgument |= Advice.ThisJoinPointStaticPart; + } else if (AjcMemberMaker.TYPEX_ENCLOSINGSTATICJOINPOINT.getSignature().equals(argumentSignatures[i])) { + extraArgument |= Advice.ThisEnclosingJoinPointStaticPart; + } + } + return extraArgument; + } + + /** + * Returns the runtime (RV/RIV) annotation of type annotationType or null if no such annotation + * + * @param rvs + * @param annotationType + * @return annotation + */ + private static AnnotationGen getAnnotation(RuntimeAnnos rvs, UnresolvedType annotationType) { + final String annotationTypeName = annotationType.getName(); + for (AnnotationGen rv : rvs.getAnnotations()) { + if (annotationTypeName.equals(rv.getTypeName())) { + return rv; + } + } + return null; + } + + /** + * Returns the value of a given element of an annotation or null if not found Caution: Does not handles default value. + * + * @param annotation + * @param elementName + * @return annotation NVP + */ + private static NameValuePair getAnnotationElement(AnnotationGen annotation, String elementName) { + for (NameValuePair element : annotation.getValues()) { + if (elementName.equals(element.getNameString())) { + return element; + } + } + return null; + } + + /** + * Return the argNames set for an annotation or null if it is not specified. + */ + private static String getArgNamesValue(AnnotationGen anno) { + List elements = anno.getValues(); + for (NameValuePair element : elements) { + if (ARGNAMES.equals(element.getNameString())) { + return element.getValue().stringifyValue(); + } + } + return null; + } + + private static String lastbit(String fqname) { + int i = fqname.lastIndexOf("."); + if (i == -1) { + return fqname; + } else { + return fqname.substring(i + 1); + } + } + + /** + * Extract the method argument names. First we try the debug info attached to the method (the LocalVariableTable) - if we cannot + * find that we look to use the argNames value that may have been supplied on the associated annotation. If that fails we just + * don't know and return an empty string. + * + * @param method + * @param argNamesFromAnnotation + * @param methodStruct + * @return method argument names + */ + private static String[] getMethodArgumentNames(Method method, String argNamesFromAnnotation, + AjAttributeMethodStruct methodStruct) { + if (method.getArgumentTypes().length == 0) { + return EMPTY_STRINGS; + } + + final int startAtStackIndex = method.isStatic() ? 0 : 1; + final List arguments = new ArrayList(); + LocalVariableTable lt = method.getLocalVariableTable(); + if (lt != null) { + LocalVariable[] lvt = lt.getLocalVariableTable(); + for (int j = 0; j < lvt.length; j++) { + LocalVariable localVariable = lvt[j]; + if (localVariable != null) { // pr348488 + if (localVariable.getStartPC() == 0) { + if (localVariable.getIndex() >= startAtStackIndex) { + arguments.add(new MethodArgument(localVariable.getName(), localVariable.getIndex())); + } + } + } else { + String typename = (methodStruct.enclosingType != null ? methodStruct.enclosingType.getName() : ""); + System.err.println("AspectJ: 348488 debug: unusual local variable table for method " + typename + "." + + method.getName()); + } + } + if (arguments.size() == 0) { + // The local variable table is causing us trouble, try the annotation value + // See 539121 for a jacoco variant of the cobertura issue below + if (argNamesFromAnnotation != null) { + String[] argNames = extractArgNamesFromAnnotationValue(method, argNamesFromAnnotation, methodStruct); + if (argNames.length != 0) { + return argNames; + } + } + // could be cobertura code where some extra bytecode has been stuffed in at the start of the method + // but the local variable table hasn't been repaired - for example: + // LocalVariable(start_pc = 6, length = 40, index = 0:com.example.ExampleAspect this) + // LocalVariable(start_pc = 6, length = 40, index = 1:org.aspectj.lang.ProceedingJoinPoint pjp) + // LocalVariable(start_pc = 6, length = 40, index = 2:int __cobertura__line__number__) + // LocalVariable(start_pc = 6, length = 40, index = 3:int __cobertura__branch__number__) + LocalVariable localVariable = lvt[0]; + if (localVariable != null) { // pr348488 + if (localVariable.getStartPC() != 0) { + // looks suspicious so let's use this information + for (int j = 0; j < lvt.length && arguments.size() < method.getArgumentTypes().length; j++) { + localVariable = lvt[j]; + if (localVariable.getIndex() >= startAtStackIndex) { + arguments.add(new MethodArgument(localVariable.getName(), localVariable.getIndex())); + } + } + } + } + } + } else { + if (argNamesFromAnnotation != null) { + String[] argNames = extractArgNamesFromAnnotationValue(method, argNamesFromAnnotation, methodStruct); + if (argNames != null) { + return argNames; + } + } + } + + if (arguments.size() != method.getArgumentTypes().length) { + return EMPTY_STRINGS; + } + + // sort by index + Collections.sort(arguments, new Comparator() { + public int compare(MethodArgument mo, MethodArgument mo1) { + if (mo.indexOnStack == mo1.indexOnStack) { + return 0; + } else if (mo.indexOnStack > mo1.indexOnStack) { + return 1; + } else { + return -1; + } + } + }); + String[] argumentNames = new String[arguments.size()]; + int i = 0; + for (MethodArgument methodArgument : arguments) { + argumentNames[i++] = methodArgument.name; + } + return argumentNames; + } + + private static String[] extractArgNamesFromAnnotationValue(Method method, String argNamesFromAnnotation, + AjAttributeMethodStruct methodStruct) { + StringTokenizer st = new StringTokenizer(argNamesFromAnnotation, " ,"); + List args = new ArrayList(); + while (st.hasMoreTokens()) { + args.add(st.nextToken()); + } + if (args.size() != method.getArgumentTypes().length) { + StringBuffer shortString = new StringBuffer().append(lastbit(method.getReturnType().toString())).append(" ") + .append(method.getName()); + if (method.getArgumentTypes().length > 0) { + shortString.append("("); + for (int i = 0; i < method.getArgumentTypes().length; i++) { + shortString.append(lastbit(method.getArgumentTypes()[i].toString())); + if ((i + 1) < method.getArgumentTypes().length) { + shortString.append(","); + } + + } + shortString.append(")"); + } + reportError("argNames annotation value does not specify the right number of argument names for the method '" + + shortString.toString() + "'", methodStruct); + return EMPTY_STRINGS; + } + return args.toArray(new String[] {}); + } + + /** + * A method argument, used for sorting by indexOnStack (ie order in signature) + * + * @author Alexandre Vasseur + */ + private static class MethodArgument { + String name; + int indexOnStack; + + public MethodArgument(String name, int indexOnStack) { + this.name = name; + this.indexOnStack = indexOnStack; + } + } + + /** + * LazyResolvedPointcutDefinition lazyly resolve the pointcut so that we have time to register all pointcut referenced before + * pointcut resolution happens + * + * @author Alexandre Vasseur + */ + public static class LazyResolvedPointcutDefinition extends ResolvedPointcutDefinition { + private final Pointcut m_pointcutUnresolved; // null for abstract + // pointcut + private final IScope m_binding; + + private Pointcut m_lazyPointcut = null; + + public LazyResolvedPointcutDefinition(UnresolvedType declaringType, int modifiers, String name, + UnresolvedType[] parameterTypes, UnresolvedType returnType, Pointcut pointcut, IScope binding) { + super(declaringType, modifiers, name, parameterTypes, returnType, Pointcut.makeMatchesNothing(Pointcut.RESOLVED)); + m_pointcutUnresolved = pointcut; + m_binding = binding; + } + + @Override + public Pointcut getPointcut() { + if (m_lazyPointcut == null && m_pointcutUnresolved == null) { + m_lazyPointcut = Pointcut.makeMatchesNothing(Pointcut.CONCRETE); + } + if (m_lazyPointcut == null && m_pointcutUnresolved != null) { + m_lazyPointcut = m_pointcutUnresolved.resolve(m_binding); + m_lazyPointcut.copyLocationFrom(m_pointcutUnresolved); + } + return m_lazyPointcut; + } + } + + /** + * Helper to test empty strings + * + * @param s + * @return true if empty or null + */ + private static boolean isNullOrEmpty(String s) { + return (s == null || s.length() <= 0); + } + + /** + * Set the pointcut bindings for which to ignore unbound issues, so that we can implicitly bind xxxJoinPoint for @AJ advices + * + * @param pointcut + * @param bindings + */ + private static void setIgnoreUnboundBindingNames(Pointcut pointcut, FormalBinding[] bindings) { + // register ImplicitBindings as to be ignored since unbound + // TODO is it likely to fail in a bad way if f.e. this(jp) etc ? + List ignores = new ArrayList(); + for (int i = 0; i < bindings.length; i++) { + FormalBinding formalBinding = bindings[i]; + if (formalBinding instanceof FormalBinding.ImplicitFormalBinding) { + ignores.add(formalBinding.getName()); + } + } + pointcut.m_ignoreUnboundBindingForNames = ignores.toArray(new String[ignores.size()]); + } + + /** + * A check exception when we cannot read debug info (needed for formal binding) + */ + private static class UnreadableDebugInfoException extends Exception { + } + + /** + * Report an error + * + * @param message + * @param location + */ + private static void reportError(String message, AjAttributeStruct location) { + if (!location.handler.isIgnoring(IMessage.ERROR)) { + location.handler.handleMessage(new Message(message, location.enclosingType.getSourceLocation(), true)); + } + } + + // private static void reportError(String message, IMessageHandler handler, ISourceLocation sourceLocation) { + // if (!handler.isIgnoring(IMessage.ERROR)) { + // handler.handleMessage(new Message(message, sourceLocation, true)); + // } + // } + + /** + * Report a warning + * + * @param message + * @param location + */ + private static void reportWarning(String message, AjAttributeStruct location) { + if (!location.handler.isIgnoring(IMessage.WARNING)) { + location.handler.handleMessage(new Message(message, location.enclosingType.getSourceLocation(), false)); + } + } + + /** + * Parse the given pointcut, return null on failure and issue an error + * + * @param pointcutString + * @param struct + * @param allowIf + * @return pointcut, unresolved + */ + private static Pointcut parsePointcut(String pointcutString, AjAttributeStruct struct, boolean allowIf) { + try { + PatternParser parser = new PatternParser(pointcutString, struct.context); + Pointcut pointcut = parser.parsePointcut(); + parser.checkEof(); + pointcut.check(null, struct.enclosingType.getWorld()); + if (!allowIf && pointcutString.indexOf("if()") >= 0 && hasIf(pointcut)) { + reportError("if() pointcut is not allowed at this pointcut location '" + pointcutString + "'", struct); + return null; + } + pointcut.setLocation(struct.context, -1, -1);// FIXME -1,-1 is not + // good enough + return pointcut; + } catch (ParserException e) { + reportError("Invalid pointcut '" + pointcutString + "': " + e.toString() + + (e.getLocation() == null ? "" : " at position " + e.getLocation().getStart()), struct); + return null; + } + } + + private static boolean hasIf(Pointcut pointcut) { + IfFinder visitor = new IfFinder(); + pointcut.accept(visitor, null); + return visitor.hasIf; + } + + /** + * Parse the given type pattern, return null on failure and issue an error + * + * @param patternString + * @param location + * @return type pattern + */ + private static TypePattern parseTypePattern(String patternString, AjAttributeStruct location) { + try { + TypePattern typePattern = new PatternParser(patternString).parseTypePattern(); + typePattern.setLocation(location.context, -1, -1);// FIXME -1,-1 is + // not good + // enough + return typePattern; + } catch (ParserException e) { + reportError("Invalid type pattern'" + patternString + "' : " + e.getLocation(), location); + return null; + } + } + + static class ThrownFormalNotDeclaredInAdviceSignatureException extends Exception { + + private final String formalName; + + public ThrownFormalNotDeclaredInAdviceSignatureException(String formalName) { + this.formalName = formalName; + } + + public String getFormalName() { + return formalName; + } + } + + static class ReturningFormalNotDeclaredInAdviceSignatureException extends Exception { + + private final String formalName; + + public ReturningFormalNotDeclaredInAdviceSignatureException(String formalName) { + this.formalName = formalName; + } + + public String getFormalName() { + return formalName; + } + } +} \ No newline at end of file diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/BcelAccessForInlineMunger.java b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelAccessForInlineMunger.java new file mode 100644 index 000000000..b81f7ffb1 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelAccessForInlineMunger.java @@ -0,0 +1,386 @@ +/******************************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alexandre Vasseur initial implementation + *******************************************************************************/ +package org.aspectj.weaver.bcel; + +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.aspectj.apache.bcel.Constants; +import org.aspectj.apache.bcel.classfile.ConstantPool; +import org.aspectj.apache.bcel.generic.FieldInstruction; +import org.aspectj.apache.bcel.generic.Instruction; +import org.aspectj.apache.bcel.generic.InstructionConstants; +import org.aspectj.apache.bcel.generic.InstructionFactory; +import org.aspectj.apache.bcel.generic.InstructionHandle; +import org.aspectj.apache.bcel.generic.InstructionList; +import org.aspectj.apache.bcel.generic.InvokeDynamic; +import org.aspectj.apache.bcel.generic.InvokeInstruction; +import org.aspectj.apache.bcel.generic.Type; +import org.aspectj.weaver.AjAttribute; +import org.aspectj.weaver.AjcMemberMaker; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.NameMangler; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.UnresolvedType; + +/** + * Looks for all access to method or field that are not public within the body of the around advices and replace the invocations to + * a wrapper call so that the around advice can further be inlined. + *

+ * This munger is used for @AJ aspects for which inlining wrapper is not done at compile time. + *

+ * Specific state and logic is kept in the munger ala ITD so that call/get/set pointcuts can still be matched on the wrapped member + * thanks to the EffectiveSignature attribute. + * + * @author Alexandre Vasseur + * @author Andy Clement + */ +public class BcelAccessForInlineMunger extends BcelTypeMunger { + + private Map inlineAccessors; + + private LazyClassGen aspectGen; + + /** + * The wrapper methods representing any created inlineAccessors + */ + private Set inlineAccessorMethodGens; + + public BcelAccessForInlineMunger(ResolvedType aspectType) { + super(null, aspectType); + if (aspectType.getWorld().isXnoInline()) { + throw new Error("This should not happen"); + } + } + + @Override + public boolean munge(BcelClassWeaver weaver) { + aspectGen = weaver.getLazyClassGen(); + inlineAccessors = new HashMap(0); + inlineAccessorMethodGens = new HashSet(); + + // look for all @Around advices + for (LazyMethodGen methodGen : aspectGen.getMethodGens()) { + if (methodGen.hasAnnotation(UnresolvedType.forName("org/aspectj/lang/annotation/Around"))) { + openAroundAdvice(methodGen); + } + } + + // add the accessors + for (LazyMethodGen lazyMethodGen : inlineAccessorMethodGens) { + aspectGen.addMethodGen(lazyMethodGen); + } + + // flush some + inlineAccessorMethodGens = null; + // we keep m_inlineAccessorsResolvedMembers for shadow matching + + return true; + } + + /** + * Looks in the wrapper we have added so that we can find their effective signature if needed + */ + @Override + public ResolvedMember getMatchingSyntheticMember(Member member) { + ResolvedMember rm = inlineAccessors.get(member.getName());// + member.getSignature()); +// System.err.println("lookup for " + member.getName() + ":" + member.getSignature() + " = " +// + (rm == null ? "" : rm.getName())); + return rm; + } + + @Override + public ResolvedMember getSignature() { + return null; + } + + /** + * Match only the aspect for which we act + */ + @Override + public boolean matches(ResolvedType onType) { + return aspectType.equals(onType); + } + + /** + * Prepare the around advice, flag it as cannot be inlined if it can't be + */ + private void openAroundAdvice(LazyMethodGen aroundAdvice) { + InstructionHandle curr = aroundAdvice.getBody().getStart(); + InstructionHandle end = aroundAdvice.getBody().getEnd(); + ConstantPool cpg = aroundAdvice.getEnclosingClass().getConstantPool(); + InstructionFactory factory = aroundAdvice.getEnclosingClass().getFactory(); + + boolean realizedCannotInline = false; + while (curr != end) { + if (realizedCannotInline) { + // we know we cannot inline this advice so no need for futher handling + break; + } + InstructionHandle next = curr.getNext(); + Instruction inst = curr.getInstruction(); + + // open-up method call + if ((inst instanceof InvokeInstruction)) { + InvokeInstruction invoke = (InvokeInstruction) inst; + if (invoke instanceof InvokeDynamic) { + realizedCannotInline = true; + break; + } + ResolvedType callee = aspectGen.getWorld().resolve(UnresolvedType.forName(invoke.getClassName(cpg))); + + // look in the whole method list and not just declared for super calls and alike + List methods = callee.getMethodsWithoutIterator(false, true, false); + for (ResolvedMember resolvedMember : methods) { + if (invoke.getName(cpg).equals(resolvedMember.getName()) + && invoke.getSignature(cpg).equals(resolvedMember.getSignature()) && !resolvedMember.isPublic()) { + if ("".equals(invoke.getName(cpg))) { + // skipping open up for private constructor + // can occur when aspect new a private inner type + // too complex to handle new + dup + .. + invokespecial here. + aroundAdvice.setCanInline(false); + realizedCannotInline = true; + } else { + // specific handling for super.foo() calls, where foo is non public + ResolvedType memberType = aspectGen.getWorld().resolve(resolvedMember.getDeclaringType()); + if (!aspectType.equals(memberType) && memberType.isAssignableFrom(aspectType)) { + // old test was... + // if (aspectType.getSuperclass() != null + // && aspectType.getSuperclass().getName().equals(resolvedMember.getDeclaringType().getName())) { + ResolvedMember accessor = createOrGetInlineAccessorForSuperDispatch(resolvedMember); + InvokeInstruction newInst = factory.createInvoke(aspectType.getName(), accessor.getName(), + BcelWorld.makeBcelType(accessor.getReturnType()), + BcelWorld.makeBcelTypes(accessor.getParameterTypes()), Constants.INVOKEVIRTUAL); + curr.setInstruction(newInst); + } else { + ResolvedMember accessor = createOrGetInlineAccessorForMethod(resolvedMember); + InvokeInstruction newInst = factory.createInvoke(aspectType.getName(), accessor.getName(), + BcelWorld.makeBcelType(accessor.getReturnType()), + BcelWorld.makeBcelTypes(accessor.getParameterTypes()), Constants.INVOKESTATIC); + curr.setInstruction(newInst); + } + } + + break;// ok we found a matching callee member and swapped the instruction with the accessor + } + } + } else if (inst instanceof FieldInstruction) { + FieldInstruction invoke = (FieldInstruction) inst; + ResolvedType callee = aspectGen.getWorld().resolve(UnresolvedType.forName(invoke.getClassName(cpg))); + for (int i = 0; i < callee.getDeclaredJavaFields().length; i++) { + ResolvedMember resolvedMember = callee.getDeclaredJavaFields()[i]; + if (invoke.getName(cpg).equals(resolvedMember.getName()) + && invoke.getSignature(cpg).equals(resolvedMember.getSignature()) && !resolvedMember.isPublic()) { + final ResolvedMember accessor; + if ((inst.opcode == Constants.GETFIELD) || (inst.opcode == Constants.GETSTATIC)) { + accessor = createOrGetInlineAccessorForFieldGet(resolvedMember); + } else { + accessor = createOrGetInlineAccessorForFieldSet(resolvedMember); + } + InvokeInstruction newInst = factory.createInvoke(aspectType.getName(), accessor.getName(), + BcelWorld.makeBcelType(accessor.getReturnType()), + BcelWorld.makeBcelTypes(accessor.getParameterTypes()), Constants.INVOKESTATIC); + curr.setInstruction(newInst); + + break;// ok we found a matching callee member and swapped the instruction with the accessor + } + } + } + + curr = next; + } + + // no reason for not inlining this advice + // since it is used for @AJ advice that cannot be inlined by defauilt + // make sure we set inline to true since we have done this analysis + if (!realizedCannotInline) { + aroundAdvice.setCanInline(true); + } + } + + /** + * Find (or add if not yet created) an inline wrapper for a non public method call + */ + private ResolvedMember createOrGetInlineAccessorForMethod(ResolvedMember resolvedMember) { + String accessorName = NameMangler.inlineAccessMethodForMethod(resolvedMember.getName(), resolvedMember.getDeclaringType(), + aspectType); + String key = accessorName;// new StringBuilder(accessorName).append(resolvedMember.getSignature()).toString(); + ResolvedMember inlineAccessor = inlineAccessors.get(key); +// System.err.println(key + " accessor=" + inlineAccessor); + if (inlineAccessor == null) { + // add static method to aspect + inlineAccessor = AjcMemberMaker.inlineAccessMethodForMethod(aspectType, resolvedMember); + + // add new accessor method to aspect bytecode + InstructionFactory factory = aspectGen.getFactory(); + LazyMethodGen method = makeMethodGen(aspectGen, inlineAccessor); + method.makeSynthetic(); + List methodAttributes = new ArrayList(); + methodAttributes.add(new AjAttribute.AjSynthetic()); + methodAttributes.add(new AjAttribute.EffectiveSignatureAttribute(resolvedMember, Shadow.MethodCall, false)); + method.addAttribute(Utility.bcelAttribute(methodAttributes.get(0), aspectGen.getConstantPool())); + // flag the effective signature, so that we can deobfuscate the signature to apply method call pointcut + method.addAttribute(Utility.bcelAttribute(methodAttributes.get(1), aspectGen.getConstantPool())); + + inlineAccessorMethodGens.add(method); + + InstructionList il = method.getBody(); + int register = 0; + for (int i = 0, max = inlineAccessor.getParameterTypes().length; i < max; i++) { + UnresolvedType ptype = inlineAccessor.getParameterTypes()[i]; + Type type = BcelWorld.makeBcelType(ptype); + il.append(InstructionFactory.createLoad(type, register)); + register += type.getSize(); + } + il.append(Utility.createInvoke(factory, Modifier.isStatic(resolvedMember.getModifiers()) ? Constants.INVOKESTATIC + : Constants.INVOKEVIRTUAL, resolvedMember)); + il.append(InstructionFactory.createReturn(BcelWorld.makeBcelType(inlineAccessor.getReturnType()))); + + inlineAccessors.put(key, new BcelMethod(aspectGen.getBcelObjectType(), method.getMethod(), methodAttributes)); + } + return inlineAccessor; + } + + /** + * Add an inline wrapper for a non public super.method call + */ + private ResolvedMember createOrGetInlineAccessorForSuperDispatch(ResolvedMember resolvedMember) { + String accessor = NameMangler.superDispatchMethod(aspectType, resolvedMember.getName()); + + String key = accessor; + ResolvedMember inlineAccessor = inlineAccessors.get(key); + + if (inlineAccessor == null) { + // add super accessor method to class: + inlineAccessor = AjcMemberMaker.superAccessMethod(aspectType, resolvedMember); + + // add new accessor method to aspect bytecode + InstructionFactory factory = aspectGen.getFactory(); + LazyMethodGen method = makeMethodGen(aspectGen, inlineAccessor); + // flag it synthetic, AjSynthetic + method.makeSynthetic(); + List methodAttributes = new ArrayList(); + methodAttributes.add(new AjAttribute.AjSynthetic()); + methodAttributes.add(new AjAttribute.EffectiveSignatureAttribute(resolvedMember, Shadow.MethodCall, false)); + method.addAttribute(Utility.bcelAttribute(methodAttributes.get(0), aspectGen.getConstantPool())); + // flag the effective signature, so that we can deobfuscate the signature to apply method call pointcut + method.addAttribute(Utility.bcelAttribute(methodAttributes.get(1), aspectGen.getConstantPool())); + + inlineAccessorMethodGens.add(method); + + InstructionList il = method.getBody(); + il.append(InstructionConstants.ALOAD_0); + int register = 1; + for (int i = 0; i < inlineAccessor.getParameterTypes().length; i++) { + UnresolvedType typeX = inlineAccessor.getParameterTypes()[i]; + Type type = BcelWorld.makeBcelType(typeX); + il.append(InstructionFactory.createLoad(type, register)); + register += type.getSize(); + } + il.append(Utility.createInvoke(factory, Constants.INVOKESPECIAL, resolvedMember)); + il.append(InstructionFactory.createReturn(BcelWorld.makeBcelType(inlineAccessor.getReturnType()))); + + inlineAccessors.put(key, new BcelMethod(aspectGen.getBcelObjectType(), method.getMethod(), methodAttributes)); + } + return inlineAccessor; + } + + /** + * Add an inline wrapper for a non public field get + */ + private ResolvedMember createOrGetInlineAccessorForFieldGet(ResolvedMember resolvedMember) { + String accessor = NameMangler.inlineAccessMethodForFieldGet(resolvedMember.getName(), resolvedMember.getDeclaringType(), + aspectType); + String key = accessor; + ResolvedMember inlineAccessor = inlineAccessors.get(key); + + if (inlineAccessor == null) { + // add static method to aspect + inlineAccessor = AjcMemberMaker.inlineAccessMethodForFieldGet(aspectType, resolvedMember); + + // add new accessor method to aspect bytecode + InstructionFactory factory = aspectGen.getFactory(); + LazyMethodGen method = makeMethodGen(aspectGen, inlineAccessor); + // flag it synthetic, AjSynthetic + method.makeSynthetic(); + List methodAttributes = new ArrayList(); + methodAttributes.add(new AjAttribute.AjSynthetic()); + methodAttributes.add(new AjAttribute.EffectiveSignatureAttribute(resolvedMember, Shadow.FieldGet, false)); + // flag the effective signature, so that we can deobfuscate the signature to apply method call pointcut + method.addAttribute(Utility.bcelAttribute(methodAttributes.get(0), aspectGen.getConstantPool())); + method.addAttribute(Utility.bcelAttribute(methodAttributes.get(1), aspectGen.getConstantPool())); + + inlineAccessorMethodGens.add(method); + + InstructionList il = method.getBody(); + if (Modifier.isStatic(resolvedMember.getModifiers())) { + // field accessed is static so no "this" as accessor sole parameter + } else { + il.append(InstructionConstants.ALOAD_0); + } + il.append(Utility.createGet(factory, resolvedMember)); + il.append(InstructionFactory.createReturn(BcelWorld.makeBcelType(inlineAccessor.getReturnType()))); + + inlineAccessors.put(key, new BcelMethod(aspectGen.getBcelObjectType(), method.getMethod(), methodAttributes)); + } + return inlineAccessor; + } + + /** + * Add an inline wrapper for a non public field set + */ + private ResolvedMember createOrGetInlineAccessorForFieldSet(ResolvedMember resolvedMember) { + String accessor = NameMangler.inlineAccessMethodForFieldSet(resolvedMember.getName(), resolvedMember.getDeclaringType(), + aspectType); + String key = accessor; + ResolvedMember inlineAccessor = inlineAccessors.get(key); + + if (inlineAccessor == null) { + // add static method to aspect + inlineAccessor = AjcMemberMaker.inlineAccessMethodForFieldSet(aspectType, resolvedMember); + + // add new accessor method to aspect bytecode + InstructionFactory factory = aspectGen.getFactory(); + LazyMethodGen method = makeMethodGen(aspectGen, inlineAccessor); + // flag it synthetic, AjSynthetic + method.makeSynthetic(); + List methodAttributes = new ArrayList(); + methodAttributes.add(new AjAttribute.AjSynthetic()); + methodAttributes.add(new AjAttribute.EffectiveSignatureAttribute(resolvedMember, Shadow.FieldSet, false)); + method.addAttribute(Utility.bcelAttribute(methodAttributes.get(0), aspectGen.getConstantPool())); + // flag the effective signature, so that we can deobfuscate the signature to apply method call pointcut + method.addAttribute(Utility.bcelAttribute(methodAttributes.get(1), aspectGen.getConstantPool())); + + inlineAccessorMethodGens.add(method); + + InstructionList il = method.getBody(); + if (Modifier.isStatic(resolvedMember.getModifiers())) { + // field accessed is static so sole parameter is field value to be set + il.append(InstructionFactory.createLoad(BcelWorld.makeBcelType(resolvedMember.getReturnType()), 0)); + } else { + il.append(InstructionConstants.ALOAD_0); + il.append(InstructionFactory.createLoad(BcelWorld.makeBcelType(resolvedMember.getReturnType()), 1)); + } + il.append(Utility.createSet(factory, resolvedMember)); + il.append(InstructionConstants.RETURN); + inlineAccessors.put(key, new BcelMethod(aspectGen.getBcelObjectType(), method.getMethod(), methodAttributes)); + } + return inlineAccessor; + } +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/BcelAdvice.java b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelAdvice.java new file mode 100644 index 000000000..d22b17d12 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelAdvice.java @@ -0,0 +1,801 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * Alexandre Vasseur support for @AJ aspects + * ******************************************************************/ + +package org.aspectj.weaver.bcel; + +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +import org.aspectj.apache.bcel.Constants; +import org.aspectj.apache.bcel.classfile.LocalVariable; +import org.aspectj.apache.bcel.classfile.LocalVariableTable; +import org.aspectj.apache.bcel.generic.InstructionConstants; +import org.aspectj.apache.bcel.generic.InstructionFactory; +import org.aspectj.apache.bcel.generic.InstructionHandle; +import org.aspectj.apache.bcel.generic.InstructionList; +import org.aspectj.apache.bcel.generic.LineNumberTag; +import org.aspectj.apache.bcel.generic.LocalVariableTag; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.Message; +import org.aspectj.weaver.Advice; +import org.aspectj.weaver.AdviceKind; +import org.aspectj.weaver.AjAttribute; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.IEclipseSourceContext; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.Lint; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.ReferenceType; +import org.aspectj.weaver.ReferenceTypeDelegate; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedMemberImpl; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.ShadowMunger; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Test; +import org.aspectj.weaver.patterns.ExactTypePattern; +import org.aspectj.weaver.patterns.ExposedState; +import org.aspectj.weaver.patterns.PerClause; +import org.aspectj.weaver.patterns.Pointcut; + +/** + * Advice implemented for BCEL + * + * @author Erik Hilsdale + * @author Jim Hugunin + * @author Andy Clement + */ +class BcelAdvice extends Advice { + + /** + * If a match is not entirely statically determinable, this captures the runtime test that must succeed in order for the advice + * to run. + */ + private Test runtimeTest; + private ExposedState exposedState; + private int containsInvokedynamic = 0;// 0 = dontknow, 1=no, 2=yes + + public BcelAdvice(AjAttribute.AdviceAttribute attribute, Pointcut pointcut, Member adviceSignature, ResolvedType concreteAspect) { + super(attribute, pointcut, simplify(attribute.getKind(), adviceSignature)); + this.concreteAspect = concreteAspect; + } + + /** + * A heavyweight BcelMethod object is only required for around advice that will be inlined. For other kinds of advice it is + * possible to save some space. + */ + private static Member simplify(AdviceKind kind, Member adviceSignature) { + if (adviceSignature != null) { + UnresolvedType adviceDeclaringType = adviceSignature.getDeclaringType(); + // if it isnt around advice or it is but inlining is turned off then shrink it to a ResolvedMemberImpl + if (kind != AdviceKind.Around + || ((adviceDeclaringType instanceof ResolvedType) && ((ResolvedType) adviceDeclaringType).getWorld() + .isXnoInline())) { + if (adviceSignature instanceof BcelMethod) { + BcelMethod bm = (BcelMethod) adviceSignature; + if (bm.getMethod() != null && bm.getMethod().getAnnotations() != null) { + return adviceSignature; + } + ResolvedMemberImpl simplermember = new ResolvedMemberImpl(bm.getKind(), bm.getDeclaringType(), + bm.getModifiers(), bm.getReturnType(), bm.getName(), bm.getParameterTypes());// ,bm.getExceptions(),bm.getBackingGenericMember() + // ); + simplermember.setParameterNames(bm.getParameterNames()); + return simplermember; + } + } + } + return adviceSignature; + } + + @Override + public ShadowMunger concretize(ResolvedType fromType, World world, PerClause clause) { + if (!world.areAllLintIgnored()) { + suppressLintWarnings(world); + } + ShadowMunger ret = super.concretize(fromType, world, clause); + if (!world.areAllLintIgnored()) { + clearLintSuppressions(world, this.suppressedLintKinds); + } + IfFinder ifinder = new IfFinder(); + ret.getPointcut().accept(ifinder, null); + boolean hasGuardTest = ifinder.hasIf && getKind() != AdviceKind.Around; + boolean isAround = getKind() == AdviceKind.Around; + if ((getExtraParameterFlags() & ThisJoinPoint) != 0) { + if (!isAround && !hasGuardTest && world.getLint().noGuardForLazyTjp.isEnabled()) { + // can't build tjp lazily, no suitable test... + // ... only want to record it once against the advice(bug 133117) + world.getLint().noGuardForLazyTjp.signal("", getSourceLocation()); + } + } + return ret; + } + + @Override + public ShadowMunger parameterizeWith(ResolvedType declaringType, Map typeVariableMap) { + Pointcut pc = getPointcut().parameterizeWith(typeVariableMap, declaringType.getWorld()); + + BcelAdvice ret = null; + Member adviceSignature = signature; + // allows for around advice where the return value is a type variable (see pr115250) + if (signature instanceof ResolvedMember && signature.getDeclaringType().isGenericType()) { + adviceSignature = ((ResolvedMember) signature).parameterizedWith(declaringType.getTypeParameters(), declaringType, + declaringType.isParameterizedType()); + } + ret = new BcelAdvice(this.attribute, pc, adviceSignature, this.concreteAspect); + return ret; + } + + @Override + public boolean match(Shadow shadow, World world) { + if (world.areAllLintIgnored()) { + return super.match(shadow, world); + } else { + suppressLintWarnings(world); + boolean ret = super.match(shadow, world); + clearLintSuppressions(world, this.suppressedLintKinds); + return ret; + } + } + + @Override + public void specializeOn(Shadow shadow) { + if (getKind() == AdviceKind.Around) { + ((BcelShadow) shadow).initializeForAroundClosure(); + } + + // XXX this case is just here for supporting lazy test code + if (getKind() == null) { + exposedState = new ExposedState(0); + return; + } + if (getKind().isPerEntry()) { + exposedState = new ExposedState(0); + } else if (getKind().isCflow()) { + exposedState = new ExposedState(nFreeVars); + } else if (getSignature() != null) { + exposedState = new ExposedState(getSignature()); + } else { + exposedState = new ExposedState(0); + return; // XXX this case is just here for supporting lazy test code + } + + World world = shadow.getIWorld(); + if (!world.areAllLintIgnored()) { + suppressLintWarnings(world); + } + exposedState.setConcreteAspect(concreteAspect); + runtimeTest = getPointcut().findResidue(shadow, exposedState); + if (!world.areAllLintIgnored()) { + clearLintSuppressions(world, this.suppressedLintKinds); + } + + // these initializations won't be performed by findResidue, but need to be + // so that the joinpoint is primed for weaving + if (getKind() == AdviceKind.PerThisEntry) { + shadow.getThisVar(); + } else if (getKind() == AdviceKind.PerTargetEntry) { + shadow.getTargetVar(); + } + + // make sure thisJoinPoint parameters are initialized + if ((getExtraParameterFlags() & ThisJoinPointStaticPart) != 0) { + ((BcelShadow) shadow).getThisJoinPointStaticPartVar(); + ((BcelShadow) shadow).getEnclosingClass().warnOnAddedStaticInitializer(shadow, getSourceLocation()); + } + + if ((getExtraParameterFlags() & ThisJoinPoint) != 0) { + boolean hasGuardTest = runtimeTest != Literal.TRUE && getKind() != AdviceKind.Around; + boolean isAround = getKind() == AdviceKind.Around; + ((BcelShadow) shadow).requireThisJoinPoint(hasGuardTest, isAround); + ((BcelShadow) shadow).getEnclosingClass().warnOnAddedStaticInitializer(shadow, getSourceLocation()); + if (!hasGuardTest && world.getLint().multipleAdviceStoppingLazyTjp.isEnabled()) { + // collect up the problematic advice + ((BcelShadow) shadow).addAdvicePreventingLazyTjp(this); + } + } + + if ((getExtraParameterFlags() & ThisEnclosingJoinPointStaticPart) != 0) { + ((BcelShadow) shadow).getThisEnclosingJoinPointStaticPartVar(); + ((BcelShadow) shadow).getEnclosingClass().warnOnAddedStaticInitializer(shadow, getSourceLocation()); + } + } + + private boolean canInline(Shadow s) { + if (attribute.isProceedInInners()) { + return false; + } + // XXX this guard seems to only be needed for bad test cases + if (concreteAspect == null || concreteAspect.isMissing()) { + return false; + } + + if (concreteAspect.getWorld().isXnoInline()) { + return false; + } + // System.err.println("isWoven? " + ((BcelObjectType)concreteAspect).getLazyClassGen().getWeaverState()); + BcelObjectType boType = BcelWorld.getBcelObjectType(concreteAspect); + if (boType == null) { + // Could be a symptom that the aspect failed to build last build... return the default answer of false + return false; + } + // Need isJava8 check + // Does the advice contain invokedynamic... + if (boType.javaClass.getMajor() == Constants.MAJOR_1_8) { + if (containsInvokedynamic == 0) { + containsInvokedynamic = 1; + LazyMethodGen lmg = boType.getLazyClassGen().getLazyMethodGen(this.signature.getName(), this.signature.getSignature(), true); + // Check Java8 supertypes + ResolvedType searchType = concreteAspect; + while (lmg == null) { + searchType = searchType.getSuperclass(); + if (searchType == null) break; + ReferenceTypeDelegate rtd = ((ReferenceType)searchType).getDelegate(); + if (rtd instanceof BcelObjectType) { + BcelObjectType bot = (BcelObjectType)rtd; + if (bot.javaClass.getMajor() < Constants.MAJOR_1_8) { + break; + } + lmg = bot.getLazyClassGen().getLazyMethodGen(this.signature.getName(), this.signature.getSignature(), true); + } + } + if (lmg != null) { + InstructionList ilist = lmg.getBody(); + for (InstructionHandle src = ilist.getStart(); src != null; src = src.getNext()) { + if (src.getInstruction().opcode == Constants.INVOKEDYNAMIC) { + containsInvokedynamic = 2; + break; + } + } + } + } + } + if (containsInvokedynamic == 2) { + return false; + } + return boType.getLazyClassGen().isWoven(); + } + + private boolean aspectIsBroken() { + if (concreteAspect instanceof ReferenceType) { + ReferenceTypeDelegate rtDelegate = ((ReferenceType) concreteAspect).getDelegate(); + if (!(rtDelegate instanceof BcelObjectType)) { + return true; + } + } + return false; + } + + @Override + public boolean implementOn(Shadow s) { + hasMatchedAtLeastOnce = true; + + // pr263323 - if the aspect is broken then the delegate will not be usable for weaving + if (aspectIsBroken()) { + return false; + } + + BcelShadow shadow = (BcelShadow) s; + + // remove any unnecessary exceptions if the compiler option is set to + // error or warning and if this piece of advice throws exceptions + // (bug 129282). This may be expanded to include other compiler warnings + // at the moment it only deals with 'declared exception is not thrown' + if (!shadow.getWorld().isIgnoringUnusedDeclaredThrownException() && !getThrownExceptions().isEmpty()) { + Member member = shadow.getSignature(); + if (member instanceof BcelMethod) { + removeUnnecessaryProblems((BcelMethod) member, ((BcelMethod) member).getDeclarationLineNumber()); + } else { + // we're in a call shadow therefore need the line number of the + // declared method (which may be in a different type). However, + // we want to remove the problems from the CompilationResult + // held within the current type's EclipseSourceContext so need + // the enclosing shadow too + ResolvedMember resolvedMember = shadow.getSignature().resolve(shadow.getWorld()); + if (resolvedMember instanceof BcelMethod && shadow.getEnclosingShadow() instanceof BcelShadow) { + Member enclosingMember = shadow.getEnclosingShadow().getSignature(); + if (enclosingMember instanceof BcelMethod) { + removeUnnecessaryProblems((BcelMethod) enclosingMember, + ((BcelMethod) resolvedMember).getDeclarationLineNumber()); + } + } + } + } + + if (shadow.getIWorld().isJoinpointSynchronizationEnabled() && shadow.getKind() == Shadow.MethodExecution + && (s.getSignature().getModifiers() & Modifier.SYNCHRONIZED) != 0) { + shadow.getIWorld().getLint().advisingSynchronizedMethods.signal(new String[] { shadow.toString() }, + shadow.getSourceLocation(), new ISourceLocation[] { getSourceLocation() }); + } + + // FIXME AV - see #75442, this logic is not enough so for now comment it out until we fix the bug + // // callback for perObject AJC MightHaveAspect postMunge (#75442) + // if (getConcreteAspect() != null + // && getConcreteAspect().getPerClause() != null + // && PerClause.PEROBJECT.equals(getConcreteAspect().getPerClause().getKind())) { + // final PerObject clause; + // if (getConcreteAspect().getPerClause() instanceof PerFromSuper) { + // clause = (PerObject)((PerFromSuper) getConcreteAspect().getPerClause()).lookupConcretePerClause(getConcreteAspect()); + // } else { + // clause = (PerObject) getConcreteAspect().getPerClause(); + // } + // if (clause.isThis()) { + // PerObjectInterfaceTypeMunger.registerAsAdvisedBy(s.getThisVar().getType(), getConcreteAspect()); + // } else { + // PerObjectInterfaceTypeMunger.registerAsAdvisedBy(s.getTargetVar().getType(), getConcreteAspect()); + // } + // } + if (runtimeTest == Literal.FALSE) { // not usually allowed, except in one case (260384) + Member sig = shadow.getSignature(); + if (sig.getArity() == 0 && shadow.getKind() == Shadow.MethodCall && sig.getName().charAt(0) == 'c' + && sig.getReturnType().equals(ResolvedType.OBJECT) && sig.getName().equals("clone")) { + return false; + } + } + + if (getKind() == AdviceKind.Before) { + shadow.weaveBefore(this); + } else if (getKind() == AdviceKind.AfterReturning) { + shadow.weaveAfterReturning(this); + } else if (getKind() == AdviceKind.AfterThrowing) { + UnresolvedType catchType = hasExtraParameter() ? getExtraParameterType() : UnresolvedType.THROWABLE; + shadow.weaveAfterThrowing(this, catchType); + } else if (getKind() == AdviceKind.After) { + shadow.weaveAfter(this); + } else if (getKind() == AdviceKind.Around) { + // Note: under regular LTW the aspect is usually loaded after the first use of any class affected by it. + // This means that as long as the aspect has not been thru the LTW, it's woven state is unknown + // and thus canInline(s) will return false. + // To force inlining (test), ones can do Class aspect = FQNAspect.class in the clinit of the target class + // FIXME AV : for AJC compiled @AJ aspect (or any code style aspect), the woven state can never be known + // if the aspect belongs to a parent classloader. In that case the aspect will never be inlined. + // It might be dangerous to change that especially for @AJ aspect non compiled with AJC since if those + // are not weaved (f.e. use of some limited LTW etc) then they cannot be prepared for inlining. + // One solution would be to flag @AJ aspect with an annotation as "prepared" and query that one. + LazyClassGen enclosingClass = shadow.getEnclosingClass(); + if (enclosingClass != null && enclosingClass.isInterface() && shadow.getEnclosingMethod().getName().charAt(0) == '<') { + // Do not add methods with bodies to an interface (252198, 163005) + shadow.getWorld().getLint().cannotAdviseJoinpointInInterfaceWithAroundAdvice.signal(shadow.toString(), + shadow.getSourceLocation()); + return false; + } + if (!canInline(s)) { + shadow.weaveAroundClosure(this, hasDynamicTests()); + } else { + shadow.weaveAroundInline(this, hasDynamicTests()); + } + } else if (getKind() == AdviceKind.InterInitializer) { + shadow.weaveAfterReturning(this); + } else if (getKind().isCflow()) { + shadow.weaveCflowEntry(this, getSignature()); + } else if (getKind() == AdviceKind.PerThisEntry) { + shadow.weavePerObjectEntry(this, (BcelVar) shadow.getThisVar()); + } else if (getKind() == AdviceKind.PerTargetEntry) { + shadow.weavePerObjectEntry(this, (BcelVar) shadow.getTargetVar()); + } else if (getKind() == AdviceKind.Softener) { + shadow.weaveSoftener(this, ((ExactTypePattern) exceptionType).getType()); + } else if (getKind() == AdviceKind.PerTypeWithinEntry) { + // PTWIMPL Entry to ptw is the static initialization of a type that matched the ptw type pattern + shadow.weavePerTypeWithinAspectInitialization(this, shadow.getEnclosingType()); + } else { + throw new BCException("unimplemented kind: " + getKind()); + } + return true; + } + + private void removeUnnecessaryProblems(BcelMethod method, int problemLineNumber) { + ISourceContext sourceContext = method.getSourceContext(); + if (sourceContext instanceof IEclipseSourceContext) { + ((IEclipseSourceContext) sourceContext).removeUnnecessaryProblems(method, problemLineNumber); + } + } + + // ---- implementations + + private Collection collectCheckedExceptions(UnresolvedType[] excs) { + if (excs == null || excs.length == 0) { + return Collections.emptyList(); + } + + Collection ret = new ArrayList(); + World world = concreteAspect.getWorld(); + ResolvedType runtimeException = world.getCoreType(UnresolvedType.RUNTIME_EXCEPTION); + ResolvedType error = world.getCoreType(UnresolvedType.ERROR); + + for (int i = 0, len = excs.length; i < len; i++) { + ResolvedType t = world.resolve(excs[i], true); + if (t.isMissing()) { + world.getLint().cantFindType + .signal(WeaverMessages.format(WeaverMessages.CANT_FIND_TYPE_EXCEPTION_TYPE, excs[i].getName()), + getSourceLocation()); + // IMessage msg = new Message( + // WeaverMessages.format(WeaverMessages.CANT_FIND_TYPE_EXCEPTION_TYPE,excs[i].getName()), + // "",IMessage.ERROR,getSourceLocation(),null,null); + // world.getMessageHandler().handleMessage(msg); + } + if (!(runtimeException.isAssignableFrom(t) || error.isAssignableFrom(t))) { + ret.add(t); + } + } + + return ret; + } + + private Collection thrownExceptions = null; + + @Override + public Collection getThrownExceptions() { + if (thrownExceptions == null) { + // ??? can we really lump in Around here, how does this interact with Throwable + if (concreteAspect != null && concreteAspect.getWorld() != null && // null tests for test harness + (getKind().isAfter() || getKind() == AdviceKind.Before || getKind() == AdviceKind.Around)) { + World world = concreteAspect.getWorld(); + ResolvedMember m = world.resolve(signature); + if (m == null) { + thrownExceptions = Collections.emptyList(); + } else { + thrownExceptions = collectCheckedExceptions(m.getExceptions()); + } + } else { + thrownExceptions = Collections.emptyList(); + } + } + return thrownExceptions; + } + + /** + * The munger must not check for the advice exceptions to be declared by the shadow in the case of @AJ aspects so that around + * can throws Throwable + * + * @return + */ + @Override + public boolean mustCheckExceptions() { + if (getConcreteAspect() == null) { + return true; + } + return !getConcreteAspect().isAnnotationStyleAspect(); + } + + // only call me after prepare has been called + @Override + public boolean hasDynamicTests() { + // if (hasExtraParameter() && getKind() == AdviceKind.AfterReturning) { + // UnresolvedType extraParameterType = getExtraParameterType(); + // if (! extraParameterType.equals(UnresolvedType.OBJECT) + // && ! extraParameterType.isPrimitive()) + // return true; + // } + + return runtimeTest != null && !(runtimeTest == Literal.TRUE);// || pointcutTest == Literal.NO_TEST); + } + + /** + * get the instruction list for the really simple version of this advice. Is broken apart for other advice, but if you want it + * in one block, this is the method to call. + * + * @param s The shadow around which these instructions will eventually live. + * @param extraArgVar The var that will hold the return value or thrown exception for afterX advice + * @param ifNoAdvice The instructionHandle to jump to if the dynamic tests for this munger fails. + */ + InstructionList getAdviceInstructions(BcelShadow s, BcelVar extraArgVar, InstructionHandle ifNoAdvice) { + BcelShadow shadow = s; + InstructionFactory fact = shadow.getFactory(); + BcelWorld world = shadow.getWorld(); + + InstructionList il = new InstructionList(); + + // we test to see if we have the right kind of thing... + // after throwing does this just by the exception mechanism. + if (hasExtraParameter() && getKind() == AdviceKind.AfterReturning) { + UnresolvedType extraParameterType = getExtraParameterType(); + if (!extraParameterType.equals(UnresolvedType.OBJECT) && !extraParameterType.isPrimitiveType()) { + il.append(BcelRenderer.renderTest(fact, world, + Test.makeInstanceof(extraArgVar, getExtraParameterType().resolve(world)), null, ifNoAdvice, null)); + } + } + il.append(getAdviceArgSetup(shadow, extraArgVar, null)); + il.append(getNonTestAdviceInstructions(shadow)); + + InstructionHandle ifYesAdvice = il.getStart(); + il.insert(getTestInstructions(shadow, ifYesAdvice, ifNoAdvice, ifYesAdvice)); + + // If inserting instructions at the start of a method, we need a nice line number for this entry + // in the stack trace + if (shadow.getKind() == Shadow.MethodExecution && getKind() == AdviceKind.Before) { + int lineNumber = 0; + // Uncomment this code if you think we should use the method decl line number when it exists... + // // If the advised join point is in a class built by AspectJ, we can use the declaration line number + // boolean b = shadow.getEnclosingMethod().getMemberView().hasDeclarationLineNumberInfo(); + // if (b) { + // lineNumber = shadow.getEnclosingMethod().getMemberView().getDeclarationLineNumber(); + // } else { // If it wasn't, the best we can do is the line number of the first instruction in the method + lineNumber = shadow.getEnclosingMethod().getMemberView().getLineNumberOfFirstInstruction(); + // } + InstructionHandle start = il.getStart(); + if (lineNumber > 0) { + start.addTargeter(new LineNumberTag(lineNumber)); + } + // Fix up the local variables: find any that have a startPC of 0 and ensure they target the new start of the method + LocalVariableTable lvt = shadow.getEnclosingMethod().getMemberView().getMethod().getLocalVariableTable(); + if (lvt != null) { + LocalVariable[] lvTable = lvt.getLocalVariableTable(); + for (int i = 0; i < lvTable.length; i++) { + LocalVariable lv = lvTable[i]; + if (lv.getStartPC() == 0) { + start.addTargeter(new LocalVariableTag(lv.getSignature(), lv.getName(), lv.getIndex(), 0)); + } + } + } + } + + return il; + } + + public InstructionList getAdviceArgSetup(BcelShadow shadow, BcelVar extraVar, InstructionList closureInstantiation) { + InstructionFactory fact = shadow.getFactory(); + BcelWorld world = shadow.getWorld(); + InstructionList il = new InstructionList(); + + // if (targetAspectField != null) { + // il.append(fact.createFieldAccess( + // targetAspectField.getDeclaringType().getName(), + // targetAspectField.getName(), + // BcelWorld.makeBcelType(targetAspectField.getType()), + // Constants.GETSTATIC)); + // } + // + // System.err.println("BcelAdvice: " + exposedState); + + if (exposedState.getAspectInstance() != null) { + il.append(BcelRenderer.renderExpr(fact, world, exposedState.getAspectInstance())); + } + // pr121385 + boolean x = this.getDeclaringAspect().resolve(world).isAnnotationStyleAspect(); + final boolean isAnnotationStyleAspect = getConcreteAspect() != null && getConcreteAspect().isAnnotationStyleAspect() && x; + boolean previousIsClosure = false; + for (int i = 0, len = exposedState.size(); i < len; i++) { + if (exposedState.isErroneousVar(i)) { + continue; // Erroneous vars have already had error msgs reported! + } + BcelVar v = (BcelVar) exposedState.get(i); + + if (v == null) { + // if not @AJ aspect, go on with the regular binding handling + if (!isAnnotationStyleAspect) { + + } else { + // ATAJ: for @AJ aspects, handle implicit binding of xxJoinPoint + // if (getKind() == AdviceKind.Around) { + // previousIsClosure = true; + // il.append(closureInstantiation); + if ("Lorg/aspectj/lang/ProceedingJoinPoint;".equals(getSignature().getParameterTypes()[i].getSignature())) { + // make sure we are in an around, since we deal with the closure, not the arg here + if (getKind() != AdviceKind.Around) { + previousIsClosure = false; + getConcreteAspect() + .getWorld() + .getMessageHandler() + .handleMessage( + new Message("use of ProceedingJoinPoint is allowed only on around advice (" + "arg " + + i + " in " + toString() + ")", this.getSourceLocation(), true)); + // try to avoid verify error and pass in null + il.append(InstructionConstants.ACONST_NULL); + } else { + if (previousIsClosure) { + il.append(InstructionConstants.DUP); + } else { + previousIsClosure = true; + il.append(closureInstantiation.copy()); + } + } + } else if ("Lorg/aspectj/lang/JoinPoint$StaticPart;".equals(getSignature().getParameterTypes()[i] + .getSignature())) { + previousIsClosure = false; + if ((getExtraParameterFlags() & ThisJoinPointStaticPart) != 0) { + shadow.getThisJoinPointStaticPartBcelVar().appendLoad(il, fact); + } + } else if ("Lorg/aspectj/lang/JoinPoint;".equals(getSignature().getParameterTypes()[i].getSignature())) { + previousIsClosure = false; + if ((getExtraParameterFlags() & ThisJoinPoint) != 0) { + il.append(shadow.loadThisJoinPoint()); + } + } else if ("Lorg/aspectj/lang/JoinPoint$EnclosingStaticPart;".equals(getSignature().getParameterTypes()[i] + .getSignature())) { + previousIsClosure = false; + if ((getExtraParameterFlags() & ThisEnclosingJoinPointStaticPart) != 0) { + shadow.getThisEnclosingJoinPointStaticPartBcelVar().appendLoad(il, fact); + } + } else if (hasExtraParameter()) { + previousIsClosure = false; + extraVar.appendLoadAndConvert(il, fact, getExtraParameterType().resolve(world)); + } else { + previousIsClosure = false; + getConcreteAspect() + .getWorld() + .getMessageHandler() + .handleMessage( + new Message("use of ProceedingJoinPoint is allowed only on around advice (" + "arg " + i + + " in " + toString() + ")", this.getSourceLocation(), true)); + // try to avoid verify error and pass in null + il.append(InstructionConstants.ACONST_NULL); + } + } + } else { + UnresolvedType desiredTy = getBindingParameterTypes()[i]; + v.appendLoadAndConvert(il, fact, desiredTy.resolve(world)); + } + } + + // ATAJ: for code style aspect, handles the extraFlag as usual ie not + // in the middle of the formal bindings but at the end, in a rock solid ordering + if (!isAnnotationStyleAspect) { + if (getKind() == AdviceKind.Around) { + il.append(closureInstantiation); + } else if (hasExtraParameter()) { + extraVar.appendLoadAndConvert(il, fact, getExtraParameterType().resolve(world)); + } + + // handle thisJoinPoint parameters + // these need to be in that same order as parameters in + // org.aspectj.ajdt.internal.compiler.ast.AdviceDeclaration + if ((getExtraParameterFlags() & ThisJoinPointStaticPart) != 0) { + shadow.getThisJoinPointStaticPartBcelVar().appendLoad(il, fact); + } + + if ((getExtraParameterFlags() & ThisJoinPoint) != 0) { + il.append(shadow.loadThisJoinPoint()); + } + + if ((getExtraParameterFlags() & ThisEnclosingJoinPointStaticPart) != 0) { + shadow.getThisEnclosingJoinPointStaticPartBcelVar().appendLoad(il, fact); + } + } + + return il; + } + + public InstructionList getNonTestAdviceInstructions(BcelShadow shadow) { + return new InstructionList(Utility.createInvoke(shadow.getFactory(), shadow.getWorld(), getOriginalSignature())); + } + + @Override + public Member getOriginalSignature() { + Member sig = getSignature(); + if (sig instanceof ResolvedMember) { + ResolvedMember rsig = (ResolvedMember) sig; + if (rsig.hasBackingGenericMember()) { + return rsig.getBackingGenericMember(); + } + } + return sig; + } + + public InstructionList getTestInstructions(BcelShadow shadow, InstructionHandle sk, InstructionHandle fk, InstructionHandle next) { + // System.err.println("test: " + pointcutTest); + return BcelRenderer.renderTest(shadow.getFactory(), shadow.getWorld(), runtimeTest, sk, fk, next); + } + + public int compareTo(Object other) { + if (!(other instanceof BcelAdvice)) { + return 0; + } + BcelAdvice o = (BcelAdvice) other; + + // System.err.println("compareTo: " + this + ", " + o); + if (kind.getPrecedence() != o.kind.getPrecedence()) { + if (kind.getPrecedence() > o.kind.getPrecedence()) { + return +1; + } else { + return -1; + } + } + + if (kind.isCflow()) { + // System.err.println("sort: " + this + " innerCflowEntries " + innerCflowEntries); + // System.err.println(" " + o + " innerCflowEntries " + o.innerCflowEntries); + boolean isBelow = (kind == AdviceKind.CflowBelowEntry); + + if (this.innerCflowEntries.contains(o)) { + return isBelow ? +1 : -1; + } else if (o.innerCflowEntries.contains(this)) { + return isBelow ? -1 : +1; + } else { + return 0; + } + } + + if (kind.isPerEntry() || kind == AdviceKind.Softener) { + return 0; + } + + // System.out.println("compare: " + this + " with " + other); + World world = concreteAspect.getWorld(); + + int ret = concreteAspect.getWorld().compareByPrecedence(concreteAspect, o.concreteAspect); + if (ret != 0) { + return ret; + } + + ResolvedType declaringAspect = getDeclaringAspect().resolve(world); + ResolvedType o_declaringAspect = o.getDeclaringAspect().resolve(world); + + if (declaringAspect == o_declaringAspect) { + if (kind.isAfter() || o.kind.isAfter()) { + return this.getStart() < o.getStart() ? -1 : +1; + } else { + return this.getStart() < o.getStart() ? +1 : -1; + } + } else if (declaringAspect.isAssignableFrom(o_declaringAspect)) { + return -1; + } else if (o_declaringAspect.isAssignableFrom(declaringAspect)) { + return +1; + } else { + return 0; + } + } + + public BcelVar[] getExposedStateAsBcelVars(boolean isAround) { + // ATAJ aspect + if (isAround) { + // the closure instantiation has the same mapping as the extracted method from wich it is called + if (getConcreteAspect() != null && getConcreteAspect().isAnnotationStyleAspect()) { + return BcelVar.NONE; + } + } + + // System.out.println("vars: " + Arrays.asList(exposedState.vars)); + if (exposedState == null) { + return BcelVar.NONE; + } + int len = exposedState.vars.length; + BcelVar[] ret = new BcelVar[len]; + for (int i = 0; i < len; i++) { + ret[i] = (BcelVar) exposedState.vars[i]; + } + return ret; // (BcelVar[]) exposedState.vars; + } + + protected void suppressLintWarnings(World inWorld) { + if (suppressedLintKinds == null) { + if (signature instanceof BcelMethod) { + this.suppressedLintKinds = Utility.getSuppressedWarnings(signature.getAnnotations(), inWorld.getLint()); + } else { + this.suppressedLintKinds = Collections.emptyList(); + return; + } + } + inWorld.getLint().suppressKinds(suppressedLintKinds); + } + + protected void clearLintSuppressions(World inWorld, Collection toClear) { + inWorld.getLint().clearSuppressions(toClear); + } + + /** + * For testing only + */ + public BcelAdvice(AdviceKind kind, Pointcut pointcut, Member signature, int extraArgumentFlags, int start, int end, + ISourceContext sourceContext, ResolvedType concreteAspect) { + this(new AjAttribute.AdviceAttribute(kind, pointcut, extraArgumentFlags, start, end, sourceContext), pointcut, signature, + concreteAspect); + thrownExceptions = Collections.emptyList(); // !!! interaction with unit tests + } + +} \ No newline at end of file diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/BcelAnnotation.java b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelAnnotation.java new file mode 100644 index 000000000..275eae512 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelAnnotation.java @@ -0,0 +1,151 @@ +/* ******************************************************************* + * Copyright (c) 2008 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * ******************************************************************/ +package org.aspectj.weaver.bcel; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; +import org.aspectj.apache.bcel.classfile.annotation.ArrayElementValue; +import org.aspectj.apache.bcel.classfile.annotation.ElementValue; +import org.aspectj.apache.bcel.classfile.annotation.EnumElementValue; +import org.aspectj.apache.bcel.classfile.annotation.NameValuePair; +import org.aspectj.weaver.AbstractAnnotationAJ; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.World; + +/** + * Wraps a Bcel Annotation object and uses it to answer AnnotationAJ method calls. This is cheaper than translating all Bcel + * annotations into AnnotationAJ objects. + * + * @author AndyClement + */ +public class BcelAnnotation extends AbstractAnnotationAJ { + + private final AnnotationGen bcelAnnotation; + + public BcelAnnotation(AnnotationGen theBcelAnnotation, World world) { + super(UnresolvedType.forSignature(theBcelAnnotation.getTypeSignature()).resolve(world)); + this.bcelAnnotation = theBcelAnnotation; + } + + public BcelAnnotation(AnnotationGen theBcelAnnotation, ResolvedType resolvedAnnotationType) { + super(resolvedAnnotationType); + this.bcelAnnotation = theBcelAnnotation; + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + List nvPairs = bcelAnnotation.getValues(); + sb.append("Anno[" + getTypeSignature() + " " + (isRuntimeVisible() ? "rVis" : "rInvis")); + if (nvPairs.size() > 0) { + sb.append(" "); + int i = 0; + for (NameValuePair element : nvPairs) { + if (i > 0) { + sb.append(','); + } + sb.append(element.getNameString()).append("=").append(element.getValue().toString()); + i++; + } + } + sb.append("]"); + return sb.toString(); + } + + /** + * {@inheritDoc} + */ + @Override + public Set getTargets() { + if (!type.equals(UnresolvedType.AT_TARGET)) { + return Collections.emptySet(); + } + List values = bcelAnnotation.getValues(); + NameValuePair envp = values.get(0); + ArrayElementValue aev = (ArrayElementValue) envp.getValue(); + ElementValue[] evs = aev.getElementValuesArray(); + Set targets = new HashSet(); + for (int i = 0; i < evs.length; i++) { + EnumElementValue ev = (EnumElementValue) evs[i]; + targets.add(ev.getEnumValueString()); + } + return targets; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean hasNameValuePair(String name, String value) { + return bcelAnnotation.hasNameValuePair(name, value); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean hasNamedValue(String name) { + return bcelAnnotation.hasNamedValue(name); + } + + /** + * {@inheritDoc} + */ + @Override + public String stringify() { + StringBuffer sb = new StringBuffer(); + sb.append("@").append(type.getClassName()); + List values = bcelAnnotation.getValues(); + if (values != null && values.size() != 0) { + sb.append("("); + for (NameValuePair nvPair : values) { + sb.append(nvPair.getNameString()).append("=").append(nvPair.getValue().stringifyValue()); + } + sb.append(")"); + } + return sb.toString(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isRuntimeVisible() { + return this.bcelAnnotation.isRuntimeVisible(); + } + + /** + * @return return the real bcel annotation being wrapped + */ + public AnnotationGen getBcelAnnotation() { + return bcelAnnotation; + } + + /** + * {@inheritDoc} + */ + public String getStringFormOfValue(String name) { + List annotationValues = this.bcelAnnotation.getValues(); + if (annotationValues == null || annotationValues.size() == 0) { + return null; + } else { + for (NameValuePair nvPair : annotationValues) { + if (nvPair.getNameString().equals(name)) { + return nvPair.getValue().stringifyValue(); + } + } + return null; + } + } +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/BcelCflowAccessVar.java b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelCflowAccessVar.java new file mode 100644 index 000000000..2640edd95 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelCflowAccessVar.java @@ -0,0 +1,87 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.bcel; + +import org.aspectj.apache.bcel.Constants; +import org.aspectj.apache.bcel.generic.Instruction; +import org.aspectj.apache.bcel.generic.InstructionFactory; +import org.aspectj.apache.bcel.generic.InstructionList; +import org.aspectj.apache.bcel.generic.Type; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.NameMangler; +import org.aspectj.weaver.ResolvedType; + +/** + * XXX Erik and I need to discuss this hierarchy. Having FieldRef extend Var is convenient, but hopefully there's a better design. + * + * This is always a static reference. + */ +public class BcelCflowAccessVar extends BcelVar { + + private Member stackField; + private int index; + + /** + * @param type The type to convert to from Object + * @param stackField the member containing the CFLOW_STACK_TYPE + * @param index yeah yeah + */ + public BcelCflowAccessVar(ResolvedType type, Member stackField, int index) { + super(type, 0); + this.stackField = stackField; + this.index = index; + } + + public String toString() { + return "BcelCflowAccessVar(" + getType() + " " + stackField + "." + index + ")"; + } + + public Instruction createLoad(InstructionFactory fact) { + throw new RuntimeException("unimplemented"); + } + + public Instruction createStore(InstructionFactory fact) { + throw new RuntimeException("unimplemented"); + } + + public InstructionList createCopyFrom(InstructionFactory fact, int oldSlot) { + throw new RuntimeException("unimplemented"); + } + + public void appendLoad(InstructionList il, InstructionFactory fact) { + il.append(createLoadInstructions(getType(), fact)); + } + + public InstructionList createLoadInstructions(ResolvedType toType, InstructionFactory fact) { + InstructionList il = new InstructionList(); + + il.append(Utility.createGet(fact, stackField)); + il.append(Utility.createConstant(fact, index)); + il.append(fact.createInvoke(NameMangler.CFLOW_STACK_TYPE, "get", Type.OBJECT, new Type[] { Type.INT }, + Constants.INVOKEVIRTUAL)); + il.append(Utility.createConversion(fact, Type.OBJECT, BcelWorld.makeBcelType(toType))); + + return il; + + } + + public void appendLoadAndConvert(InstructionList il, InstructionFactory fact, ResolvedType toType) { + il.append(createLoadInstructions(toType, fact)); + + } + + public void insertLoad(InstructionList il, InstructionFactory fact) { + il.insert(createLoadInstructions(getType(), fact)); + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/BcelCflowCounterFieldAdder.java b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelCflowCounterFieldAdder.java new file mode 100644 index 000000000..a70a5d8a9 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelCflowCounterFieldAdder.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (c) 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * (Andy Clement) + *******************************************************************************/ + +package org.aspectj.weaver.bcel; + +import org.aspectj.apache.bcel.Constants; +import org.aspectj.apache.bcel.generic.FieldGen; +import org.aspectj.apache.bcel.generic.InstructionFactory; +import org.aspectj.apache.bcel.generic.InstructionList; +import org.aspectj.apache.bcel.generic.ObjectType; +import org.aspectj.apache.bcel.generic.Type; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.NameMangler; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; + +/** + * This type munger will modify a given class (see the munge() method) to include a field representing a CflowCounter object. + */ +public class BcelCflowCounterFieldAdder extends BcelTypeMunger { + private ResolvedMember cflowCounterField; + + public BcelCflowCounterFieldAdder(ResolvedMember cflowCounterField) { + super(null, (ResolvedType) cflowCounterField.getDeclaringType()); + this.cflowCounterField = cflowCounterField; + } + + public boolean munge(BcelClassWeaver weaver) { + LazyClassGen gen = weaver.getLazyClassGen(); + + // Only munge one type! + if (!gen.getType().equals(cflowCounterField.getDeclaringType())) + return false; + + // Create the field declaration. + // Something like: "public static final CflowCounter ajc$cflowCounter$0;" + FieldGen f = new FieldGen(cflowCounterField.getModifiers(), BcelWorld.makeBcelType(cflowCounterField.getReturnType()), + cflowCounterField.getName(), gen.getConstantPool()); + + gen.addField(f, getSourceLocation()); + + // Modify the ajc$preClinit() method to initialize it. + // Something like: "ajc$cflowCounter$0 = new CflowCounter();" + LazyMethodGen clinit = gen.getAjcPreClinit(); // StaticInitializer(); + InstructionList setup = new InstructionList(); + InstructionFactory fact = gen.getFactory(); + + setup.append(fact.createNew(new ObjectType(NameMangler.CFLOW_COUNTER_TYPE))); + setup.append(InstructionFactory.createDup(1)); + setup.append(fact.createInvoke(NameMangler.CFLOW_COUNTER_TYPE, "", Type.VOID, new Type[0], Constants.INVOKESPECIAL)); + + setup.append(Utility.createSet(fact, cflowCounterField)); + clinit.getBody().insert(setup); + + return true; + } + + public ResolvedMember getMatchingSyntheticMember(Member member) { + return null; + } + + public ResolvedMember getSignature() { + return cflowCounterField; + } + + public boolean matches(ResolvedType onType) { + return onType.equals(cflowCounterField.getDeclaringType()); + } + + public boolean existsToSupportShadowMunging() { + return true; + } + + public String toString() { + return "(BcelTypeMunger: CflowField " + cflowCounterField.getDeclaringType().getName() + " " + cflowCounterField.getName() + + ")"; + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/BcelCflowStackFieldAdder.java b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelCflowStackFieldAdder.java new file mode 100644 index 000000000..455edd174 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelCflowStackFieldAdder.java @@ -0,0 +1,77 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.bcel; + +import org.aspectj.apache.bcel.Constants; +import org.aspectj.apache.bcel.generic.FieldGen; +import org.aspectj.apache.bcel.generic.InstructionFactory; +import org.aspectj.apache.bcel.generic.InstructionList; +import org.aspectj.apache.bcel.generic.Type; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.NameMangler; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; + +public class BcelCflowStackFieldAdder extends BcelTypeMunger { + private ResolvedMember cflowStackField; + + public BcelCflowStackFieldAdder(ResolvedMember cflowStackField) { + super(null, (ResolvedType) cflowStackField.getDeclaringType()); + this.cflowStackField = cflowStackField; + } + + @Override + public boolean munge(BcelClassWeaver weaver) { + LazyClassGen gen = weaver.getLazyClassGen(); + if (!gen.getType().equals(cflowStackField.getDeclaringType())) { + return false; + } + FieldGen f = new FieldGen(cflowStackField.getModifiers(), BcelWorld.makeBcelType(cflowStackField.getReturnType()), + cflowStackField.getName(), gen.getConstantPool()); + gen.addField(f, getSourceLocation()); + + LazyMethodGen clinit = gen.getAjcPreClinit(); // StaticInitializer(); + InstructionList setup = new InstructionList(); + InstructionFactory fact = gen.getFactory(); + + setup.append(fact.createNew(NameMangler.CFLOW_STACK_TYPE)); + setup.append(InstructionFactory.createDup(1)); + setup.append(fact.createInvoke(NameMangler.CFLOW_STACK_TYPE, "", Type.VOID, Type.NO_ARGS, Constants.INVOKESPECIAL)); + + setup.append(Utility.createSet(fact, cflowStackField)); + clinit.getBody().insert(setup); + + return true; + } + + @Override + public ResolvedMember getMatchingSyntheticMember(Member member) { + return null; + } + + @Override + public ResolvedMember getSignature() { + return cflowStackField; + } + + @Override + public boolean matches(ResolvedType onType) { + return onType.equals(cflowStackField.getDeclaringType()); + } + + @Override + public boolean existsToSupportShadowMunging() { + return true; + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/BcelClassWeaver.java b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelClassWeaver.java new file mode 100644 index 000000000..83919ed50 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelClassWeaver.java @@ -0,0 +1,3377 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.bcel; + +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import org.aspectj.apache.bcel.Constants; +import org.aspectj.apache.bcel.classfile.BootstrapMethods; +import org.aspectj.apache.bcel.classfile.ConstantPool; +import org.aspectj.apache.bcel.classfile.Method; +import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; +import org.aspectj.apache.bcel.generic.FieldGen; +import org.aspectj.apache.bcel.generic.FieldInstruction; +import org.aspectj.apache.bcel.generic.Instruction; +import org.aspectj.apache.bcel.generic.InstructionBranch; +import org.aspectj.apache.bcel.generic.InstructionCP; +import org.aspectj.apache.bcel.generic.InstructionConstants; +import org.aspectj.apache.bcel.generic.InstructionFactory; +import org.aspectj.apache.bcel.generic.InstructionHandle; +import org.aspectj.apache.bcel.generic.InstructionLV; +import org.aspectj.apache.bcel.generic.InstructionList; +import org.aspectj.apache.bcel.generic.InstructionSelect; +import org.aspectj.apache.bcel.generic.InstructionTargeter; +import org.aspectj.apache.bcel.generic.InvokeInstruction; +import org.aspectj.apache.bcel.generic.LineNumberTag; +import org.aspectj.apache.bcel.generic.LocalVariableTag; +import org.aspectj.apache.bcel.generic.MULTIANEWARRAY; +import org.aspectj.apache.bcel.generic.MethodGen; +import org.aspectj.apache.bcel.generic.ObjectType; +import org.aspectj.apache.bcel.generic.RET; +import org.aspectj.apache.bcel.generic.Tag; +import org.aspectj.apache.bcel.generic.Type; +import org.aspectj.asm.AsmManager; +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.Message; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.bridge.WeaveMessage; +import org.aspectj.bridge.context.CompilationAndWeavingContext; +import org.aspectj.bridge.context.ContextToken; +import org.aspectj.util.PartialOrder; +import org.aspectj.weaver.AjAttribute; +import org.aspectj.weaver.AjcMemberMaker; +import org.aspectj.weaver.AnnotationAJ; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.ConcreteTypeMunger; +import org.aspectj.weaver.IClassWeaver; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.MissingResolvedTypeWithKnownSignature; +import org.aspectj.weaver.NameMangler; +import org.aspectj.weaver.NewConstructorTypeMunger; +import org.aspectj.weaver.NewFieldTypeMunger; +import org.aspectj.weaver.NewMethodTypeMunger; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedMemberImpl; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.ResolvedTypeMunger; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.ShadowMunger; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.UnresolvedTypeVariableReferenceType; +import org.aspectj.weaver.WeaverStateInfo; +import org.aspectj.weaver.World; +import org.aspectj.weaver.model.AsmRelationshipProvider; +import org.aspectj.weaver.patterns.DeclareAnnotation; +import org.aspectj.weaver.patterns.ExactTypePattern; +import org.aspectj.weaver.tools.Trace; +import org.aspectj.weaver.tools.TraceFactory; + +class BcelClassWeaver implements IClassWeaver { + + private static Trace trace = TraceFactory.getTraceFactory().getTrace(BcelClassWeaver.class); + + public static boolean weave(BcelWorld world, LazyClassGen clazz, List shadowMungers, + List typeMungers, List lateTypeMungers, boolean inReweavableMode) { + BcelClassWeaver classWeaver = new BcelClassWeaver(world, clazz, shadowMungers, typeMungers, lateTypeMungers); + classWeaver.setReweavableMode(inReweavableMode); + boolean b = classWeaver.weave(); + return b; + } + + // -------------------------------------------- + + private final LazyClassGen clazz; + private final List shadowMungers; + private final List typeMungers; + private final List lateTypeMungers; + + private List[] indexedShadowMungers; + private boolean canMatchBodyShadows = false; + + private final BcelObjectType ty; // alias of clazz.getType() + private final BcelWorld world; // alias of ty.getWorld() + private final ConstantPool cpg; // alias of clazz.getConstantPoolGen() + private final InstructionFactory fact; // alias of clazz.getFactory(); + + private final List addedLazyMethodGens = new ArrayList(); + private final Set addedDispatchTargets = new HashSet(); + + private boolean inReweavableMode = false; + + private List addedSuperInitializersAsList = null; + private final Map addedSuperInitializers = new HashMap(); + private final List addedThisInitializers = new ArrayList(); + private final List addedClassInitializers = new ArrayList(); + + private final Map mapToAnnotationHolder = new HashMap(); + + // private BcelShadow clinitShadow = null; + + /** + * This holds the initialization and pre-initialization shadows for this class that were actually matched by mungers (if no + * match, then we don't even create the shadows really). + */ + private final List initializationShadows = new ArrayList(); + + private BcelClassWeaver(BcelWorld world, LazyClassGen clazz, List shadowMungers, + List typeMungers, List lateTypeMungers) { + super(); + this.world = world; + this.clazz = clazz; + this.shadowMungers = shadowMungers; + this.typeMungers = typeMungers; + this.lateTypeMungers = lateTypeMungers; + this.ty = clazz.getBcelObjectType(); + this.cpg = clazz.getConstantPool(); + this.fact = clazz.getFactory(); + + indexShadowMungers(); + + initializeSuperInitializerMap(ty.getResolvedTypeX()); + if (!checkedXsetForLowLevelContextCapturing) { + Properties p = world.getExtraConfiguration(); + if (p != null) { + String s = p.getProperty(World.xsetCAPTURE_ALL_CONTEXT, "false"); + captureLowLevelContext = s.equalsIgnoreCase("true"); + if (captureLowLevelContext) { + world.getMessageHandler().handleMessage( + MessageUtil.info("[" + World.xsetCAPTURE_ALL_CONTEXT + + "=true] Enabling collection of low level context for debug/crash messages")); + } + } + checkedXsetForLowLevelContextCapturing = true; + } + } + + private boolean canMatch(Shadow.Kind kind) { + return indexedShadowMungers[kind.getKey()] != null; + } + + // private void fastMatchShadowMungers(List shadowMungers, ArrayList + // mungers, Kind kind) { + // FastMatchInfo info = new FastMatchInfo(clazz.getType(), kind); + // for (Iterator i = shadowMungers.iterator(); i.hasNext();) { + // ShadowMunger munger = (ShadowMunger) i.next(); + // FuzzyBoolean fb = munger.getPointcut().fastMatch(info); + // WeaverMetrics.recordFastMatchResult(fb);// Could pass: + // munger.getPointcut().toString() + // if (fb.maybeTrue()) mungers.add(munger); + // } + // } + + private void initializeSuperInitializerMap(ResolvedType child) { + ResolvedType[] superInterfaces = child.getDeclaredInterfaces(); + for (int i = 0, len = superInterfaces.length; i < len; i++) { + if (ty.getResolvedTypeX().isTopmostImplementor(superInterfaces[i])) { + if (addSuperInitializer(superInterfaces[i])) { + initializeSuperInitializerMap(superInterfaces[i]); + } + } + } + } + + /** + * Process the shadow mungers into array 'buckets', each bucket represents a shadow kind and contains a list of shadowmungers + * that could potentially apply at that shadow kind. + */ + private void indexShadowMungers() { + // beware the annoying property that SHADOW_KINDS[i].getKey == (i+1) ! + indexedShadowMungers = new List[Shadow.MAX_SHADOW_KIND + 1]; + for (ShadowMunger shadowMunger : shadowMungers) { + int couldMatchKinds = shadowMunger.getPointcut().couldMatchKinds(); + for (Shadow.Kind kind : Shadow.SHADOW_KINDS) { + if (kind.isSet(couldMatchKinds)) { + byte k = kind.getKey(); + if (indexedShadowMungers[k] == null) { + indexedShadowMungers[k] = new ArrayList(); + if (!kind.isEnclosingKind()) { + canMatchBodyShadows = true; + } + } + indexedShadowMungers[k].add(shadowMunger); + } + } + } + } + + private boolean addSuperInitializer(ResolvedType onType) { + if (onType.isRawType() || onType.isParameterizedType()) { + onType = onType.getGenericType(); + } + IfaceInitList l = addedSuperInitializers.get(onType); + if (l != null) { + return false; + } + l = new IfaceInitList(onType); + addedSuperInitializers.put(onType, l); + return true; + } + + public void addInitializer(ConcreteTypeMunger cm) { + NewFieldTypeMunger m = (NewFieldTypeMunger) cm.getMunger(); + ResolvedType onType = m.getSignature().getDeclaringType().resolve(world); + if (onType.isRawType()) { + onType = onType.getGenericType(); + } + + if (Modifier.isStatic(m.getSignature().getModifiers())) { + addedClassInitializers.add(cm); + } else { + if (onType == ty.getResolvedTypeX()) { + addedThisInitializers.add(cm); + } else { + IfaceInitList l = addedSuperInitializers.get(onType); + l.list.add(cm); + } + } + } + + private static class IfaceInitList implements PartialOrder.PartialComparable { + final ResolvedType onType; + List list = new ArrayList(); + + IfaceInitList(ResolvedType onType) { + this.onType = onType; + } + + public int compareTo(Object other) { + IfaceInitList o = (IfaceInitList) other; + if (onType.isAssignableFrom(o.onType)) { + return +1; + } else if (o.onType.isAssignableFrom(onType)) { + return -1; + } else { + return 0; + } + } + + public int fallbackCompareTo(Object other) { + return 0; + } + } + + // XXX this is being called, but the result doesn't seem to be being used + public boolean addDispatchTarget(ResolvedMember m) { + return addedDispatchTargets.add(m); + } + + public void addLazyMethodGen(LazyMethodGen gen) { + addedLazyMethodGens.add(gen); + } + + public void addOrReplaceLazyMethodGen(LazyMethodGen mg) { + if (alreadyDefined(clazz, mg)) { + return; + } + + for (Iterator i = addedLazyMethodGens.iterator(); i.hasNext();) { + LazyMethodGen existing = i.next(); + if (signaturesMatch(mg, existing)) { + if (existing.definingType == null) { + // this means existing was introduced on the class itself + return; + } else if (mg.definingType.isAssignableFrom(existing.definingType)) { + // existing is mg's subtype and dominates mg + return; + } else if (existing.definingType.isAssignableFrom(mg.definingType)) { + // mg is existing's subtype and dominates existing + i.remove(); + addedLazyMethodGens.add(mg); + return; + } else { + throw new BCException("conflict between: " + mg + " and " + existing); + } + } + } + addedLazyMethodGens.add(mg); + } + + private boolean alreadyDefined(LazyClassGen clazz, LazyMethodGen mg) { + for (Iterator i = clazz.getMethodGens().iterator(); i.hasNext();) { + LazyMethodGen existing = i.next(); + if (signaturesMatch(mg, existing)) { + if (!mg.isAbstract() && existing.isAbstract()) { + i.remove(); + return false; + } + return true; + } + } + return false; + } + + private boolean signaturesMatch(LazyMethodGen mg, LazyMethodGen existing) { + return mg.getName().equals(existing.getName()) && mg.getSignature().equals(existing.getSignature()); + } + + protected static LazyMethodGen makeBridgeMethod(LazyClassGen gen, ResolvedMember member) { + + // remove abstract modifier + int mods = member.getModifiers(); + if (Modifier.isAbstract(mods)) { + mods = mods - Modifier.ABSTRACT; + } + + LazyMethodGen ret = new LazyMethodGen(mods, BcelWorld.makeBcelType(member.getReturnType()), member.getName(), + BcelWorld.makeBcelTypes(member.getParameterTypes()), UnresolvedType.getNames(member.getExceptions()), gen); + + // 43972 : Static crosscutting makes interfaces unusable for javac + // ret.makeSynthetic(); + return ret; + } + + /** + * Create a single bridge method called 'theBridgeMethod' that bridges to 'whatToBridgeTo' + */ + private static void createBridgeMethod(BcelWorld world, LazyMethodGen whatToBridgeToMethodGen, LazyClassGen clazz, ResolvedMember theBridgeMethod) { + InstructionList body; + InstructionFactory fact; + int pos = 0; + + ResolvedMember whatToBridgeTo = whatToBridgeToMethodGen.getMemberView(); + + if (whatToBridgeTo == null) { + whatToBridgeTo = new ResolvedMemberImpl(Member.METHOD, whatToBridgeToMethodGen.getEnclosingClass().getType(), + whatToBridgeToMethodGen.getAccessFlags(), whatToBridgeToMethodGen.getName(), + whatToBridgeToMethodGen.getSignature()); + } + // The bridge method in this type will have the same signature as the one in the supertype + LazyMethodGen bridgeMethod = makeBridgeMethod(clazz, theBridgeMethod); + int newflags = bridgeMethod.getAccessFlags() | Constants.ACC_BRIDGE | Constants.ACC_SYNTHETIC ;// BRIDGE = 0x00000040 + + if ((newflags & 0x00000100) != 0) { + newflags = newflags - 0x100;// NATIVE = 0x00000100 - need to clear it + } + + bridgeMethod.setAccessFlags(newflags); + Type returnType = BcelWorld.makeBcelType(theBridgeMethod.getReturnType()); + Type[] paramTypes = BcelWorld.makeBcelTypes(theBridgeMethod.getParameterTypes()); + Type[] newParamTypes = whatToBridgeToMethodGen.getArgumentTypes(); + body = bridgeMethod.getBody(); + fact = clazz.getFactory(); + + if (!whatToBridgeToMethodGen.isStatic()) { + body.append(InstructionFactory.createThis()); + pos++; + } + for (int i = 0, len = paramTypes.length; i < len; i++) { + Type paramType = paramTypes[i]; + body.append(InstructionFactory.createLoad(paramType, pos)); + if (!newParamTypes[i].equals(paramTypes[i])) { + if (world.forDEBUG_bridgingCode) { + System.err.println("Bridging: Cast " + newParamTypes[i] + " from " + paramTypes[i]); + } + body.append(fact.createCast(paramTypes[i], newParamTypes[i])); + } + pos += paramType.getSize(); + } + + body.append(Utility.createInvoke(fact, world, whatToBridgeTo)); + body.append(InstructionFactory.createReturn(returnType)); + clazz.addMethodGen(bridgeMethod); + } + + /** + * Weave a class and indicate through the return value whether the class was modified. + * + * @return true if the class was modified + */ + public boolean weave() { + if (clazz.isWoven() && !clazz.isReweavable()) { + if (world.getLint().nonReweavableTypeEncountered.isEnabled()) { + world.getLint().nonReweavableTypeEncountered.signal(clazz.getType().getName(), ty.getSourceLocation()); + } + // Integer uniqueID = new Integer(rm.hashCode() * deca.hashCode()); + // if (!reportedProblems.contains(uniqueID)) { + // reportedProblems.add(uniqueID); + // world.getLint().elementAlreadyAnnotated.signal(new String[] { rm.toString(), + // world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.ALREADY_WOVEN, clazz.getType().getName()), + // ty.getSourceLocation(), null); + return false; + } + + Set aspectsAffectingType = null; + if (inReweavableMode || clazz.getType().isAspect()) { + aspectsAffectingType = new HashSet(); + } + + boolean isChanged = false; + + // we want to "touch" all aspects + if (clazz.getType().isAspect()) { + isChanged = true; + } + + WeaverStateInfo typeWeaverState = (world.isOverWeaving() ? getLazyClassGen().getType().getWeaverState() : null); + // start by munging all typeMungers + for (ConcreteTypeMunger o : typeMungers) { + if (!(o instanceof BcelTypeMunger)) { + // ???System.err.println("surprising: " + o); + continue; + } + BcelTypeMunger munger = (BcelTypeMunger) o; + + if (typeWeaverState != null && typeWeaverState.isAspectAlreadyApplied(munger.getAspectType())) { + continue; + } + boolean typeMungerAffectedType = munger.munge(this); + if (typeMungerAffectedType) { + isChanged = true; + if (inReweavableMode || clazz.getType().isAspect()) { + aspectsAffectingType.add(munger.getAspectType().getSignature()); + } + } + } + + // Weave special half type/half shadow mungers... + isChanged = weaveDeclareAtMethodCtor(clazz) || isChanged; + isChanged = weaveDeclareAtField(clazz) || isChanged; + + // XXX do major sort of stuff + // sort according to: Major: type hierarchy + // within each list: dominates + // don't forget to sort addedThisInitialiers according to dominates + addedSuperInitializersAsList = new ArrayList(addedSuperInitializers.values()); + addedSuperInitializersAsList = PartialOrder.sort(addedSuperInitializersAsList); + if (addedSuperInitializersAsList == null) { + throw new BCException("circularity in inter-types"); + } + + // this will create a static initializer if there isn't one + // this is in just as bad taste as NOPs + LazyMethodGen staticInit = clazz.getStaticInitializer(); + staticInit.getBody().insert(genInitInstructions(addedClassInitializers, true)); + + // now go through each method, and match against each method. This + // sets up each method's {@link LazyMethodGen#matchedShadows} field, + // and it also possibly adds to {@link #initializationShadows}. + List methodGens = new ArrayList(clazz.getMethodGens()); + for (LazyMethodGen member : methodGens) { + if (!member.hasBody()) { + continue; + } + if (world.isJoinpointSynchronizationEnabled() && world.areSynchronizationPointcutsInUse() + && member.getMethod().isSynchronized()) { + transformSynchronizedMethod(member); + } + boolean shadowMungerMatched = match(member); + if (shadowMungerMatched) { + // For matching mungers, add their declaring aspects to the list + // that affected this type + if (inReweavableMode || clazz.getType().isAspect()) { + aspectsAffectingType.addAll(findAspectsForMungers(member)); + } + isChanged = true; + } + } + + // now we weave all but the initialization shadows + for (LazyMethodGen methodGen : methodGens) { + if (!methodGen.hasBody()) { + continue; + } + implement(methodGen); + } + + // if we matched any initialization shadows, we inline and weave + if (!initializationShadows.isEmpty()) { + // Repeat next step until nothing left to inline...cant go on + // infinetly as compiler will have detected and reported + // "Recursive constructor invocation" + List recursiveCtors = new ArrayList(); + while (inlineSelfConstructors(methodGens, recursiveCtors)) { + } + positionAndImplement(initializationShadows); + } + + // now proceed with late type mungers + if (lateTypeMungers != null) { + for (Iterator i = lateTypeMungers.iterator(); i.hasNext();) { + BcelTypeMunger munger = (BcelTypeMunger) i.next(); + if (munger.matches(clazz.getType())) { + boolean typeMungerAffectedType = munger.munge(this); + if (typeMungerAffectedType) { + isChanged = true; + if (inReweavableMode || clazz.getType().isAspect()) { + aspectsAffectingType.add(munger.getAspectType().getSignature()); + } + } + } + } + } + + // FIXME AV - see #75442, for now this is not enough to fix the bug, + // comment that out until we really fix it + // // flush to save some memory + // PerObjectInterfaceTypeMunger.unregisterFromAsAdvisedBy(clazz.getType() + // ); + + // finally, if we changed, we add in the introduced methods. + if (isChanged) { + clazz.getOrCreateWeaverStateInfo(inReweavableMode); + weaveInAddedMethods(); + } + + if (inReweavableMode) { + WeaverStateInfo wsi = clazz.getOrCreateWeaverStateInfo(true); + wsi.addAspectsAffectingType(aspectsAffectingType); + if (!world.isOverWeaving()) { + wsi.setUnwovenClassFileData(ty.getJavaClass().getBytes()); + wsi.setReweavable(true); + } else { + wsi.markOverweavingInUse(); + } + } else { + clazz.getOrCreateWeaverStateInfo(false).setReweavable(false); + } + + // tidyup, reduce ongoing memory usage of BcelMethods that hang around + for (LazyMethodGen mg : methodGens) { + BcelMethod method = mg.getMemberView(); + if (method != null) { + method.wipeJoinpointSignatures(); + } + } + + return isChanged; + } + + // **************************** start of bridge method creation code + // ***************** + + // FIXASC tidy this lot up !! + // FIXASC refactor into ResolvedType or even ResolvedMember? + /** + * Check if a particular method is overriding another - refactored into this helper so it can be used from multiple places. + * @return method that is overriding if it + */ + private static ResolvedMember isOverriding(ResolvedType typeToCheck, ResolvedMember methodThatMightBeGettingOverridden, + String mname, String mrettype, int mmods, boolean inSamePackage, UnresolvedType[] methodParamsArray) { + // Check if we can be an override... + if (Modifier.isStatic(methodThatMightBeGettingOverridden.getModifiers())) { + // we can't be overriding a static method + return null; + } + if (Modifier.isPrivate(methodThatMightBeGettingOverridden.getModifiers())) { + // we can't be overriding a private method + return null; + } + if (!methodThatMightBeGettingOverridden.getName().equals(mname)) { + // names do not match (this will also skip and ) + return null; + } + if (methodThatMightBeGettingOverridden.getParameterTypes().length != methodParamsArray.length) { + // not the same number of parameters + return null; + } + if (!isVisibilityOverride(mmods, methodThatMightBeGettingOverridden, inSamePackage)) { + // not override from visibility point of view + return null; + } + + if (typeToCheck.getWorld().forDEBUG_bridgingCode) { + System.err.println(" Bridging:seriously considering this might be getting overridden '" + + methodThatMightBeGettingOverridden + "'"); + } + + World w = typeToCheck.getWorld(); + + // Look at erasures of parameters (List erased is List) + boolean sameParams = true; + for (int p = 0, max = methodThatMightBeGettingOverridden.getParameterTypes().length; p < max; p++) { + + UnresolvedType mtmbgoParameter = methodThatMightBeGettingOverridden.getParameterTypes()[p]; + UnresolvedType ptype = methodParamsArray[p]; + + if (mtmbgoParameter.isTypeVariableReference()) { + if (!mtmbgoParameter.resolve(w).isAssignableFrom(ptype.resolve(w))) { + sameParams = false; + } + } else { + // old condition: + boolean b = !methodThatMightBeGettingOverridden.getParameterTypes()[p].getErasureSignature().equals( + methodParamsArray[p].getErasureSignature()); + + UnresolvedType parameterType = methodThatMightBeGettingOverridden.getParameterTypes()[p]; + + // Collapse to first bound (isn't that the same as erasure! + if (parameterType instanceof UnresolvedTypeVariableReferenceType) { + parameterType = ((UnresolvedTypeVariableReferenceType) parameterType).getTypeVariable().getFirstBound(); + } + + if (b) { // !parameterType.resolve(w).equals(parameterType2.resolve(w))) { + sameParams = false; + } + } + // + // if (!ut.getErasureSignature().equals(ut2.getErasureSignature())) + // sameParams = false; + } + + // If the 'typeToCheck' represents a parameterized type then the method + // will be the parameterized form of the + // generic method in the generic type. So if the method was 'void + // m(List lt, T t)' and the parameterized type here + // is I then the method we are looking at will be 'void + // m(List lt, String t)' which when erased + // is 'void m(List lt,String t)' - so if the parameters *do* match then + // there is a generic method we are + // overriding + + // FIXASC Why bother with the return type? If it is incompatible then the code has other problems! + if (sameParams) { + if (typeToCheck.isParameterizedType()) { + return methodThatMightBeGettingOverridden.getBackingGenericMember(); + } else if (!methodThatMightBeGettingOverridden.getReturnType().getErasureSignature().equals(mrettype)) { + // addressing the wierd situation from bug 147801 + // just check whether these things are in the right relationship + // for covariance... + ResolvedType superReturn = typeToCheck.getWorld().resolve( + UnresolvedType.forSignature(methodThatMightBeGettingOverridden.getReturnType().getErasureSignature())); + ResolvedType subReturn = typeToCheck.getWorld().resolve(UnresolvedType.forSignature(mrettype)); + if (superReturn.isAssignableFrom(subReturn)) { + return methodThatMightBeGettingOverridden; + } + // } else if (typeToCheck.isParameterizedType()) { + // return methodThatMightBeGettingOverridden.getBackingGenericMember(); + } else { + return methodThatMightBeGettingOverridden; + } + } + return null; + } + + /** + * Looks at the visibility modifiers between two methods, and knows whether they are from classes in the same package, and + * decides whether one overrides the other. + * + * @return true if there is an overrides rather than a 'hides' relationship + */ + static boolean isVisibilityOverride(int methodMods, ResolvedMember inheritedMethod, boolean inSamePackage) { + int inheritedModifiers = inheritedMethod.getModifiers(); + if (Modifier.isStatic(inheritedModifiers)) { + return false; + } + if (methodMods == inheritedModifiers) { + return true; + } + + if (Modifier.isPrivate(inheritedModifiers)) { + return false; + } + + boolean isPackageVisible = !Modifier.isPrivate(inheritedModifiers) && !Modifier.isProtected(inheritedModifiers) + && !Modifier.isPublic(inheritedModifiers); + if (isPackageVisible && !inSamePackage) { + return false; + } + + return true; + } + + /** + * This method recurses up a specified type looking for a method that overrides the one passed in. + * + * @return the method being overridden or null if none is found + */ + public static void checkForOverride(ResolvedType typeToCheck, String mname, String mparams, String mrettype, + int mmods, String mpkg, UnresolvedType[] methodParamsArray, List overriddenMethodsCollector) { + + if (typeToCheck == null) { + return; + } + if (typeToCheck instanceof MissingResolvedTypeWithKnownSignature) { + return; // we just can't tell ! + } + + + if (typeToCheck.getWorld().forDEBUG_bridgingCode) { + System.err.println(" Bridging:checking for override of " + mname + " in " + typeToCheck); + } + + String packageName = typeToCheck.getPackageName(); + if (packageName == null) { + packageName = ""; + } + // used when looking at visibility rules + boolean inSamePackage = packageName.equals(mpkg); + + ResolvedMember[] methods = typeToCheck.getDeclaredMethods(); + for (int ii = 0; ii < methods.length; ii++) { + // the method we are going to check + ResolvedMember methodThatMightBeGettingOverridden = methods[ii]; + ResolvedMember isOverriding = isOverriding(typeToCheck, methodThatMightBeGettingOverridden, mname, mrettype, mmods, + inSamePackage, methodParamsArray); + if (isOverriding != null) { + overriddenMethodsCollector.add(isOverriding); + } + } + // was: List l = typeToCheck.getInterTypeMungers(); + List l = (typeToCheck.isRawType() ? typeToCheck.getGenericType().getInterTypeMungers() : typeToCheck + .getInterTypeMungers()); + for (Iterator iterator = l.iterator(); iterator.hasNext();) { + ConcreteTypeMunger o = iterator.next(); + // FIXME asc if its not a BcelTypeMunger then its an + // EclipseTypeMunger ... do I need to worry about that? + if (o instanceof BcelTypeMunger) { + BcelTypeMunger element = (BcelTypeMunger) o; + if (element.getMunger() instanceof NewMethodTypeMunger) { + if (typeToCheck.getWorld().forDEBUG_bridgingCode) { + System.err.println("Possible ITD candidate " + element); + } + ResolvedMember aMethod = element.getSignature(); + ResolvedMember isOverriding = isOverriding(typeToCheck, aMethod, mname, mrettype, mmods, inSamePackage, + methodParamsArray); + if (isOverriding != null) { + overriddenMethodsCollector.add(isOverriding); + } + } + } + } + + if (typeToCheck.equals(UnresolvedType.OBJECT)) { + return; + } + + ResolvedType superclass = typeToCheck.getSuperclass(); + checkForOverride(superclass, mname, mparams, mrettype, mmods, mpkg, methodParamsArray,overriddenMethodsCollector); + + ResolvedType[] interfaces = typeToCheck.getDeclaredInterfaces(); + for (int i = 0; i < interfaces.length; i++) { + ResolvedType anInterface = interfaces[i]; + checkForOverride(anInterface, mname, mparams, mrettype, mmods, mpkg, methodParamsArray,overriddenMethodsCollector); + } + } + + /** + * We need to determine if any methods in this type require bridge methods - this method should only be called if necessary to + * do this calculation, i.e. we are on a 1.5 VM (where covariance/generics exist) and the type hierarchy for the specified class + * has changed (via decp/itd). + * + * See pr108101 + */ + public static boolean calculateAnyRequiredBridgeMethods(BcelWorld world, LazyClassGen clazz) { + world.ensureAdvancedConfigurationProcessed(); + + if (!world.isInJava5Mode()) { + return false; // just double check... the caller should have already + } + if (clazz.isInterface()) { + return false; // dont bother if we are an interface + } + + boolean didSomething = false; // set if we build any bridge methods + // So what methods do we have right now in this class? + List methods = clazz.getMethodGens(); + + // Keep a set of all methods from this type - it'll help us to check if bridge methods + // have already been created, we don't want to do it twice! + Set methodsSet = new HashSet(); + for (int i = 0; i < methods.size(); i++) { + LazyMethodGen aMethod = methods.get(i); + StringBuilder sb = new StringBuilder(aMethod.getName()); + sb.append(aMethod.getSignature()); + methodsSet.add(sb.toString()); // e.g. "foo(Ljava/lang/String;)V" + } + + // Now go through all the methods in this type + for (int i = 0; i < methods.size(); i++) { + // This is the local method that we *might* have to bridge to + LazyMethodGen bridgeToCandidate = methods.get(i); + if (bridgeToCandidate.isBridgeMethod()) { + continue; // Doh! + } + String name = bridgeToCandidate.getName(); + String psig = bridgeToCandidate.getParameterSignature(); + String rsig = bridgeToCandidate.getReturnType().getSignature(); + + // if (bridgeToCandidate.isAbstract()) continue; + if (bridgeToCandidate.isStatic()) { + continue; // ignore static methods + } + if (name.endsWith("init>")) { + continue; // Skip constructors and static initializers + } + + if (world.forDEBUG_bridgingCode) { + System.err.println("Bridging: Determining if we have to bridge to " + clazz.getName() + "." + name + "" + bridgeToCandidate.getSignature()); + } + + // Let's take a look at the superclass + ResolvedType theSuperclass = clazz.getSuperClass(); + if (world.forDEBUG_bridgingCode) { + System.err.println("Bridging: Checking supertype " + theSuperclass); + } + String pkgName = clazz.getPackageName(); + UnresolvedType[] bm = BcelWorld.fromBcel(bridgeToCandidate.getArgumentTypes()); + List overriddenMethodsCollector = new ArrayList(); + checkForOverride(theSuperclass, name, psig, rsig, bridgeToCandidate.getAccessFlags(), pkgName, bm, overriddenMethodsCollector); + if (overriddenMethodsCollector.size() != 0) { + for (ResolvedMember overriddenMethod: overriddenMethodsCollector) { + String key = new StringBuilder(overriddenMethod.getName()).append(overriddenMethod.getSignatureErased()).toString(); // pr237419 + boolean alreadyHaveABridgeMethod = methodsSet.contains(key); + if (!alreadyHaveABridgeMethod) { + if (world.forDEBUG_bridgingCode) { + System.err.println("Bridging:bridging to '" + overriddenMethod + "'"); + } + createBridgeMethod(world, bridgeToCandidate, clazz, overriddenMethod); + methodsSet.add(key); + didSomething = true; + } + } + } + + // Check superinterfaces + String[] interfaces = clazz.getInterfaceNames(); + for (int j = 0; j < interfaces.length; j++) { + if (world.forDEBUG_bridgingCode) { + System.err.println("Bridging:checking superinterface " + interfaces[j]); + } + ResolvedType interfaceType = world.resolve(interfaces[j]); + overriddenMethodsCollector.clear(); + checkForOverride(interfaceType, name, psig, rsig, bridgeToCandidate.getAccessFlags(), + clazz.getPackageName(), bm, overriddenMethodsCollector); + for (ResolvedMember overriddenMethod: overriddenMethodsCollector) { + String key = new StringBuffer().append(overriddenMethod.getName()).append(overriddenMethod.getSignatureErased()).toString(); // pr237419 + boolean alreadyHaveABridgeMethod = methodsSet.contains(key); + if (!alreadyHaveABridgeMethod) { + createBridgeMethod(world, bridgeToCandidate, clazz, overriddenMethod); + methodsSet.add(key); + didSomething = true; + if (world.forDEBUG_bridgingCode) { + System.err.println("Bridging:bridging to " + overriddenMethod); + } + } + } + } + } + + return didSomething; + } + + // **************************** end of bridge method creation code ***************** + + /** + * Weave any declare @method/@ctor statements into the members of the supplied class + */ + private boolean weaveDeclareAtMethodCtor(LazyClassGen clazz) { + List reportedProblems = new ArrayList(); + + List allDecams = world.getDeclareAnnotationOnMethods(); + if (allDecams.isEmpty()) { + return false; + } + + boolean isChanged = false; + + // deal with ITDs + List itdMethodsCtors = getITDSubset(clazz, ResolvedTypeMunger.Method); + itdMethodsCtors.addAll(getITDSubset(clazz, ResolvedTypeMunger.Constructor)); + if (!itdMethodsCtors.isEmpty()) { + // Can't use the subset called 'decaMs' as it won't be right for + // ITDs... + isChanged = weaveAtMethodOnITDSRepeatedly(allDecams, itdMethodsCtors, reportedProblems); + } + + List decaMs = getMatchingSubset(allDecams, clazz.getType()); + if (decaMs.isEmpty()) { + return false; // nothing to do + } + + Set unusedDecams = new HashSet(); + unusedDecams.addAll(decaMs); + + // These methods may have been targeted with declare annotation. Example: ITD on an interface + // where the top most implementor gets a real method. The top most implementor method + // is an 'addedLazyMethodGen' + if (addedLazyMethodGens!=null) { + for (LazyMethodGen method: addedLazyMethodGens) { + // They have no resolvedmember of their own, conjure one up for matching purposes + ResolvedMember resolvedmember = + new ResolvedMemberImpl(ResolvedMember.METHOD,method.getEnclosingClass().getType(),method.getAccessFlags(), + BcelWorld.fromBcel(method.getReturnType()),method.getName(), + BcelWorld.fromBcel(method.getArgumentTypes()),UnresolvedType.forNames(method.getDeclaredExceptions())); + resolvedmember.setAnnotationTypes(method.getAnnotationTypes()); + resolvedmember.setAnnotations(method.getAnnotations()); + + List worthRetrying = new ArrayList(); + boolean modificationOccured = false; + for (DeclareAnnotation decam: decaMs) { + if (decam.matches(resolvedmember, world)) { + if (doesAlreadyHaveAnnotation(resolvedmember, decam, reportedProblems,false)) { + // remove the declare @method since don't want an error when the annotation is already there + unusedDecams.remove(decam); + continue; + } + + AnnotationGen a = ((BcelAnnotation) decam.getAnnotation()).getBcelAnnotation(); + // create copy to get the annotation type into the right constant pool + AnnotationAJ aj = new BcelAnnotation(new AnnotationGen(a, clazz.getConstantPool(), true),world); + method.addAnnotation(aj); + resolvedmember.addAnnotation(decam.getAnnotation()); + + AsmRelationshipProvider.addDeclareAnnotationMethodRelationship(decam.getSourceLocation(), + clazz.getName(), resolvedmember, world.getModelAsAsmManager()); + reportMethodCtorWeavingMessage(clazz, resolvedmember, decam, method.getDeclarationLineNumber()); + isChanged = true; + modificationOccured = true; + unusedDecams.remove(decam); + } else if (!decam.isStarredAnnotationPattern()) { + // an annotation is specified that might be put on by a subsequent decaf + worthRetrying.add(decam); + } + } + + // Multiple secondary passes + while (!worthRetrying.isEmpty() && modificationOccured) { + modificationOccured = false; + // lets have another go + List forRemoval = new ArrayList(); + for (DeclareAnnotation decam : worthRetrying) { + if (decam.matches(resolvedmember, world)) { + if (doesAlreadyHaveAnnotation(resolvedmember, decam, reportedProblems,false)) { + // remove the declare @method since don't + // want an error when + // the annotation is already there + unusedDecams.remove(decam); + continue; // skip this one... + } + AnnotationGen a = ((BcelAnnotation) decam.getAnnotation()).getBcelAnnotation(); + // create copy to get the annotation type into the right constant pool + AnnotationAJ aj = new BcelAnnotation(new AnnotationGen(a, clazz.getConstantPool(), true),world); + method.addAnnotation(aj); + resolvedmember.addAnnotation(decam.getAnnotation()); + AsmRelationshipProvider.addDeclareAnnotationMethodRelationship(decam.getSourceLocation(), + clazz.getName(), resolvedmember, world.getModelAsAsmManager());// getMethod()); + isChanged = true; + modificationOccured = true; + forRemoval.add(decam); + unusedDecams.remove(decam); + } + } + worthRetrying.removeAll(forRemoval); + } + } + } + + + // deal with all the other methods... + List members = clazz.getMethodGens(); + if (!members.isEmpty()) { + for (int memberCounter = 0; memberCounter < members.size(); memberCounter++) { + LazyMethodGen mg = members.get(memberCounter); + if (!mg.getName().startsWith(NameMangler.PREFIX)) { + + // Single first pass + List worthRetrying = new ArrayList(); + boolean modificationOccured = false; + List annotationsToAdd = null; + for (DeclareAnnotation decaM : decaMs) { + + if (decaM.matches(mg.getMemberView(), world)) { + if (doesAlreadyHaveAnnotation(mg.getMemberView(), decaM, reportedProblems,true)) { + // remove the declare @method since don't want + // an error when the annotation is already there + unusedDecams.remove(decaM); + continue; // skip this one... + } + + if (annotationsToAdd == null) { + annotationsToAdd = new ArrayList(); + } + AnnotationGen a = ((BcelAnnotation) decaM.getAnnotation()).getBcelAnnotation(); + AnnotationGen ag = new AnnotationGen(a, clazz.getConstantPool(), true); + annotationsToAdd.add(ag); + mg.addAnnotation(decaM.getAnnotation()); + + AsmRelationshipProvider.addDeclareAnnotationMethodRelationship(decaM.getSourceLocation(), + clazz.getName(), mg.getMemberView(), world.getModelAsAsmManager());// getMethod()); + reportMethodCtorWeavingMessage(clazz, mg.getMemberView(), decaM, mg.getDeclarationLineNumber()); + isChanged = true; + modificationOccured = true; + // remove the declare @method since have matched + // against it + unusedDecams.remove(decaM); + } else { + if (!decaM.isStarredAnnotationPattern()) { + worthRetrying.add(decaM); // an annotation is + // specified that + // might be put on + // by a subsequent + // decaf + } + } + } + + // Multiple secondary passes + while (!worthRetrying.isEmpty() && modificationOccured) { + modificationOccured = false; + // lets have another go + List forRemoval = new ArrayList(); + for (DeclareAnnotation decaM : worthRetrying) { + if (decaM.matches(mg.getMemberView(), world)) { + if (doesAlreadyHaveAnnotation(mg.getMemberView(), decaM, reportedProblems,true)) { + // remove the declare @method since don't + // want an error when + // the annotation is already there + unusedDecams.remove(decaM); + continue; // skip this one... + } + + if (annotationsToAdd == null) { + annotationsToAdd = new ArrayList(); + } + AnnotationGen a = ((BcelAnnotation) decaM.getAnnotation()).getBcelAnnotation(); + // create copy to get the annotation type into the right constant pool + AnnotationGen ag = new AnnotationGen(a, clazz.getConstantPool(), true); + annotationsToAdd.add(ag); + mg.addAnnotation(decaM.getAnnotation()); + AsmRelationshipProvider.addDeclareAnnotationMethodRelationship(decaM.getSourceLocation(), + clazz.getName(), mg.getMemberView(), world.getModelAsAsmManager());// getMethod()); + isChanged = true; + modificationOccured = true; + forRemoval.add(decaM); + // remove the declare @method since have matched + // against it + unusedDecams.remove(decaM); + } + } + worthRetrying.removeAll(forRemoval); + } + if (annotationsToAdd != null) { + Method oldMethod = mg.getMethod(); + MethodGen myGen = new MethodGen(oldMethod, clazz.getClassName(), clazz.getConstantPool(), false); + for (AnnotationGen a : annotationsToAdd) { + myGen.addAnnotation(a); + } + Method newMethod = myGen.getMethod(); + members.set(memberCounter, new LazyMethodGen(newMethod, clazz)); + } + + } + } + checkUnusedDeclareAts(unusedDecams, false); + } + return isChanged; + } + + // TAG: WeavingMessage + private void reportMethodCtorWeavingMessage(LazyClassGen clazz, ResolvedMember member, DeclareAnnotation decaM, + int memberLineNumber) { + if (!getWorld().getMessageHandler().isIgnoring(IMessage.WEAVEINFO)) { + StringBuffer parmString = new StringBuffer("("); + UnresolvedType[] paramTypes = member.getParameterTypes(); + for (int i = 0; i < paramTypes.length; i++) { + UnresolvedType type = paramTypes[i]; + String s = org.aspectj.apache.bcel.classfile.Utility.signatureToString(type.getSignature()); + if (s.lastIndexOf('.') != -1) { + s = s.substring(s.lastIndexOf('.') + 1); + } + parmString.append(s); + if ((i + 1) < paramTypes.length) { + parmString.append(","); + } + } + parmString.append(")"); + String methodName = member.getName(); + StringBuffer sig = new StringBuffer(); + sig.append(org.aspectj.apache.bcel.classfile.Utility.accessToString(member.getModifiers())); + sig.append(" "); + sig.append(member.getReturnType().toString()); + sig.append(" "); + sig.append(member.getDeclaringType().toString()); + sig.append("."); + sig.append(methodName.equals("") ? "new" : methodName); + sig.append(parmString); + + StringBuffer loc = new StringBuffer(); + if (clazz.getFileName() == null) { + loc.append("no debug info available"); + } else { + loc.append(clazz.getFileName()); + if (memberLineNumber != -1) { + loc.append(":" + memberLineNumber); + } + } + getWorld().getMessageHandler().handleMessage( + WeaveMessage.constructWeavingMessage( + WeaveMessage.WEAVEMESSAGE_ANNOTATES, + new String[] { sig.toString(), loc.toString(), decaM.getAnnotationString(), + methodName.startsWith("") ? "constructor" : "method", decaM.getAspect().toString(), + Utility.beautifyLocation(decaM.getSourceLocation()) })); + } + } + + /** + * Looks through a list of declare annotation statements and only returns those that could possibly match on a field/method/ctor + * in type. + */ + private List getMatchingSubset(List declareAnnotations, ResolvedType type) { + List subset = new ArrayList(); + for (DeclareAnnotation da : declareAnnotations) { + if (da.couldEverMatch(type)) { + subset.add(da); + } + } + return subset; + } + + /** + * Get a subset of all the type mungers defined on this aspect + */ + private List getITDSubset(LazyClassGen clazz, ResolvedTypeMunger.Kind wantedKind) { + List subset = new ArrayList(); + for (ConcreteTypeMunger typeMunger : clazz.getBcelObjectType().getTypeMungers()) { + if (typeMunger.getMunger().getKind() == wantedKind) { + subset.add(typeMunger); + } + } + return subset; + } + + public LazyMethodGen locateAnnotationHolderForFieldMunger(LazyClassGen clazz, ConcreteTypeMunger fieldMunger) { + NewFieldTypeMunger newFieldMunger = (NewFieldTypeMunger) fieldMunger.getMunger(); + ResolvedMember lookingFor = AjcMemberMaker.interFieldInitializer(newFieldMunger.getSignature(), clazz.getType()); + for (LazyMethodGen method : clazz.getMethodGens()) { + if (method.getName().equals(lookingFor.getName())) { + return method; + } + } + return null; + } + + // FIXME asc refactor this to neaten it up + public LazyMethodGen locateAnnotationHolderForMethodCtorMunger(LazyClassGen clazz, ConcreteTypeMunger methodCtorMunger) { + ResolvedTypeMunger rtMunger = methodCtorMunger.getMunger(); + ResolvedMember lookingFor = null; + if (rtMunger instanceof NewMethodTypeMunger) { + NewMethodTypeMunger nftm = (NewMethodTypeMunger) rtMunger; + lookingFor = AjcMemberMaker.interMethodDispatcher(nftm.getSignature(), methodCtorMunger.getAspectType()); + } else if (rtMunger instanceof NewConstructorTypeMunger) { + NewConstructorTypeMunger nftm = (NewConstructorTypeMunger) rtMunger; + lookingFor = AjcMemberMaker.postIntroducedConstructor(methodCtorMunger.getAspectType(), nftm.getSignature() + .getDeclaringType(), nftm.getSignature().getParameterTypes()); + } else { + throw new BCException("Not sure what this is: " + methodCtorMunger); + } + String name = lookingFor.getName(); + String paramSignature = lookingFor.getParameterSignature(); + for (LazyMethodGen member : clazz.getMethodGens()) { + if (member.getName().equals(name) && member.getParameterSignature().equals(paramSignature)) { + return member; + } + } + return null; + } + + /** + * Applies some set of declare @field constructs (List) to some bunch of ITDfields (List. It + * will iterate over the fields repeatedly until everything has been applied. + * + */ + private boolean weaveAtFieldRepeatedly(List decaFs, List itdFields, + List reportedErrors) { + boolean isChanged = false; + for (Iterator iter = itdFields.iterator(); iter.hasNext();) { + BcelTypeMunger fieldMunger = (BcelTypeMunger) iter.next(); + ResolvedMember itdIsActually = fieldMunger.getSignature(); + Set worthRetrying = new LinkedHashSet(); + boolean modificationOccured = false; + + for (Iterator iter2 = decaFs.iterator(); iter2.hasNext();) { + DeclareAnnotation decaF = iter2.next(); + if (decaF.matches(itdIsActually, world)) { + if (decaF.isRemover()) { + LazyMethodGen annotationHolder = locateAnnotationHolderForFieldMunger(clazz, fieldMunger); + if (annotationHolder.hasAnnotation(decaF.getAnnotationType())) { + isChanged = true; + // something to remove + annotationHolder.removeAnnotation(decaF.getAnnotationType()); + AsmRelationshipProvider.addDeclareAnnotationRelationship(world.getModelAsAsmManager(), + decaF.getSourceLocation(), itdIsActually.getSourceLocation(), true); + } else { + worthRetrying.add(decaF); + } + } else { + + LazyMethodGen annotationHolder = locateAnnotationHolderForFieldMunger(clazz, fieldMunger); + if (doesAlreadyHaveAnnotation(annotationHolder, itdIsActually, decaF, reportedErrors)) { + continue; // skip this one... + } + annotationHolder.addAnnotation(decaF.getAnnotation()); + AsmRelationshipProvider.addDeclareAnnotationRelationship(world.getModelAsAsmManager(), + decaF.getSourceLocation(), itdIsActually.getSourceLocation(), false); + isChanged = true; + modificationOccured = true; + } + } else { + if (!decaF.isStarredAnnotationPattern()) { + worthRetrying.add(decaF); // an annotation is specified + // that might be put on by a + // subsequent decaf + } + } + } + + while (!worthRetrying.isEmpty() && modificationOccured) { + modificationOccured = false; + List forRemoval = new ArrayList(); + for (Iterator iter2 = worthRetrying.iterator(); iter2.hasNext();) { + DeclareAnnotation decaF = iter2.next(); + if (decaF.matches(itdIsActually, world)) { + if (decaF.isRemover()) { + LazyMethodGen annotationHolder = locateAnnotationHolderForFieldMunger(clazz, fieldMunger); + if (annotationHolder.hasAnnotation(decaF.getAnnotationType())) { + isChanged = true; + // something to remove + annotationHolder.removeAnnotation(decaF.getAnnotationType()); + AsmRelationshipProvider.addDeclareAnnotationRelationship(world.getModelAsAsmManager(), + decaF.getSourceLocation(), itdIsActually.getSourceLocation(), true); + forRemoval.add(decaF); + } + } else { + LazyMethodGen annotationHolder = locateAnnotationHolderForFieldMunger(clazz, fieldMunger); + if (doesAlreadyHaveAnnotation(annotationHolder, itdIsActually, decaF, reportedErrors)) { + continue; // skip this one... + } + annotationHolder.addAnnotation(decaF.getAnnotation()); + AsmRelationshipProvider.addDeclareAnnotationRelationship(world.getModelAsAsmManager(), + decaF.getSourceLocation(), itdIsActually.getSourceLocation(), false); + isChanged = true; + modificationOccured = true; + forRemoval.add(decaF); + } + } + } + worthRetrying.removeAll(forRemoval); + } + } + return isChanged; + } + + /** + * Applies some set of declare @method/@ctor constructs (List) to some bunch of ITDmembers + * (List. It will iterate over the fields repeatedly until everything has been applied. + */ + private boolean weaveAtMethodOnITDSRepeatedly(List decaMCs, + List itdsForMethodAndConstructor, List reportedErrors) { + boolean isChanged = false; + AsmManager asmManager = world.getModelAsAsmManager(); + for (ConcreteTypeMunger methodctorMunger : itdsForMethodAndConstructor) { + // for (Iterator iter = itdsForMethodAndConstructor.iterator(); iter.hasNext();) { + // BcelTypeMunger methodctorMunger = (BcelTypeMunger) iter.next(); + ResolvedMember unMangledInterMethod = methodctorMunger.getSignature(); + List worthRetrying = new ArrayList(); + boolean modificationOccured = false; + + for (Iterator iter2 = decaMCs.iterator(); iter2.hasNext();) { + DeclareAnnotation decaMC = iter2.next(); + if (decaMC.matches(unMangledInterMethod, world)) { + LazyMethodGen annotationHolder = locateAnnotationHolderForMethodCtorMunger(clazz, methodctorMunger); + if (annotationHolder == null + || doesAlreadyHaveAnnotation(annotationHolder, unMangledInterMethod, decaMC, reportedErrors)) { + continue; // skip this one... + } + annotationHolder.addAnnotation(decaMC.getAnnotation()); + isChanged = true; + AsmRelationshipProvider.addDeclareAnnotationRelationship(asmManager, decaMC.getSourceLocation(), + unMangledInterMethod.getSourceLocation(), false); + reportMethodCtorWeavingMessage(clazz, unMangledInterMethod, decaMC, -1); + modificationOccured = true; + } else { + // If an annotation is specified, it might be added by one of the other declare annotation statements + if (!decaMC.isStarredAnnotationPattern()) { + worthRetrying.add(decaMC); + } + } + } + + while (!worthRetrying.isEmpty() && modificationOccured) { + modificationOccured = false; + List forRemoval = new ArrayList(); + for (Iterator iter2 = worthRetrying.iterator(); iter2.hasNext();) { + DeclareAnnotation decaMC = iter2.next(); + if (decaMC.matches(unMangledInterMethod, world)) { + LazyMethodGen annotationHolder = locateAnnotationHolderForFieldMunger(clazz, methodctorMunger); + if (doesAlreadyHaveAnnotation(annotationHolder, unMangledInterMethod, decaMC, reportedErrors)) { + continue; // skip this one... + } + annotationHolder.addAnnotation(decaMC.getAnnotation()); + unMangledInterMethod.addAnnotation(decaMC.getAnnotation()); + AsmRelationshipProvider.addDeclareAnnotationRelationship(asmManager, decaMC.getSourceLocation(), + unMangledInterMethod.getSourceLocation(), false); + isChanged = true; + modificationOccured = true; + forRemoval.add(decaMC); + } + worthRetrying.removeAll(forRemoval); + } + } + } + return isChanged; + } + + private boolean dontAddTwice(DeclareAnnotation decaF, AnnotationAJ[] dontAddMeTwice) { + for (AnnotationAJ ann : dontAddMeTwice) { + if (ann != null && decaF.getAnnotation().getTypeName().equals(ann.getTypeName())) { + return true; + } + } + return false; + } + + /** + * Remove an annotation from the supplied array, if it is in there. + */ + private AnnotationAJ[] removeFromAnnotationsArray(AnnotationAJ[] annotations,AnnotationAJ annotation) { + for (int i=0;i reportedProblems = new ArrayList(); + List allDecafs = world.getDeclareAnnotationOnFields(); + if (allDecafs.isEmpty()) { + return false; + } + boolean typeIsChanged = false; + List relevantItdFields = getITDSubset(clazz, ResolvedTypeMunger.Field); + if (relevantItdFields != null) { + typeIsChanged = weaveAtFieldRepeatedly(allDecafs, relevantItdFields, reportedProblems); + } + + List decafs = getMatchingSubset(allDecafs, clazz.getType()); + if (decafs.isEmpty()) { + return typeIsChanged; + } + + List fields = clazz.getFieldGens(); + if (fields != null) { + Set unusedDecafs = new HashSet(); + unusedDecafs.addAll(decafs); + for (BcelField field : fields) { + if (!field.getName().startsWith(NameMangler.PREFIX)) { + // Single first pass + Set worthRetrying = new LinkedHashSet(); + boolean modificationOccured = false; + AnnotationAJ[] dontAddMeTwice = field.getAnnotations(); + + // go through all the declare @field statements + for (DeclareAnnotation decaf : decafs) { + if (decaf.getAnnotation() == null) { + return false; + } + if (decaf.matches(field, world)) { + if (decaf.isRemover()) { + AnnotationAJ annotation = decaf.getAnnotation(); + if (field.hasAnnotation(annotation.getType())) { + // something to remove + typeIsChanged = true; + field.removeAnnotation(annotation); + AsmRelationshipProvider.addDeclareAnnotationFieldRelationship(world.getModelAsAsmManager(), + decaf.getSourceLocation(), clazz.getName(), field, true); + reportFieldAnnotationWeavingMessage(clazz, field, decaf, true); + dontAddMeTwice = removeFromAnnotationsArray(dontAddMeTwice, annotation); + } else { + worthRetrying.add(decaf); + } + unusedDecafs.remove(decaf); + } else { + if (!dontAddTwice(decaf, dontAddMeTwice)) { + if (doesAlreadyHaveAnnotation(field, decaf, reportedProblems,true )) { + // remove the declare @field since don't want an error when the annotation is already there + unusedDecafs.remove(decaf); + continue; + } + field.addAnnotation(decaf.getAnnotation()); + } + AsmRelationshipProvider.addDeclareAnnotationFieldRelationship(world.getModelAsAsmManager(), + decaf.getSourceLocation(), clazz.getName(), field, false); + reportFieldAnnotationWeavingMessage(clazz, field, decaf, false); + typeIsChanged = true; + modificationOccured = true; + unusedDecafs.remove(decaf); + } + } else if (!decaf.isStarredAnnotationPattern() || decaf.isRemover()) { + worthRetrying.add(decaf); // an annotation is specified that might be put on by a subsequent decaf + } + } + + // Multiple secondary passes + while (!worthRetrying.isEmpty() && modificationOccured) { + modificationOccured = false; + // lets have another go with any remaining ones + List forRemoval = new ArrayList(); + for (Iterator iter = worthRetrying.iterator(); iter.hasNext();) { + DeclareAnnotation decaF = iter.next(); + + if (decaF.matches(field, world)) { + if (decaF.isRemover()) { + AnnotationAJ annotation = decaF.getAnnotation(); + if (field.hasAnnotation(annotation.getType())) { + // something to remove + typeIsChanged = modificationOccured = true; + forRemoval.add(decaF); + field.removeAnnotation(annotation); + AsmRelationshipProvider.addDeclareAnnotationFieldRelationship(world.getModelAsAsmManager(), + decaF.getSourceLocation(), clazz.getName(), field, true); + reportFieldAnnotationWeavingMessage(clazz, field, decaF, true); + } + } else { + // below code is for recursive things + unusedDecafs.remove(decaF); + if (doesAlreadyHaveAnnotation(field, decaF, reportedProblems,true)) { + continue; + } + field.addAnnotation(decaF.getAnnotation()); + AsmRelationshipProvider.addDeclareAnnotationFieldRelationship(world.getModelAsAsmManager(), + decaF.getSourceLocation(), clazz.getName(), field, false); + typeIsChanged = modificationOccured = true; + forRemoval.add(decaF); + } + } + } + worthRetrying.removeAll(forRemoval); + } + } + } + checkUnusedDeclareAts(unusedDecafs, true); + } + return typeIsChanged; + } + + // bug 99191 - put out an error message if the type doesn't exist + /** + * Report an error if the reason a "declare @method/ctor/field" was not used was because the member specified does not exist. + * This method is passed some set of declare statements that didn't match and a flag indicating whether the set contains declare @field + * or declare @method/ctor entries. + */ + private void checkUnusedDeclareAts(Set unusedDecaTs, boolean isDeclareAtField) { + for (DeclareAnnotation declA : unusedDecaTs) { + + // Error if an exact type pattern was specified + boolean shouldCheck = declA.isExactPattern() || declA.getSignaturePattern().getExactDeclaringTypes().size() != 0; + + if (shouldCheck && declA.getKind() != DeclareAnnotation.AT_CONSTRUCTOR) { + if (declA.getSignaturePattern().isMatchOnAnyName()) { + shouldCheck = false; + } else { + List declaringTypePatterns = declA.getSignaturePattern().getExactDeclaringTypes(); + if (declaringTypePatterns.size() == 0) { + shouldCheck = false; + } else { + for (ExactTypePattern exactTypePattern : declaringTypePatterns) { + if (exactTypePattern.isIncludeSubtypes()) { + shouldCheck = false; + break; + } + } + } + } + } + if (shouldCheck) { + // Quickly check if an ITD supplies the 'missing' member + boolean itdMatch = false; + List lst = clazz.getType().getInterTypeMungers(); + for (Iterator iterator = lst.iterator(); iterator.hasNext() && !itdMatch;) { + ConcreteTypeMunger element = iterator.next(); + if (element.getMunger() instanceof NewFieldTypeMunger) { + NewFieldTypeMunger nftm = (NewFieldTypeMunger) element.getMunger(); + itdMatch = declA.matches(nftm.getSignature(), world); + } else if (element.getMunger() instanceof NewMethodTypeMunger) { + NewMethodTypeMunger nmtm = (NewMethodTypeMunger) element.getMunger(); + itdMatch = declA.matches(nmtm.getSignature(), world); + } else if (element.getMunger() instanceof NewConstructorTypeMunger) { + NewConstructorTypeMunger nctm = (NewConstructorTypeMunger) element.getMunger(); + itdMatch = declA.matches(nctm.getSignature(), world); + } + } + if (!itdMatch) { + IMessage message = null; + if (isDeclareAtField) { + message = new Message("The field '" + declA.getSignaturePattern().toString() + "' does not exist", + declA.getSourceLocation(), true); + } else { + message = new Message("The method '" + declA.getSignaturePattern().toString() + "' does not exist", + declA.getSourceLocation(), true); + } + world.getMessageHandler().handleMessage(message); + } + } + } + } + + // TAG: WeavingMessage + private void reportFieldAnnotationWeavingMessage(LazyClassGen clazz, BcelField theField, DeclareAnnotation decaf, + boolean isRemove) { + if (!getWorld().getMessageHandler().isIgnoring(IMessage.WEAVEINFO)) { + world.getMessageHandler().handleMessage( + WeaveMessage.constructWeavingMessage( + isRemove ? WeaveMessage.WEAVEMESSAGE_REMOVES_ANNOTATION : WeaveMessage.WEAVEMESSAGE_ANNOTATES, + new String[] { theField.getFieldAsIs().toString() + "' of type '" + clazz.getName(), + clazz.getFileName(), decaf.getAnnotationString(), "field", decaf.getAspect().toString(), + Utility.beautifyLocation(decaf.getSourceLocation()) })); + } + } + + /** + * Check if a resolved member (field/method/ctor) already has an annotation, if it does then put out a warning and return true + */ + private boolean doesAlreadyHaveAnnotation(ResolvedMember rm, DeclareAnnotation deca, List reportedProblems, boolean reportError) { + if (rm.hasAnnotation(deca.getAnnotationType())) { + if (reportError && world.getLint().elementAlreadyAnnotated.isEnabled()) { + Integer uniqueID = new Integer(rm.hashCode() * deca.hashCode()); + if (!reportedProblems.contains(uniqueID)) { + reportedProblems.add(uniqueID); + world.getLint().elementAlreadyAnnotated.signal(new String[] { rm.toString(), + deca.getAnnotationType().toString() }, rm.getSourceLocation(), + new ISourceLocation[] { deca.getSourceLocation() }); + } + } + return true; + } + return false; + } + + private boolean doesAlreadyHaveAnnotation(LazyMethodGen rm, ResolvedMember itdfieldsig, DeclareAnnotation deca, + List reportedProblems) { + if (rm != null && rm.hasAnnotation(deca.getAnnotationType())) { + if (world.getLint().elementAlreadyAnnotated.isEnabled()) { + Integer uniqueID = new Integer(rm.hashCode() * deca.hashCode()); + if (!reportedProblems.contains(uniqueID)) { + reportedProblems.add(uniqueID); + reportedProblems.add(new Integer(itdfieldsig.hashCode() * deca.hashCode())); + world.getLint().elementAlreadyAnnotated.signal(new String[] { itdfieldsig.toString(), + deca.getAnnotationType().toString() }, rm.getSourceLocation(), + new ISourceLocation[] { deca.getSourceLocation() }); + } + } + return true; + } + return false; + } + + private Set findAspectsForMungers(LazyMethodGen mg) { + Set aspectsAffectingType = new HashSet(); + for (BcelShadow shadow : mg.matchedShadows) { + for (ShadowMunger munger : shadow.getMungers()) { + if (munger instanceof BcelAdvice) { + BcelAdvice bcelAdvice = (BcelAdvice) munger; + if (bcelAdvice.getConcreteAspect() != null) { + aspectsAffectingType.add(bcelAdvice.getConcreteAspect().getSignature()); + } + } else { + // It is a 'Checker' - we don't need to remember aspects + // that only contributed Checkers... + } + } + } + return aspectsAffectingType; + } + + private boolean inlineSelfConstructors(List methodGens, List recursiveCtors) { + boolean inlinedSomething = false; + List newRecursiveCtors = new ArrayList(); + for (LazyMethodGen methodGen : methodGens) { + if (!methodGen.getName().equals("")) { + continue; + } + InstructionHandle ih = findSuperOrThisCall(methodGen); + if (ih != null && isThisCall(ih)) { + LazyMethodGen donor = getCalledMethod(ih); + if (donor.equals(methodGen)) { + newRecursiveCtors.add(donor); + } else { + if (!recursiveCtors.contains(donor)) { + inlineMethod(donor, methodGen, ih); + inlinedSomething = true; + } + } + } + } + recursiveCtors.addAll(newRecursiveCtors); + return inlinedSomething; + } + + private void positionAndImplement(List initializationShadows) { + for (BcelShadow s : initializationShadows) { + positionInitializationShadow(s); + // s.getEnclosingMethod().print(); + s.implement(); + } + } + + private void positionInitializationShadow(BcelShadow s) { + LazyMethodGen mg = s.getEnclosingMethod(); + InstructionHandle call = findSuperOrThisCall(mg); + InstructionList body = mg.getBody(); + ShadowRange r = new ShadowRange(body); + r.associateWithShadow(s); + if (s.getKind() == Shadow.PreInitialization) { + // XXX assert first instruction is an ALOAD_0. + // a pre shadow goes from AFTER the first instruction (which we + // believe to + // be an ALOAD_0) to just before the call to super + r.associateWithTargets(Range.genStart(body, body.getStart().getNext()), Range.genEnd(body, call.getPrev())); + } else { + // assert s.getKind() == Shadow.Initialization + r.associateWithTargets(Range.genStart(body, call.getNext()), Range.genEnd(body)); + } + } + + private boolean isThisCall(InstructionHandle ih) { + InvokeInstruction inst = (InvokeInstruction) ih.getInstruction(); + return inst.getClassName(cpg).equals(clazz.getName()); + } + + /** + * inline a particular call in bytecode. + * + * @param donor the method we want to inline + * @param recipient the method containing the call we want to inline + * @param call the instructionHandle in recipient's body holding the call we want to inline. + */ + public static void inlineMethod(LazyMethodGen donor, LazyMethodGen recipient, InstructionHandle call) { + // assert recipient.contains(call) + + /* + * Implementation notes: + * + * We allocate two slots for every tempvar so we don't screw up longs and doubles which may share space. This could be + * conservatively avoided (no reference to a long/double instruction, don't do it) or packed later. Right now we don't + * bother to pack. + * + * Allocate a new var for each formal param of the inlined. Fill with stack contents. Then copy the inlined instructions in + * with the appropriate remap table. Any framelocs used by locals in inlined are reallocated to top of frame, + */ + final InstructionFactory fact = recipient.getEnclosingClass().getFactory(); + + IntMap frameEnv = new IntMap(); + + // this also sets up the initial environment + InstructionList argumentStores = genArgumentStores(donor, recipient, frameEnv, fact); + + InstructionList inlineInstructions = genInlineInstructions(donor, recipient, frameEnv, fact, false); + + inlineInstructions.insert(argumentStores); + + recipient.getBody().append(call, inlineInstructions); + Utility.deleteInstruction(call, recipient); + } + + // public BcelVar genTempVar(UnresolvedType typeX) { + // return new BcelVar(typeX.resolve(world), + // genTempVarIndex(typeX.getSize())); + // } + // + // private int genTempVarIndex(int size) { + // return enclosingMethod.allocateLocal(size); + // } + + /** + * Input method is a synchronized method, we remove the bit flag for synchronized and then insert a try..finally block + * + * Some jumping through firey hoops required - depending on the input code level (1.5 or not) we may or may not be able to use + * the LDC instruction that takes a class literal (doesnt on <1.5). + * + * FIXME asc Before promoting -Xjoinpoints:synchronization to be a standard option, this needs a bunch of tidying up - there is + * some duplication that can be removed. + */ + public static void transformSynchronizedMethod(LazyMethodGen synchronizedMethod) { + if (trace.isTraceEnabled()) { + trace.enter("transformSynchronizedMethod", synchronizedMethod); + } + // System.err.println("DEBUG: Transforming synchronized method: "+ + // synchronizedMethod.getName()); + final InstructionFactory fact = synchronizedMethod.getEnclosingClass().getFactory(); + InstructionList body = synchronizedMethod.getBody(); + InstructionList prepend = new InstructionList(); + Type enclosingClassType = BcelWorld.makeBcelType(synchronizedMethod.getEnclosingClass().getType()); + + // STATIC METHOD TRANSFORMATION + if (synchronizedMethod.isStatic()) { + + // What to do here depends on the level of the class file! + // LDC can handle class literals in Java5 and above *sigh* + if (synchronizedMethod.getEnclosingClass().isAtLeastJava5()) { + // MONITORENTER logic: + // 0: ldc #2; //class C + // 2: dup + // 3: astore_0 + // 4: monitorenter + int slotForLockObject = synchronizedMethod.allocateLocal(enclosingClassType); + prepend.append(fact.createConstant(enclosingClassType)); + prepend.append(InstructionFactory.createDup(1)); + prepend.append(InstructionFactory.createStore(enclosingClassType, slotForLockObject)); + prepend.append(InstructionFactory.MONITORENTER); + + // MONITOREXIT logic: + + // We basically need to wrap the code from the method in a + // finally block that + // will ensure monitorexit is called. Content on the finally + // block seems to + // be always: + // + // E1: ALOAD_1 + // MONITOREXIT + // ATHROW + // + // so lets build that: + InstructionList finallyBlock = new InstructionList(); + finallyBlock.append(InstructionFactory.createLoad(Type.getType(java.lang.Class.class), slotForLockObject)); + finallyBlock.append(InstructionConstants.MONITOREXIT); + finallyBlock.append(InstructionConstants.ATHROW); + + // finally -> E1 + // | GETSTATIC java.lang.System.out Ljava/io/PrintStream; (line + // 21) + // | LDC "hello" + // | INVOKEVIRTUAL java.io.PrintStream.println + // (Ljava/lang/String;)V + // | ALOAD_1 (line 20) + // | MONITOREXIT + // finally -> E1 + // GOTO L0 + // finally -> E1 + // | E1: ALOAD_1 + // | MONITOREXIT + // finally -> E1 + // ATHROW + // L0: RETURN (line 23) + + // search for 'returns' and make them jump to the + // aload_,monitorexit + InstructionHandle walker = body.getStart(); + List rets = new ArrayList(); + while (walker != null) { + if (walker.getInstruction().isReturnInstruction()) { + rets.add(walker); + } + walker = walker.getNext(); + } + if (!rets.isEmpty()) { + // need to ensure targeters for 'return' now instead target + // the load instruction + // (so we never jump over the monitorexit logic) + + for (Iterator iter = rets.iterator(); iter.hasNext();) { + InstructionHandle element = iter.next(); + InstructionList monitorExitBlock = new InstructionList(); + monitorExitBlock.append(InstructionFactory.createLoad(enclosingClassType, slotForLockObject)); + monitorExitBlock.append(InstructionConstants.MONITOREXIT); + // monitorExitBlock.append(Utility.copyInstruction(element + // .getInstruction())); + // element.setInstruction(InstructionFactory.createLoad( + // classType,slotForThis)); + InstructionHandle monitorExitBlockStart = body.insert(element, monitorExitBlock); + + // now move the targeters from the RET to the start of + // the monitorexit block + for (InstructionTargeter targeter : element.getTargetersCopy()) { + // what kinds are there? + if (targeter instanceof LocalVariableTag) { + // ignore + } else if (targeter instanceof LineNumberTag) { + // ignore + // } else if (targeter instanceof + // InstructionBranch && + // ((InstructionBranch)targeter).isGoto()) { + // // move it... + // targeter.updateTarget(element, + // monitorExitBlockStart); + } else if (targeter instanceof InstructionBranch) { + // move it + targeter.updateTarget(element, monitorExitBlockStart); + } else { + throw new BCException("Unexpected targeter encountered during transform: " + targeter); + } + } + } + } + + // now the magic, putting the finally block around the code + InstructionHandle finallyStart = finallyBlock.getStart(); + + InstructionHandle tryPosition = body.getStart(); + InstructionHandle catchPosition = body.getEnd(); + body.insert(body.getStart(), prepend); // now we can put the + // monitorenter stuff on + synchronizedMethod.getBody().append(finallyBlock); + synchronizedMethod.addExceptionHandler(tryPosition, catchPosition, finallyStart, null/* ==finally */, false); + synchronizedMethod.addExceptionHandler(finallyStart, finallyStart.getNext(), finallyStart, null, false); + } else { + + // TRANSFORMING STATIC METHOD ON PRE JAVA5 + + // Hideous nightmare, class literal references prior to Java5 + + // YIKES! this is just the code for MONITORENTER ! + // 0: getstatic #59; //Field class$1:Ljava/lang/Class; + // 3: dup + // 4: ifnonnull 32 + // 7: pop + // try + // 8: ldc #61; //String java.lang.String + // 10: invokestatic #44; //Method + // java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class; + // 13: dup + // catch + // 14: putstatic #59; //Field class$1:Ljava/lang/Class; + // 17: goto 32 + // 20: new #46; //class java/lang/NoClassDefFoundError + // 23: dup_x1 + // 24: swap + // 25: invokevirtual #52; //Method + // java/lang/Throwable.getMessage:()Ljava/lang/String; + // 28: invokespecial #54; //Method + // java/lang/NoClassDefFoundError."":(Ljava/lang/String;)V + // 31: athrow + // 32: dup <-- partTwo (branch target) + // 33: astore_0 + // 34: monitorenter + // + // plus exceptiontable entry! + // 8 13 20 Class java/lang/ClassNotFoundException + Type classType = BcelWorld.makeBcelType(synchronizedMethod.getEnclosingClass().getType()); + Type clazzType = Type.getType(Class.class); + + InstructionList parttwo = new InstructionList(); + parttwo.append(InstructionFactory.createDup(1)); + int slotForThis = synchronizedMethod.allocateLocal(classType); + parttwo.append(InstructionFactory.createStore(clazzType, slotForThis)); // ? should be the real type ? String or + // something? + parttwo.append(InstructionFactory.MONITORENTER); + + String fieldname = synchronizedMethod.getEnclosingClass().allocateField("class$"); + FieldGen f = new FieldGen(Modifier.STATIC | Modifier.PRIVATE, Type.getType(Class.class), fieldname, + synchronizedMethod.getEnclosingClass().getConstantPool()); + synchronizedMethod.getEnclosingClass().addField(f, null); + + // 10: invokestatic #44; //Method + // java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class; + // 13: dup + // 14: putstatic #59; //Field class$1:Ljava/lang/Class; + // 17: goto 32 + // 20: new #46; //class java/lang/NoClassDefFoundError + // 23: dup_x1 + // 24: swap + // 25: invokevirtual #52; //Method + // java/lang/Throwable.getMessage:()Ljava/lang/String; + // 28: invokespecial #54; //Method + // java/lang/NoClassDefFoundError."":(Ljava/lang/String;)V + // 31: athrow + String name = synchronizedMethod.getEnclosingClass().getName(); + + prepend.append(fact.createGetStatic(name, fieldname, Type.getType(Class.class))); + prepend.append(InstructionFactory.createDup(1)); + prepend.append(InstructionFactory.createBranchInstruction(Constants.IFNONNULL, parttwo.getStart())); + prepend.append(InstructionFactory.POP); + + prepend.append(fact.createConstant(name)); + InstructionHandle tryInstruction = prepend.getEnd(); + prepend.append(fact.createInvoke("java.lang.Class", "forName", clazzType, + new Type[] { Type.getType(String.class) }, Constants.INVOKESTATIC)); + InstructionHandle catchInstruction = prepend.getEnd(); + prepend.append(InstructionFactory.createDup(1)); + + prepend.append(fact.createPutStatic(synchronizedMethod.getEnclosingClass().getType().getName(), fieldname, + Type.getType(Class.class))); + prepend.append(InstructionFactory.createBranchInstruction(Constants.GOTO, parttwo.getStart())); + + // start of catch block + InstructionList catchBlockForLiteralLoadingFail = new InstructionList(); + catchBlockForLiteralLoadingFail.append(fact.createNew((ObjectType) Type.getType(NoClassDefFoundError.class))); + catchBlockForLiteralLoadingFail.append(InstructionFactory.createDup_1(1)); + catchBlockForLiteralLoadingFail.append(InstructionFactory.SWAP); + catchBlockForLiteralLoadingFail.append(fact.createInvoke("java.lang.Throwable", "getMessage", + Type.getType(String.class), new Type[] {}, Constants.INVOKEVIRTUAL)); + catchBlockForLiteralLoadingFail.append(fact.createInvoke("java.lang.NoClassDefFoundError", "", Type.VOID, + new Type[] { Type.getType(String.class) }, Constants.INVOKESPECIAL)); + catchBlockForLiteralLoadingFail.append(InstructionFactory.ATHROW); + InstructionHandle catchBlockStart = catchBlockForLiteralLoadingFail.getStart(); + prepend.append(catchBlockForLiteralLoadingFail); + prepend.append(parttwo); + // MONITORENTER + // pseudocode: load up 'this' (var0), dup it, store it in a new + // local var (for use with monitorexit) and call + // monitorenter: + // ALOAD_0, DUP, ASTORE_, MONITORENTER + // prepend.append(InstructionFactory.createLoad(classType,0)); + // prepend.append(InstructionFactory.createDup(1)); + // int slotForThis = + // synchronizedMethod.allocateLocal(classType); + // prepend.append(InstructionFactory.createStore(classType, + // slotForThis)); + // prepend.append(InstructionFactory.MONITORENTER); + + // MONITOREXIT + // here be dragons + + // We basically need to wrap the code from the method in a + // finally block that + // will ensure monitorexit is called. Content on the finally + // block seems to + // be always: + // + // E1: ALOAD_1 + // MONITOREXIT + // ATHROW + // + // so lets build that: + InstructionList finallyBlock = new InstructionList(); + finallyBlock.append(InstructionFactory.createLoad(Type.getType(java.lang.Class.class), slotForThis)); + finallyBlock.append(InstructionConstants.MONITOREXIT); + finallyBlock.append(InstructionConstants.ATHROW); + + // finally -> E1 + // | GETSTATIC java.lang.System.out Ljava/io/PrintStream; (line + // 21) + // | LDC "hello" + // | INVOKEVIRTUAL java.io.PrintStream.println + // (Ljava/lang/String;)V + // | ALOAD_1 (line 20) + // | MONITOREXIT + // finally -> E1 + // GOTO L0 + // finally -> E1 + // | E1: ALOAD_1 + // | MONITOREXIT + // finally -> E1 + // ATHROW + // L0: RETURN (line 23) + // frameEnv.put(donorFramePos, thisSlot); + + // search for 'returns' and make them to the + // aload_,monitorexit + InstructionHandle walker = body.getStart(); + List rets = new ArrayList(); + while (walker != null) { // !walker.equals(body.getEnd())) { + if (walker.getInstruction().isReturnInstruction()) { + rets.add(walker); + } + walker = walker.getNext(); + } + if (rets.size() > 0) { + // need to ensure targeters for 'return' now instead target + // the load instruction + // (so we never jump over the monitorexit logic) + + for (InstructionHandle ret : rets) { + // System.err.println("Adding monitor exit block at "+ + // element); + InstructionList monitorExitBlock = new InstructionList(); + monitorExitBlock.append(InstructionFactory.createLoad(classType, slotForThis)); + monitorExitBlock.append(InstructionConstants.MONITOREXIT); + // monitorExitBlock.append(Utility.copyInstruction(element + // .getInstruction())); + // element.setInstruction(InstructionFactory.createLoad( + // classType,slotForThis)); + InstructionHandle monitorExitBlockStart = body.insert(ret, monitorExitBlock); + + // now move the targeters from the RET to the start of + // the monitorexit block + for (InstructionTargeter targeter : ret.getTargetersCopy()) { + // what kinds are there? + if (targeter instanceof LocalVariableTag) { + // ignore + } else if (targeter instanceof LineNumberTag) { + // ignore + // } else if (targeter instanceof GOTO || + // targeter instanceof GOTO_W) { + // // move it... + // targeter.updateTarget(element, + // monitorExitBlockStart); + } else if (targeter instanceof InstructionBranch) { + // move it + targeter.updateTarget(ret, monitorExitBlockStart); + } else { + throw new BCException("Unexpected targeter encountered during transform: " + targeter); + } + } + } + } + // body = + // rewriteWithMonitorExitCalls(body,fact,true,slotForThis, + // classType); + // synchronizedMethod.setBody(body); + + // now the magic, putting the finally block around the code + InstructionHandle finallyStart = finallyBlock.getStart(); + + InstructionHandle tryPosition = body.getStart(); + InstructionHandle catchPosition = body.getEnd(); + body.insert(body.getStart(), prepend); // now we can put the + // monitorenter stuff on + + synchronizedMethod.getBody().append(finallyBlock); + synchronizedMethod.addExceptionHandler(tryPosition, catchPosition, finallyStart, null/* ==finally */, false); + synchronizedMethod.addExceptionHandler(tryInstruction, catchInstruction, catchBlockStart, + (ObjectType) Type.getType(ClassNotFoundException.class), true); + synchronizedMethod.addExceptionHandler(finallyStart, finallyStart.getNext(), finallyStart, null, false); + } + } else { + + // TRANSFORMING NON STATIC METHOD + Type classType = BcelWorld.makeBcelType(synchronizedMethod.getEnclosingClass().getType()); + // MONITORENTER + // pseudocode: load up 'this' (var0), dup it, store it in a new + // local var (for use with monitorexit) and call + // monitorenter: + // ALOAD_0, DUP, ASTORE_, MONITORENTER + prepend.append(InstructionFactory.createLoad(classType, 0)); + prepend.append(InstructionFactory.createDup(1)); + int slotForThis = synchronizedMethod.allocateLocal(classType); + prepend.append(InstructionFactory.createStore(classType, slotForThis)); + prepend.append(InstructionFactory.MONITORENTER); + // body.insert(body.getStart(),prepend); + + // MONITOREXIT + + // We basically need to wrap the code from the method in a finally + // block that + // will ensure monitorexit is called. Content on the finally block + // seems to + // be always: + // + // E1: ALOAD_1 + // MONITOREXIT + // ATHROW + // + // so lets build that: + InstructionList finallyBlock = new InstructionList(); + finallyBlock.append(InstructionFactory.createLoad(classType, slotForThis)); + finallyBlock.append(InstructionConstants.MONITOREXIT); + finallyBlock.append(InstructionConstants.ATHROW); + + // finally -> E1 + // | GETSTATIC java.lang.System.out Ljava/io/PrintStream; (line 21) + // | LDC "hello" + // | INVOKEVIRTUAL java.io.PrintStream.println (Ljava/lang/String;)V + // | ALOAD_1 (line 20) + // | MONITOREXIT + // finally -> E1 + // GOTO L0 + // finally -> E1 + // | E1: ALOAD_1 + // | MONITOREXIT + // finally -> E1 + // ATHROW + // L0: RETURN (line 23) + // frameEnv.put(donorFramePos, thisSlot); + + // search for 'returns' and make them to the aload_,monitorexit + InstructionHandle walker = body.getStart(); + List rets = new ArrayList(); + while (walker != null) { // !walker.equals(body.getEnd())) { + if (walker.getInstruction().isReturnInstruction()) { + rets.add(walker); + } + walker = walker.getNext(); + } + if (!rets.isEmpty()) { + // need to ensure targeters for 'return' now instead target the + // load instruction + // (so we never jump over the monitorexit logic) + + for (Iterator iter = rets.iterator(); iter.hasNext();) { + InstructionHandle element = iter.next(); + // System.err.println("Adding monitor exit block at "+element + // ); + InstructionList monitorExitBlock = new InstructionList(); + monitorExitBlock.append(InstructionFactory.createLoad(classType, slotForThis)); + monitorExitBlock.append(InstructionConstants.MONITOREXIT); + // monitorExitBlock.append(Utility.copyInstruction(element. + // getInstruction())); + // element.setInstruction(InstructionFactory.createLoad( + // classType,slotForThis)); + InstructionHandle monitorExitBlockStart = body.insert(element, monitorExitBlock); + + // now move the targeters from the RET to the start of the + // monitorexit block + for (InstructionTargeter targeter : element.getTargetersCopy()) { + // what kinds are there? + if (targeter instanceof LocalVariableTag) { + // ignore + } else if (targeter instanceof LineNumberTag) { + // ignore + // } else if (targeter instanceof GOTO || + // targeter instanceof GOTO_W) { + // // move it... + // targeter.updateTarget(element, + // monitorExitBlockStart); + } else if (targeter instanceof InstructionBranch) { + // move it + targeter.updateTarget(element, monitorExitBlockStart); + } else { + throw new BCException("Unexpected targeter encountered during transform: " + targeter); + } + } + } + } + + // now the magic, putting the finally block around the code + InstructionHandle finallyStart = finallyBlock.getStart(); + + InstructionHandle tryPosition = body.getStart(); + InstructionHandle catchPosition = body.getEnd(); + body.insert(body.getStart(), prepend); // now we can put the + // monitorenter stuff on + synchronizedMethod.getBody().append(finallyBlock); + synchronizedMethod.addExceptionHandler(tryPosition, catchPosition, finallyStart, null/* ==finally */, false); + synchronizedMethod.addExceptionHandler(finallyStart, finallyStart.getNext(), finallyStart, null, false); + // also the exception handling for the finally block jumps to itself + + // max locals will already have been modified in the allocateLocal() + // call + + // synchronized bit is removed on LazyMethodGen.pack() + } + + // gonna have to go through and change all aload_0s to load the var from + // a variable, + // going to add a new variable for the this var + + if (trace.isTraceEnabled()) { + trace.exit("transformSynchronizedMethod"); + } + } + + /** + * generate the instructions to be inlined. + * + * @param donor the method from which we will copy (and adjust frame and jumps) instructions. + * @param recipient the method the instructions will go into. Used to get the frame size so we can allocate new frame locations + * for locals in donor. + * @param frameEnv an environment to map from donor frame to recipient frame, initially populated with argument locations. + * @param fact an instruction factory for recipient + */ + static InstructionList genInlineInstructions(LazyMethodGen donor, LazyMethodGen recipient, IntMap frameEnv, + InstructionFactory fact, boolean keepReturns) { + InstructionList footer = new InstructionList(); + InstructionHandle end = footer.append(InstructionConstants.NOP); + + InstructionList ret = new InstructionList(); + InstructionList sourceList = donor.getBody(); + + Map srcToDest = new HashMap(); + ConstantPool donorCpg = donor.getEnclosingClass().getConstantPool(); + ConstantPool recipientCpg = recipient.getEnclosingClass().getConstantPool(); + + boolean isAcrossClass = donorCpg != recipientCpg; + BootstrapMethods bootstrapMethods = null; + // first pass: copy the instructions directly, populate the srcToDest + // map, + // fix frame instructions + for (InstructionHandle src = sourceList.getStart(); src != null; src = src.getNext()) { + Instruction fresh = Utility.copyInstruction(src.getInstruction()); + InstructionHandle dest; + + // OPTIMIZE optimize this stuff? + if (fresh.isConstantPoolInstruction()) { + // need to reset index to go to new constant pool. This is totally + // a computation leak... we're testing this LOTS of times. Sigh. + if (isAcrossClass) { + InstructionCP cpi = (InstructionCP) fresh; + cpi.setIndex(recipientCpg.addConstant(donorCpg.getConstant(cpi.getIndex()), donorCpg)); + } + // May need to copy bootstrapmethods across too. +// if (fresh instanceof InvokeDynamic) { +// InvokeDynamic id = (InvokeDynamic)fresh; +// ConstantInvokeDynamic cid = (ConstantInvokeDynamic)donorCpg.getConstant(src.getInstruction().getIndex()); +// int bmaIndex = cid.getBootstrapMethodAttrIndex(); +// if (bootstrapMethods == null) { +// Collection attributes = donor.getEnclosingClass().getAttributes(); +// if (attributes != null) { +// for (Attribute attribute: attributes) { +// if (attribute instanceof BootstrapMethods) { +// bootstrapMethods = (BootstrapMethods)attribute; +// } +// } +// } +// BootstrapMethods.BootstrapMethod bootstrapMethod = +// bootstrapMethods.getBootstrapMethods()[bmaIndex]; +// ConstantMethodHandle methodhandle = (ConstantMethodHandle)donorCpg.getConstant(bootstrapMethod.getBootstrapMethodRef()); +// int bootstrapMethodArguments[] = bootstrapMethod.getBootstrapArguments(); +// +// // Finally have all we need to build the new one... +// +// int newMethodHandleIndex = recipientCpg.addConstant(methodhandle, donorCpg); +// int[] newMethodArguments = new int[bootstrapMethodArguments.length]; +// for (int a=0; a newAttributes = recipient.getEnclosingClass().getAttributes(); +// BootstrapMethods newBootstrapMethods = null; +// for (Attribute attr: newAttributes) { +// if (attr instanceof BootstrapMethods) { +// newBootstrapMethods = (BootstrapMethods)newBootstrapMethods; +// } +// } +// if (newBootstrapMethods == null) { +// newBootstrapMethods = +// new BootstrapMethods(recipientCpg.addUtf8("BootstrapMethods"), +// 2+newBootstrapMethod.getLength(), +// new BootstrapMethods.BootstrapMethod[] {newBootstrapMethod}, +// recipientCpg); +// recipient.getEnclosingClass().addAttribute(newBootstrapMethods); +// } +// TODO need to copy over lambda$0 support methods too... +// } +// +// } + } + if (src.getInstruction() == Range.RANGEINSTRUCTION) { + dest = ret.append(Range.RANGEINSTRUCTION); + } else if (fresh.isReturnInstruction()) { + if (keepReturns) { + dest = ret.append(fresh); + } else { + dest = ret.append(InstructionFactory.createBranchInstruction(Constants.GOTO, end)); + } + } else if (fresh instanceof InstructionBranch) { + dest = ret.append((InstructionBranch) fresh); + } else if (fresh.isLocalVariableInstruction() || fresh instanceof RET) { + + // IndexedInstruction indexed = (IndexedInstruction) fresh; + int oldIndex = fresh.getIndex(); + int freshIndex; + if (!frameEnv.hasKey(oldIndex)) { + freshIndex = recipient.allocateLocal(2); + frameEnv.put(oldIndex, freshIndex); + } else { + freshIndex = frameEnv.get(oldIndex); + } + if (fresh instanceof RET) { + fresh.setIndex(freshIndex); + } else { + fresh = ((InstructionLV) fresh).setIndexAndCopyIfNecessary(freshIndex); + } + dest = ret.append(fresh); + } else { + dest = ret.append(fresh); + } + srcToDest.put(src, dest); + } + + // second pass: retarget branch instructions, copy ranges and tags + Map tagMap = new HashMap(); + Map shadowMap = new HashMap(); + for (InstructionHandle dest = ret.getStart(), src = sourceList.getStart(); dest != null; dest = dest.getNext(), src = src + .getNext()) { + Instruction inst = dest.getInstruction(); + + // retarget branches + if (inst instanceof InstructionBranch) { + InstructionBranch branch = (InstructionBranch) inst; + InstructionHandle oldTarget = branch.getTarget(); + InstructionHandle newTarget = srcToDest.get(oldTarget); + if (newTarget == null) { + // assert this is a GOTO + // this was a return instruction we previously replaced + } else { + branch.setTarget(newTarget); + if (branch instanceof InstructionSelect) { + InstructionSelect select = (InstructionSelect) branch; + InstructionHandle[] oldTargets = select.getTargets(); + for (int k = oldTargets.length - 1; k >= 0; k--) { + select.setTarget(k, srcToDest.get(oldTargets[k])); + } + } + } + } + + // copy over tags and range attributes + + Iterator tIter = src.getTargeters().iterator(); + while (tIter.hasNext()) { + InstructionTargeter old = tIter.next(); + if (old instanceof Tag) { + Tag oldTag = (Tag) old; + Tag fresh = tagMap.get(oldTag); + if (fresh == null) { + fresh = oldTag.copy(); + if (old instanceof LocalVariableTag) { + // LocalVariable + LocalVariableTag lvTag = (LocalVariableTag) old; + LocalVariableTag lvTagFresh = (LocalVariableTag) fresh; + if (lvTag.getSlot() == 0) { + fresh = new LocalVariableTag(lvTag.getRealType().getSignature(), "ajc$aspectInstance", + frameEnv.get(lvTag.getSlot()), 0); + } else { + // // Do not move it - when copying the code from the aspect to the affected target, 'this' is + // // going to change from aspect to affected type. So just fix the type + // System.out.println("For local variable tag at instruction " + src + " changing slot from " + // + lvTag.getSlot() + " > " + frameEnv.get(lvTag.getSlot())); + lvTagFresh.updateSlot(frameEnv.get(lvTag.getSlot())); + } + } + tagMap.put(oldTag, fresh); + } + dest.addTargeter(fresh); + } else if (old instanceof ExceptionRange) { + ExceptionRange er = (ExceptionRange) old; + if (er.getStart() == src) { + ExceptionRange freshEr = new ExceptionRange(recipient.getBody(), er.getCatchType(), er.getPriority()); + freshEr.associateWithTargets(dest, srcToDest.get(er.getEnd()), srcToDest.get(er.getHandler())); + } + } else if (old instanceof ShadowRange) { + ShadowRange oldRange = (ShadowRange) old; + if (oldRange.getStart() == src) { + BcelShadow oldShadow = oldRange.getShadow(); + BcelShadow freshEnclosing = oldShadow.getEnclosingShadow() == null ? null : (BcelShadow) shadowMap + .get(oldShadow.getEnclosingShadow()); + BcelShadow freshShadow = oldShadow.copyInto(recipient, freshEnclosing); + ShadowRange freshRange = new ShadowRange(recipient.getBody()); + freshRange.associateWithShadow(freshShadow); + freshRange.associateWithTargets(dest, srcToDest.get(oldRange.getEnd())); + shadowMap.put(oldShadow, freshShadow); // oldRange, freshRange + // recipient.matchedShadows.add(freshShadow); + // XXX should go through the NEW copied shadow and + // update + // the thisVar, targetVar, and argsVar + // ??? Might want to also go through at this time and + // add + // "extra" vars to the shadow. + } + } + } + } + if (!keepReturns) { + ret.append(footer); + } + return ret; + } + + // static InstructionList rewriteWithMonitorExitCalls(InstructionList + // sourceList,InstructionFactory fact,boolean keepReturns,int + // monitorVarSlot,Type monitorVarType) + // { + // InstructionList footer = new InstructionList(); + // InstructionHandle end = footer.append(InstructionConstants.NOP); + // + // InstructionList newList = new InstructionList(); + // + // Map srcToDest = new HashMap(); + // + // // first pass: copy the instructions directly, populate the srcToDest + // map, + // // fix frame instructions + // for (InstructionHandle src = sourceList.getStart(); src != null; src = + // src.getNext()) { + // Instruction fresh = Utility.copyInstruction(src.getInstruction()); + // InstructionHandle dest; + // if (src.getInstruction() == Range.RANGEINSTRUCTION) { + // dest = newList.append(Range.RANGEINSTRUCTION); + // } else if (fresh.isReturnInstruction()) { + // if (keepReturns) { + // newList.append(InstructionFactory.createLoad(monitorVarType,monitorVarSlot + // )); + // newList.append(InstructionConstants.MONITOREXIT); + // dest = newList.append(fresh); + // } else { + // dest = + // newList.append(InstructionFactory.createBranchInstruction(Constants.GOTO, + // end)); + // } + // } else if (fresh instanceof InstructionBranch) { + // dest = newList.append((InstructionBranch) fresh); + // } else if ( + // fresh.isLocalVariableInstruction() || fresh instanceof RET) { + // //IndexedInstruction indexed = (IndexedInstruction) fresh; + // int oldIndex = fresh.getIndex(); + // int freshIndex; + // // if (!frameEnv.hasKey(oldIndex)) { + // // freshIndex = recipient.allocateLocal(2); + // // frameEnv.put(oldIndex, freshIndex); + // // } else { + // freshIndex = oldIndex;//frameEnv.get(oldIndex); + // // } + // if (fresh instanceof RET) { + // fresh.setIndex(freshIndex); + // } else { + // fresh = ((InstructionLV)fresh).setIndexAndCopyIfNecessary(freshIndex); + // } + // dest = newList.append(fresh); + // } else { + // dest = newList.append(fresh); + // } + // srcToDest.put(src, dest); + // } + // + // // second pass: retarget branch instructions, copy ranges and tags + // Map tagMap = new HashMap(); + // for (InstructionHandle dest = newList.getStart(), src = + // sourceList.getStart(); + // dest != null; + // dest = dest.getNext(), src = src.getNext()) { + // Instruction inst = dest.getInstruction(); + // + // // retarget branches + // if (inst instanceof InstructionBranch) { + // InstructionBranch branch = (InstructionBranch) inst; + // InstructionHandle oldTarget = branch.getTarget(); + // InstructionHandle newTarget = + // (InstructionHandle) srcToDest.get(oldTarget); + // if (newTarget == null) { + // // assert this is a GOTO + // // this was a return instruction we previously replaced + // } else { + // branch.setTarget(newTarget); + // if (branch instanceof InstructionSelect) { + // InstructionSelect select = (InstructionSelect) branch; + // InstructionHandle[] oldTargets = select.getTargets(); + // for (int k = oldTargets.length - 1; k >= 0; k--) { + // select.setTarget( + // k, + // (InstructionHandle) srcToDest.get(oldTargets[k])); + // } + // } + // } + // } + // + // //copy over tags and range attributes + // Iterator tIter = src.getTargeters().iterator(); + // + // while (tIter.hasNext()) { + // InstructionTargeter old = (InstructionTargeter)tIter.next(); + // if (old instanceof Tag) { + // Tag oldTag = (Tag) old; + // Tag fresh = (Tag) tagMap.get(oldTag); + // if (fresh == null) { + // fresh = oldTag.copy(); + // tagMap.put(oldTag, fresh); + // } + // dest.addTargeter(fresh); + // } else if (old instanceof ExceptionRange) { + // ExceptionRange er = (ExceptionRange) old; + // if (er.getStart() == src) { + // ExceptionRange freshEr = + // new ExceptionRange(newList/*recipient.getBody()*/,er.getCatchType(),er. + // getPriority()); + // freshEr.associateWithTargets( + // dest, + // (InstructionHandle)srcToDest.get(er.getEnd()), + // (InstructionHandle)srcToDest.get(er.getHandler())); + // } + // } + // /*else if (old instanceof ShadowRange) { + // ShadowRange oldRange = (ShadowRange) old; + // if (oldRange.getStart() == src) { + // BcelShadow oldShadow = oldRange.getShadow(); + // BcelShadow freshEnclosing = + // oldShadow.getEnclosingShadow() == null + // ? null + // : (BcelShadow) shadowMap.get(oldShadow.getEnclosingShadow()); + // BcelShadow freshShadow = + // oldShadow.copyInto(recipient, freshEnclosing); + // ShadowRange freshRange = new ShadowRange(recipient.getBody()); + // freshRange.associateWithShadow(freshShadow); + // freshRange.associateWithTargets( + // dest, + // (InstructionHandle) srcToDest.get(oldRange.getEnd())); + // shadowMap.put(oldRange, freshRange); + // //recipient.matchedShadows.add(freshShadow); + // // XXX should go through the NEW copied shadow and update + // // the thisVar, targetVar, and argsVar + // // ??? Might want to also go through at this time and add + // // "extra" vars to the shadow. + // } + // }*/ + // } + // } + // if (!keepReturns) newList.append(footer); + // return newList; + // } + + /** + * generate the argument stores in preparation for inlining. + * + * @param donor the method we will inline from. Used to get the signature. + * @param recipient the method we will inline into. Used to get the frame size so we can allocate fresh locations. + * @param frameEnv an empty environment we populate with a map from donor frame to recipient frame. + * @param fact an instruction factory for recipient + */ + private static InstructionList genArgumentStores(LazyMethodGen donor, LazyMethodGen recipient, IntMap frameEnv, + InstructionFactory fact) { + InstructionList ret = new InstructionList(); + + int donorFramePos = 0; + + // writing ret back to front because we're popping. + if (!donor.isStatic()) { + int targetSlot = recipient.allocateLocal(Type.OBJECT); + ret.insert(InstructionFactory.createStore(Type.OBJECT, targetSlot)); + frameEnv.put(donorFramePos, targetSlot); + donorFramePos += 1; + } + Type[] argTypes = donor.getArgumentTypes(); + for (int i = 0, len = argTypes.length; i < len; i++) { + Type argType = argTypes[i]; + int argSlot = recipient.allocateLocal(argType); + ret.insert(InstructionFactory.createStore(argType, argSlot)); + frameEnv.put(donorFramePos, argSlot); + donorFramePos += argType.getSize(); + } + return ret; + } + + /** + * get a called method: Assumes the called method is in this class, and the reference to it is exact (a la INVOKESPECIAL). + * + * @param ih The InvokeInstruction instructionHandle pointing to the called method. + */ + private LazyMethodGen getCalledMethod(InstructionHandle ih) { + InvokeInstruction inst = (InvokeInstruction) ih.getInstruction(); + + String methodName = inst.getName(cpg); + String signature = inst.getSignature(cpg); + + return clazz.getLazyMethodGen(methodName, signature); + } + + private void weaveInAddedMethods() { + Collections.sort(addedLazyMethodGens, new Comparator() { + public int compare(LazyMethodGen aa, LazyMethodGen bb) { + int i = aa.getName().compareTo(bb.getName()); + if (i != 0) { + return i; + } + return aa.getSignature().compareTo(bb.getSignature()); + } + }); + + for (LazyMethodGen addedMember : addedLazyMethodGens) { + clazz.addMethodGen(addedMember); + } + } + + // void addPerSingletonField(Member field) { + // ObjectType aspectType = (ObjectType) + // BcelWorld.makeBcelType(field.getReturnType()); + // String aspectName = field.getReturnType().getName(); + // + // LazyMethodGen clinit = clazz.getStaticInitializer(); + // InstructionList setup = new InstructionList(); + // InstructionFactory fact = clazz.getFactory(); + // + // setup.append(fact.createNew(aspectType)); + // setup.append(InstructionFactory.createDup(1)); + // setup.append(fact.createInvoke(aspectName, "", Type.VOID, new + // Type[0], Constants.INVOKESPECIAL)); + // setup.append(fact.createFieldAccess(aspectName, field.getName(), + // aspectType, Constants.PUTSTATIC)); + // clinit.getBody().insert(setup); + // } + + /** + * Returns null if this is not a Java constructor, and then we won't weave into it at all + */ + private InstructionHandle findSuperOrThisCall(LazyMethodGen mg) { + int depth = 1; + InstructionHandle start = mg.getBody().getStart(); + while (true) { + if (start == null) { + return null; + } + + Instruction inst = start.getInstruction(); + if (inst.opcode == Constants.INVOKESPECIAL && ((InvokeInstruction) inst).getName(cpg).equals("")) { + depth--; + if (depth == 0) { + return start; + } + } else if (inst.opcode == Constants.NEW) { + depth++; + } + start = start.getNext(); + } + } + + // ---- + + private boolean match(LazyMethodGen mg) { + BcelShadow enclosingShadow; + List shadowAccumulator = new ArrayList(); + boolean isOverweaving = world.isOverWeaving(); + boolean startsAngly = mg.getName().charAt(0) == '<'; + // we want to match ajsynthetic constructors... + if (startsAngly && mg.getName().equals("")) { + return matchInit(mg, shadowAccumulator); + } else if (!shouldWeaveBody(mg)) { + return false; + } else { + if (startsAngly && mg.getName().equals("")) { + // clinitShadow = + enclosingShadow = BcelShadow.makeStaticInitialization(world, mg); + // System.err.println(enclosingShadow); + } else if (mg.isAdviceMethod()) { + enclosingShadow = BcelShadow.makeAdviceExecution(world, mg); + } else { + AjAttribute.EffectiveSignatureAttribute effective = mg.getEffectiveSignature(); + if (effective == null) { + // Don't want ajc$preClinit to be considered for matching + if (isOverweaving && mg.getName().startsWith(NameMangler.PREFIX)) { + return false; + } + enclosingShadow = BcelShadow.makeMethodExecution(world, mg, !canMatchBodyShadows); + } else if (effective.isWeaveBody()) { + ResolvedMember rm = effective.getEffectiveSignature(); + + // Annotations for things with effective signatures are + // never stored in the effective + // signature itself - we have to hunt for them. Storing them + // in the effective signature + // would mean keeping two sets up to date (no way!!) + + fixParameterNamesForResolvedMember(rm, mg.getMemberView()); + fixAnnotationsForResolvedMember(rm, mg.getMemberView()); + + enclosingShadow = BcelShadow.makeShadowForMethod(world, mg, effective.getShadowKind(), rm); + } else { + return false; + } + } + + if (canMatchBodyShadows) { + for (InstructionHandle h = mg.getBody().getStart(); h != null; h = h.getNext()) { + match(mg, h, enclosingShadow, shadowAccumulator); + } + } + // FIXME asc change from string match if we can, rather brittle. + // this check actually prevents field-exec jps + if (canMatch(enclosingShadow.getKind()) + && !(mg.getName().charAt(0) == 'a' && mg.getName().startsWith("ajc$interFieldInit"))) { + if (match(enclosingShadow, shadowAccumulator)) { + enclosingShadow.init(); + } + } + mg.matchedShadows = shadowAccumulator; + return !shadowAccumulator.isEmpty(); + } + } + + private boolean matchInit(LazyMethodGen mg, List shadowAccumulator) { + BcelShadow enclosingShadow; + // XXX the enclosing join point is wrong for things before ignoreMe. + InstructionHandle superOrThisCall = findSuperOrThisCall(mg); + + // we don't walk bodies of things where it's a wrong constructor thingie + if (superOrThisCall == null) { + return false; + } + + enclosingShadow = BcelShadow.makeConstructorExecution(world, mg, superOrThisCall); + if (mg.getEffectiveSignature() != null) { + enclosingShadow.setMatchingSignature(mg.getEffectiveSignature().getEffectiveSignature()); + } + + // walk the body + boolean beforeSuperOrThisCall = true; + if (shouldWeaveBody(mg)) { + if (canMatchBodyShadows) { + for (InstructionHandle h = mg.getBody().getStart(); h != null; h = h.getNext()) { + if (h == superOrThisCall) { + beforeSuperOrThisCall = false; + continue; + } + match(mg, h, beforeSuperOrThisCall ? null : enclosingShadow, shadowAccumulator); + } + } + if (canMatch(Shadow.ConstructorExecution)) { + match(enclosingShadow, shadowAccumulator); + } + } + + // XXX we don't do pre-inits of interfaces + + // now add interface inits + if (!isThisCall(superOrThisCall)) { + InstructionHandle curr = enclosingShadow.getRange().getStart(); + for (Iterator i = addedSuperInitializersAsList.iterator(); i.hasNext();) { + IfaceInitList l = i.next(); + + Member ifaceInitSig = AjcMemberMaker.interfaceConstructor(l.onType); + + BcelShadow initShadow = BcelShadow.makeIfaceInitialization(world, mg, ifaceInitSig); + + // insert code in place + InstructionList inits = genInitInstructions(l.list, false); + if (match(initShadow, shadowAccumulator) || !inits.isEmpty()) { + initShadow.initIfaceInitializer(curr); + initShadow.getRange().insert(inits, Range.OutsideBefore); + } + } + + // now we add our initialization code + InstructionList inits = genInitInstructions(addedThisInitializers, false); + enclosingShadow.getRange().insert(inits, Range.OutsideBefore); + } + + // actually, you only need to inline the self constructors that are + // in a particular group (partition the constructors into groups where + // members + // call or are called only by those in the group). Then only inline + // constructors + // in groups where at least one initialization jp matched. Future work. + boolean addedInitialization = match(BcelShadow.makeUnfinishedInitialization(world, mg), initializationShadows); + addedInitialization |= match(BcelShadow.makeUnfinishedPreinitialization(world, mg), initializationShadows); + mg.matchedShadows = shadowAccumulator; + return addedInitialization || !shadowAccumulator.isEmpty(); + } + + private boolean shouldWeaveBody(LazyMethodGen mg) { + if (mg.isBridgeMethod()) { + return false; + } + if (mg.isAjSynthetic()) { + return mg.getName().equals(""); + } + AjAttribute.EffectiveSignatureAttribute a = mg.getEffectiveSignature(); + if (a != null) { + return a.isWeaveBody(); + } + return true; + } + + /** + * first sorts the mungers, then gens the initializers in the right order + */ + private InstructionList genInitInstructions(List list, boolean isStatic) { + list = PartialOrder.sort(list); + if (list == null) { + throw new BCException("circularity in inter-types"); + } + + InstructionList ret = new InstructionList(); + + for (ConcreteTypeMunger cmunger : list) { + NewFieldTypeMunger munger = (NewFieldTypeMunger) cmunger.getMunger(); + ResolvedMember initMethod = munger.getInitMethod(cmunger.getAspectType()); + if (!isStatic) { + ret.append(InstructionConstants.ALOAD_0); + } + ret.append(Utility.createInvoke(fact, world, initMethod)); + } + return ret; + } + + private void match(LazyMethodGen mg, InstructionHandle ih, BcelShadow enclosingShadow, List shadowAccumulator) { + Instruction i = ih.getInstruction(); + + // Exception handlers (pr230817) + if (canMatch(Shadow.ExceptionHandler) && !Range.isRangeHandle(ih)) { + Set targeters = ih.getTargetersCopy(); + // If in Java7 there may be overlapping exception ranges for multi catch - we should recognize that + for (InstructionTargeter t : targeters) { + if (t instanceof ExceptionRange) { + // assert t.getHandler() == ih + ExceptionRange er = (ExceptionRange) t; + if (er.getCatchType() == null) { + continue; + } + if (isInitFailureHandler(ih)) { + return; + } + if (!ih.getInstruction().isStoreInstruction() && ih.getInstruction().getOpcode() != Constants.NOP) { + // If using cobertura, the catch block stats with + // INVOKESTATIC rather than ASTORE, in order that the ranges + // for the methodcall and exceptionhandler shadows + // that occur at this same + // line, we need to modify the instruction list to + // split them - adding a + // NOP before the invokestatic that gets all the targeters + // that were aimed at the INVOKESTATIC + mg.getBody().insert(ih, InstructionConstants.NOP); + InstructionHandle newNOP = ih.getPrev(); + // what about a try..catch that starts at the start + // of the exception handler? need to only include + // certain targeters really. + er.updateTarget(ih, newNOP, mg.getBody()); + for (InstructionTargeter t2 : targeters) { + newNOP.addTargeter(t2); + } + ih.removeAllTargeters(); + match(BcelShadow.makeExceptionHandler(world, er, mg, newNOP, enclosingShadow), shadowAccumulator); + } else { + match(BcelShadow.makeExceptionHandler(world, er, mg, ih, enclosingShadow), shadowAccumulator); + } + } + } + } + + if ((i instanceof FieldInstruction) && (canMatch(Shadow.FieldGet) || canMatch(Shadow.FieldSet))) { + FieldInstruction fi = (FieldInstruction) i; + + if (fi.opcode == Constants.PUTFIELD || fi.opcode == Constants.PUTSTATIC) { + // check for sets of constant fields. We first check the + // previous + // instruction. If the previous instruction is a LD_WHATEVER + // (push + // constant on the stack) then we must resolve the field to + // determine + // if it's final. If it is final, then we don't generate a + // shadow. + InstructionHandle prevHandle = ih.getPrev(); + Instruction prevI = prevHandle.getInstruction(); + if (Utility.isConstantPushInstruction(prevI)) { + Member field = BcelWorld.makeFieldJoinPointSignature(clazz, (FieldInstruction) i); + ResolvedMember resolvedField = field.resolve(world); + if (resolvedField == null) { + // we can't find the field, so it's not a join point. + } else if (Modifier.isFinal(resolvedField.getModifiers())) { + // it's final, so it's the set of a final constant, so + // it's + // not a join point according to 1.0.6 and 1.1. + } else { + if (canMatch(Shadow.FieldSet)) { + matchSetInstruction(mg, ih, enclosingShadow, shadowAccumulator); + } + } + } else { + if (canMatch(Shadow.FieldSet)) { + matchSetInstruction(mg, ih, enclosingShadow, shadowAccumulator); + } + } + } else { + if (canMatch(Shadow.FieldGet)) { + matchGetInstruction(mg, ih, enclosingShadow, shadowAccumulator); + } + } + } else if (i instanceof InvokeInstruction) { + InvokeInstruction ii = (InvokeInstruction) i; + if (ii.getMethodName(clazz.getConstantPool()).equals("")) { + if (canMatch(Shadow.ConstructorCall)) { + match(BcelShadow.makeConstructorCall(world, mg, ih, enclosingShadow), shadowAccumulator); + } + } else if (ii.opcode == Constants.INVOKESPECIAL) { + String onTypeName = ii.getClassName(cpg); + if (onTypeName.equals(mg.getEnclosingClass().getName())) { + // we are private + matchInvokeInstruction(mg, ih, ii, enclosingShadow, shadowAccumulator); + } else { + // we are a super call, and this is not a join point in + // AspectJ-1.{0,1} + } + } else { + if (ii.getOpcode()!=Constants.INVOKEDYNAMIC) { + matchInvokeInstruction(mg, ih, ii, enclosingShadow, shadowAccumulator); + } + } + } else if (world.isJoinpointArrayConstructionEnabled() && i.isArrayCreationInstruction()) { + if (canMatch(Shadow.ConstructorCall)) { + if (i.opcode == Constants.ANEWARRAY) { + // ANEWARRAY arrayInstruction = (ANEWARRAY)i; + // ObjectType arrayType = i.getLoadClassType(clazz.getConstantPool()); + BcelShadow ctorCallShadow = BcelShadow.makeArrayConstructorCall(world, mg, ih, enclosingShadow); + match(ctorCallShadow, shadowAccumulator); + } else if (i.opcode == Constants.NEWARRAY) { + // NEWARRAY arrayInstruction = (NEWARRAY)i; + // Type arrayType = i.getType(); + BcelShadow ctorCallShadow = BcelShadow.makeArrayConstructorCall(world, mg, ih, enclosingShadow); + match(ctorCallShadow, shadowAccumulator); + } else if (i instanceof MULTIANEWARRAY) { + // MULTIANEWARRAY arrayInstruction = (MULTIANEWARRAY) i; + // ObjectType arrayType = arrayInstruction.getLoadClassType(clazz.getConstantPool()); + BcelShadow ctorCallShadow = BcelShadow.makeArrayConstructorCall(world, mg, ih, enclosingShadow); + match(ctorCallShadow, shadowAccumulator); + } + } + // see pr77166 if you are thinking about implementing this + // } else if (i instanceof AALOAD ) { + // AALOAD arrayLoad = (AALOAD)i; + // Type arrayType = arrayLoad.getType(clazz.getConstantPoolGen()); + // BcelShadow arrayLoadShadow = + // BcelShadow.makeArrayLoadCall(world,mg,ih,enclosingShadow); + // match(arrayLoadShadow,shadowAccumulator); + // } else if (i instanceof AASTORE) { + // // ... magic required + } else if (world.isJoinpointSynchronizationEnabled() + && ((i.getOpcode() == Constants.MONITORENTER) || (i.getOpcode() == Constants.MONITOREXIT))) { + // if (canMatch(Shadow.Monitoring)) { + if (i.getOpcode() == Constants.MONITORENTER) { + BcelShadow monitorEntryShadow = BcelShadow.makeMonitorEnter(world, mg, ih, enclosingShadow); + match(monitorEntryShadow, shadowAccumulator); + } else { + BcelShadow monitorExitShadow = BcelShadow.makeMonitorExit(world, mg, ih, enclosingShadow); + match(monitorExitShadow, shadowAccumulator); + } + // } + } + + } + + private boolean isInitFailureHandler(InstructionHandle ih) { + // Skip the astore_0 and aload_0 at the start of the handler and + // then check if the instruction following these is + // 'putstatic ajc$initFailureCause'. If it is then we are + // in the handler we created in AspectClinit.generatePostSyntheticCode() + InstructionHandle twoInstructionsAway = ih.getNext().getNext(); + if (twoInstructionsAway.getInstruction().opcode == Constants.PUTSTATIC) { + String name = ((FieldInstruction) twoInstructionsAway.getInstruction()).getFieldName(cpg); + if (name.equals(NameMangler.INITFAILURECAUSE_FIELD_NAME)) { + return true; + } + } + return false; + } + + private void matchSetInstruction(LazyMethodGen mg, InstructionHandle ih, BcelShadow enclosingShadow, + List shadowAccumulator) { + FieldInstruction fi = (FieldInstruction) ih.getInstruction(); + Member field = BcelWorld.makeFieldJoinPointSignature(clazz, fi); + + // synthetic fields are never join points + if (field.getName().startsWith(NameMangler.PREFIX)) { + return; + } + + ResolvedMember resolvedField = field.resolve(world); + if (resolvedField == null) { + // we can't find the field, so it's not a join point. + return; + } else if (Modifier.isFinal(resolvedField.getModifiers()) + && Utility.isConstantPushInstruction(ih.getPrev().getInstruction())) { + // it's the set of a final constant, so it's + // not a join point according to 1.0.6 and 1.1. + return; + } else if (resolvedField.isSynthetic()) { + // sets of synthetics aren't join points in 1.1 + return; + } else { + // Fix for bug 172107 (similar the "get" fix for bug 109728) + BcelShadow bs = BcelShadow.makeFieldSet(world, resolvedField, mg, ih, enclosingShadow); + String cname = fi.getClassName(cpg); + if (!resolvedField.getDeclaringType().getName().equals(cname)) { + bs.setActualTargetType(cname); + } + match(bs, shadowAccumulator); + } + } + + private void matchGetInstruction(LazyMethodGen mg, InstructionHandle ih, BcelShadow enclosingShadow, + List shadowAccumulator) { + FieldInstruction fi = (FieldInstruction) ih.getInstruction(); + Member field = BcelWorld.makeFieldJoinPointSignature(clazz, fi); + + // synthetic fields are never join points + if (field.getName().startsWith(NameMangler.PREFIX)) { + return; + } + + ResolvedMember resolvedField = field.resolve(world); + if (resolvedField == null) { + // we can't find the field, so it's not a join point. + return; + } else if (resolvedField.isSynthetic()) { + // sets of synthetics aren't join points in 1.1 + return; + } else { + BcelShadow bs = BcelShadow.makeFieldGet(world, resolvedField, mg, ih, enclosingShadow); + String cname = fi.getClassName(cpg); + if (!resolvedField.getDeclaringType().getName().equals(cname)) { + bs.setActualTargetType(cname); + } + match(bs, shadowAccumulator); + } + } + + /** + * For some named resolved type, this method looks for a member with a particular name - it should only be used when you truly + * believe there is only one member with that name in the type as it returns the first one it finds. + */ + private ResolvedMember findResolvedMemberNamed(ResolvedType type, String methodName) { + ResolvedMember[] allMethods = type.getDeclaredMethods(); + for (int i = 0; i < allMethods.length; i++) { + ResolvedMember member = allMethods[i]; + if (member.getName().equals(methodName)) { + return member; + } + } + return null; + } + + /** + * Find the specified member in the specified type. + * + * @param type the type to search for the member + * @param methodName the name of the method to find + * @param params the method parameters that the discovered method should have + */ + private ResolvedMember findResolvedMemberNamed(ResolvedType type, String methodName, UnresolvedType[] params) { + ResolvedMember[] allMethods = type.getDeclaredMethods(); + List candidates = new ArrayList(); + for (int i = 0; i < allMethods.length; i++) { + ResolvedMember candidate = allMethods[i]; + if (candidate.getName().equals(methodName)) { + if (candidate.getArity() == params.length) { + candidates.add(candidate); + } + } + } + + if (candidates.size() == 0) { + return null; + } else if (candidates.size() == 1) { + return candidates.get(0); + } else { + // multiple candidates + for (ResolvedMember candidate : candidates) { + // These checks will break down with generics... but that would need two ITDs with the same name, same arity and + // generics + boolean allOK = true; + UnresolvedType[] candidateParams = candidate.getParameterTypes(); + for (int p = 0; p < candidateParams.length; p++) { + if (!candidateParams[p].getErasureSignature().equals(params[p].getErasureSignature())) { + allOK = false; + break; + } + } + if (allOK) { + return candidate; + } + } + } + return null; + } + + /** + * For a given resolvedmember, this will discover the real annotations for it. Should only be used when the resolvedmember is + * the contents of an effective signature attribute, as thats the only time when the annotations aren't stored directly in the + * resolvedMember + * + * @param rm the sig we want it to pretend to be 'int A.m()' or somesuch ITD like thing + * @param declaredSig the real sig 'blah.ajc$xxx' + */ + private void fixParameterNamesForResolvedMember(ResolvedMember rm, ResolvedMember declaredSig) { + + UnresolvedType memberHostType = declaredSig.getDeclaringType(); + String methodName = declaredSig.getName(); + String[] pnames = null; + if (rm.getKind() == Member.METHOD && !rm.isAbstract()) { + if (methodName.startsWith("ajc$inlineAccessMethod") || methodName.startsWith("ajc$superDispatch")) { + ResolvedMember resolvedDooberry = world.resolve(declaredSig); + pnames = resolvedDooberry.getParameterNames(); + } else { + ResolvedMember realthing = AjcMemberMaker.interMethodDispatcher(rm.resolve(world), memberHostType).resolve(world); + ResolvedMember theRealMember = findResolvedMemberNamed(memberHostType.resolve(world), realthing.getName()); + if (theRealMember != null) { + pnames = theRealMember.getParameterNames(); + // static ITDs don't need any parameter shifting + if (pnames.length > 0 && pnames[0].equals("ajc$this_")) { + String[] pnames2 = new String[pnames.length - 1]; + System.arraycopy(pnames, 1, pnames2, 0, pnames2.length); + pnames = pnames2; + } + } + } + // i think ctors are missing from here... copy code from below... + } + rm.setParameterNames(pnames); + } + + /** + * For a given resolvedmember, this will discover the real annotations for it. Should only be used when the resolvedmember is + * the contents of an effective signature attribute, as thats the only time when the annotations aren't stored directly in the + * resolvedMember + * + * @param rm the sig we want it to pretend to be 'int A.m()' or somesuch ITD like thing + * @param declaredSig the real sig 'blah.ajc$xxx' + */ + private void fixAnnotationsForResolvedMember(ResolvedMember rm, ResolvedMember declaredSig) { + try { + UnresolvedType memberHostType = declaredSig.getDeclaringType(); + boolean containsKey = mapToAnnotationHolder.containsKey(rm); + ResolvedMember realAnnotationHolder = mapToAnnotationHolder.get(rm); + String methodName = declaredSig.getName(); + // FIXME asc shouldnt really rely on string names ! + if (!containsKey) { + if (rm.getKind() == Member.FIELD) { + if (methodName.startsWith("ajc$inlineAccessField")) { + realAnnotationHolder = world.resolve(rm); + } else { + ResolvedMember realthing = AjcMemberMaker.interFieldInitializer(rm, memberHostType); + realAnnotationHolder = world.resolve(realthing); + } + } else if (rm.getKind() == Member.METHOD && !rm.isAbstract()) { + if (methodName.startsWith("ajc$inlineAccessMethod") || methodName.startsWith("ajc$superDispatch")) { + realAnnotationHolder = world.resolve(declaredSig); + } else { + ResolvedMember realthing = AjcMemberMaker.interMethodDispatcher(rm.resolve(world), memberHostType).resolve(world); + realAnnotationHolder = findResolvedMemberNamed(memberHostType.resolve(world), realthing.getName(),realthing.getParameterTypes()); + if (realAnnotationHolder == null) { + throw new UnsupportedOperationException( + "Known limitation in M4 - can't find ITD members when type variable is used as an argument and has upper bound specified"); + } + } + } else if (rm.getKind() == Member.CONSTRUCTOR) { + ResolvedMember realThing = AjcMemberMaker.postIntroducedConstructor(memberHostType.resolve(world),rm.getDeclaringType(), rm.getParameterTypes()); + realAnnotationHolder = world.resolve(realThing); + // AMC temp guard for M4 + if (realAnnotationHolder == null) { + throw new UnsupportedOperationException("Known limitation in M4 - can't find ITD members when type variable is used as an argument and has upper bound specified"); + } + } + mapToAnnotationHolder.put(rm, realAnnotationHolder); + } + ResolvedType[] annotationTypes; + AnnotationAJ[] annotations; + if (realAnnotationHolder!=null) { + annotationTypes = realAnnotationHolder.getAnnotationTypes(); + annotations = realAnnotationHolder.getAnnotations(); + if (annotationTypes==null) { + annotationTypes = ResolvedType.EMPTY_ARRAY; + } + if (annotations==null) { + annotations = AnnotationAJ.EMPTY_ARRAY; + } + } else { + annotations = AnnotationAJ.EMPTY_ARRAY; + annotationTypes = ResolvedType.EMPTY_ARRAY; + } + rm.setAnnotations(annotations); + rm.setAnnotationTypes(annotationTypes); + } catch (UnsupportedOperationException ex) { + throw ex; + } catch (Throwable t) { + // FIXME asc remove this catch after more testing has confirmed the + // above stuff is OK + throw new BCException("Unexpectedly went bang when searching for annotations on " + rm, t); + } + } + + private void matchInvokeInstruction(LazyMethodGen mg, InstructionHandle ih, InvokeInstruction invoke, + BcelShadow enclosingShadow, List shadowAccumulator) { + String methodName = invoke.getName(cpg); + if (methodName.startsWith(NameMangler.PREFIX)) { + Member jpSig = world.makeJoinPointSignatureForMethodInvocation(clazz, invoke); + ResolvedMember declaredSig = jpSig.resolve(world); + // System.err.println(method + ", declaredSig: " +declaredSig); + if (declaredSig == null) { + return; + } + + if (declaredSig.getKind() == Member.FIELD) { + Shadow.Kind kind; + if (jpSig.getReturnType().equals(UnresolvedType.VOID)) { + kind = Shadow.FieldSet; + } else { + kind = Shadow.FieldGet; + } + + if (canMatch(Shadow.FieldGet) || canMatch(Shadow.FieldSet)) { + match(BcelShadow.makeShadowForMethodCall(world, mg, ih, enclosingShadow, kind, declaredSig), shadowAccumulator); + } + } else if (!declaredSig.getName().startsWith(NameMangler.PREFIX)) { + // 307147 - resolution above may have found the real method directly rather + // than needing to go through the effective signature attribute + if (canMatch(Shadow.MethodCall)) { + match(BcelShadow.makeShadowForMethodCall(world, mg, ih, enclosingShadow, Shadow.MethodCall, declaredSig), + shadowAccumulator); + } + } else { + AjAttribute.EffectiveSignatureAttribute effectiveSig = declaredSig.getEffectiveSignature(); + if (effectiveSig == null) { + return; + } + // System.err.println("call to inter-type member: " + + // effectiveSig); + if (effectiveSig.isWeaveBody()) { + return; + } + + ResolvedMember rm = effectiveSig.getEffectiveSignature(); + fixParameterNamesForResolvedMember(rm, declaredSig); + fixAnnotationsForResolvedMember(rm, declaredSig); // abracadabra + + if (canMatch(effectiveSig.getShadowKind())) { + match(BcelShadow.makeShadowForMethodCall(world, mg, ih, enclosingShadow, effectiveSig.getShadowKind(), rm), + shadowAccumulator); + } + } + } else { + if (canMatch(Shadow.MethodCall)) { + boolean proceed = true; + // overweaving needs to ignore some calls added by the previous weave + if (world.isOverWeaving()) { + String s = invoke.getClassName(mg.getConstantPool()); + // skip all the inc/dec/isValid/etc + if (s.length() > 4 + && s.charAt(4) == 'a' + && (s.equals("org.aspectj.runtime.internal.CFlowCounter") + || s.equals("org.aspectj.runtime.internal.CFlowStack") || s + .equals("org.aspectj.runtime.reflect.Factory"))) { + proceed = false; + } else { + if (methodName.equals("aspectOf")) { + proceed = false; + } + } + } + if (proceed) { + match(BcelShadow.makeMethodCall(world, mg, ih, enclosingShadow), shadowAccumulator); + } + } + } + } + + // static ... so all worlds will share the config for the first one + // created... + private static boolean checkedXsetForLowLevelContextCapturing = false; + private static boolean captureLowLevelContext = false; + + private boolean match(BcelShadow shadow, List shadowAccumulator) { + // Duplicate blocks - one with context one without, seems faster than multiple 'ifs' + if (captureLowLevelContext) { + ContextToken shadowMatchToken = CompilationAndWeavingContext.enteringPhase( + CompilationAndWeavingContext.MATCHING_SHADOW, shadow); + boolean isMatched = false; + + Shadow.Kind shadowKind = shadow.getKind(); + List candidateMungers = indexedShadowMungers[shadowKind.getKey()]; + + // System.out.println("Candidates " + candidateMungers); + if (candidateMungers != null) { + for (ShadowMunger munger : candidateMungers) { + + ContextToken mungerMatchToken = CompilationAndWeavingContext.enteringPhase( + CompilationAndWeavingContext.MATCHING_POINTCUT, munger.getPointcut()); + if (munger.match(shadow, world)) { + shadow.addMunger(munger); + isMatched = true; + if (shadow.getKind() == Shadow.StaticInitialization) { + clazz.warnOnAddedStaticInitializer(shadow, munger.getSourceLocation()); + } + } + CompilationAndWeavingContext.leavingPhase(mungerMatchToken); + } + + if (isMatched) { + shadowAccumulator.add(shadow); + } + } + CompilationAndWeavingContext.leavingPhase(shadowMatchToken); + return isMatched; + } else { + boolean isMatched = false; + + Shadow.Kind shadowKind = shadow.getKind(); + List candidateMungers = indexedShadowMungers[shadowKind.getKey()]; + + // System.out.println("Candidates at " + shadowKind + " are " + candidateMungers); + if (candidateMungers != null) { + for (ShadowMunger munger : candidateMungers) { + if (munger.match(shadow, world)) { + shadow.addMunger(munger); + isMatched = true; + if (shadow.getKind() == Shadow.StaticInitialization) { + clazz.warnOnAddedStaticInitializer(shadow, munger.getSourceLocation()); + } + } + } + if (isMatched) { + shadowAccumulator.add(shadow); + } + } + return isMatched; + } + } + + // ---- + + private void implement(LazyMethodGen mg) { + List shadows = mg.matchedShadows; + if (shadows == null) { + return; + } + // We depend on a partial order such that inner shadows are earlier on + // the list than outer shadows. That's fine. This order is preserved if: + + // A preceeds B iff B.getStart() is LATER THAN A.getStart(). + + for (BcelShadow shadow : shadows) { + ContextToken tok = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.IMPLEMENTING_ON_SHADOW, + shadow); + shadow.implement(); + CompilationAndWeavingContext.leavingPhase(tok); + } + // int ii = + mg.getMaxLocals(); + mg.matchedShadows = null; + } + + // ---- + + public LazyClassGen getLazyClassGen() { + return clazz; + } + + public BcelWorld getWorld() { + return world; + } + + public void setReweavableMode(boolean mode) { + inReweavableMode = mode; + } + + public boolean getReweavableMode() { + return inReweavableMode; + } + + @Override + public String toString() { + return "BcelClassWeaver instance for : " + clazz; + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/BcelConstantPoolReader.java b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelConstantPoolReader.java new file mode 100644 index 000000000..2deaf57c1 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelConstantPoolReader.java @@ -0,0 +1,34 @@ +/* ******************************************************************* + * Copyright (c) 2010 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement (SpringSource) + * ******************************************************************/ +package org.aspectj.weaver.bcel; + +import org.aspectj.apache.bcel.classfile.ConstantPool; +import org.aspectj.weaver.ConstantPoolReader; + +/** + * An implementation of the constant pool reader that speaks Bcel. + * + * @author Andy Clement + */ +public class BcelConstantPoolReader implements ConstantPoolReader { + + private ConstantPool constantPool; + + public BcelConstantPoolReader(ConstantPool constantPool) { + this.constantPool = constantPool; + } + + public String readUtf8(int cpIndex) { + return constantPool.getConstantUtf8(cpIndex).getValue(); + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/BcelConstantPoolWriter.java b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelConstantPoolWriter.java new file mode 100644 index 000000000..634764901 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelConstantPoolWriter.java @@ -0,0 +1,34 @@ +/* ******************************************************************* + * Copyright (c) 2010 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement (SpringSource) + * ******************************************************************/ +package org.aspectj.weaver.bcel; + +import org.aspectj.apache.bcel.classfile.ConstantPool; +import org.aspectj.weaver.ConstantPoolWriter; + +/** + * An implementation of the constant pool writer that speaks Bcel. + * + * @author Andy Clement + */ +class BcelConstantPoolWriter implements ConstantPoolWriter { + + ConstantPool pool; + + public BcelConstantPoolWriter(ConstantPool pool) { + this.pool = pool; + } + + public int writeUtf8(String name) { + return pool.addUtf8(name); + } + +} \ No newline at end of file diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/BcelField.java b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelField.java new file mode 100644 index 000000000..c88e8519f --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelField.java @@ -0,0 +1,307 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.bcel; + +import java.util.List; + +import org.aspectj.apache.bcel.classfile.Attribute; +import org.aspectj.apache.bcel.classfile.ConstantPool; +import org.aspectj.apache.bcel.classfile.Field; +import org.aspectj.apache.bcel.classfile.Synthetic; +import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; +import org.aspectj.apache.bcel.generic.FieldGen; +import org.aspectj.util.GenericSignature; +import org.aspectj.util.GenericSignatureParser; +import org.aspectj.weaver.AjAttribute; +import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; +import org.aspectj.weaver.AnnotationAJ; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ResolvedMemberImpl; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.World; +import org.aspectj.weaver.bcel.BcelGenericSignatureToTypeXConverter.GenericSignatureFormatException; + +/** + * An AspectJ Field object that is backed by a Bcel Field object. + * + * @author PARC + * @author Andy Clement + */ +final class BcelField extends ResolvedMemberImpl { + + public static int AccSynthetic = 0x1000; + + private Field field; + private boolean isAjSynthetic; + private boolean isSynthetic = false; + private AnnotationAJ[] annotations; + private final World world; + private final BcelObjectType bcelObjectType; + private UnresolvedType genericFieldType = null; + private boolean unpackedGenericSignature = false; + private boolean annotationsOnFieldObjectAreOutOfDate = false; + + BcelField(BcelObjectType declaringType, Field field) { + super(FIELD, declaringType.getResolvedTypeX(), field.getModifiers(), field.getName(), field.getSignature()); + this.field = field; + this.world = declaringType.getResolvedTypeX().getWorld(); + this.bcelObjectType = declaringType; + unpackAttributes(world); + checkedExceptions = UnresolvedType.NONE; + } + + /** + * Constructs an instance that wrappers a Field object, but where we do not (yet) have a BcelObjectType - usually because the + * containing type (and this field) are being constructed at runtime (so there is no .class file to retrieve). + */ + BcelField(String declaringTypeName, Field field, World world) { + super(FIELD, UnresolvedType.forName(declaringTypeName), field.getModifiers(), field.getName(), field.getSignature()); + this.field = field; + this.world = world; + this.bcelObjectType = null; + unpackAttributes(world); + checkedExceptions = UnresolvedType.NONE; + } + + private void unpackAttributes(World world) { + Attribute[] attrs = field.getAttributes(); + if (attrs != null && attrs.length > 0) { + ISourceContext sourceContext = getSourceContext(world); + List as = Utility.readAjAttributes(getDeclaringType().getClassName(), attrs, sourceContext, world, + (bcelObjectType != null ? bcelObjectType.getWeaverVersionAttribute() : WeaverVersionInfo.CURRENT), + new BcelConstantPoolReader(field.getConstantPool())); + as.addAll(AtAjAttributes.readAj5FieldAttributes(field, this, world.resolve(getDeclaringType()), sourceContext, + world.getMessageHandler())); + + // FIXME this code has no effect!!!??? it is set to false immediately after the block + // for (AjAttribute a : as) { + // if (a instanceof AjAttribute.AjSynthetic) { + // isAjSynthetic = true; + // } else { + // throw new BCException("weird field attribute " + a); + // } + // } + } + isAjSynthetic = false; + + for (int i = attrs.length - 1; i >= 0; i--) { + if (attrs[i] instanceof Synthetic) { + isSynthetic = true; + } + } + // in 1.5, synthetic is a modifier, not an attribute + if ((field.getModifiers() & AccSynthetic) != 0) { + isSynthetic = true; + } + + } + + @Override + public boolean isAjSynthetic() { + return isAjSynthetic; + } + + @Override + public boolean isSynthetic() { + return isSynthetic; + } + + @Override + public boolean hasAnnotation(UnresolvedType ofType) { + ensureAnnotationTypesRetrieved(); + for (ResolvedType aType : annotationTypes) { + if (aType.equals(ofType)) { + return true; + } + } + return false; + } + + @Override + public ResolvedType[] getAnnotationTypes() { + ensureAnnotationTypesRetrieved(); + return annotationTypes; + } + + @Override + public AnnotationAJ[] getAnnotations() { + ensureAnnotationTypesRetrieved(); + return annotations; + } + + @Override + public AnnotationAJ getAnnotationOfType(UnresolvedType ofType) { + ensureAnnotationTypesRetrieved(); + for (AnnotationAJ annotation : annotations) { + if (annotation.getTypeName().equals(ofType.getName())) { + return annotation; + } + } + return null; + } + + private void ensureAnnotationTypesRetrieved() { + if (annotationTypes == null) { + AnnotationGen annos[] = field.getAnnotations(); + if (annos.length == 0) { + annotationTypes = ResolvedType.EMPTY_ARRAY; + annotations = AnnotationAJ.EMPTY_ARRAY; + } else { + int annosCount = annos.length; + annotationTypes = new ResolvedType[annosCount]; + annotations = new AnnotationAJ[annosCount]; + for (int i = 0; i < annosCount; i++) { + AnnotationGen anno = annos[i]; + annotations[i] = new BcelAnnotation(anno, world); + annotationTypes[i] = annotations[i].getType(); + } + } + } + } + + @Override + public void addAnnotation(AnnotationAJ annotation) { + ensureAnnotationTypesRetrieved(); + int len = annotations.length; + AnnotationAJ[] ret = new AnnotationAJ[len + 1]; + System.arraycopy(annotations, 0, ret, 0, len); + ret[len] = annotation; + annotations = ret; + + ResolvedType[] newAnnotationTypes = new ResolvedType[len + 1]; + System.arraycopy(annotationTypes, 0, newAnnotationTypes, 0, len); + newAnnotationTypes[len] = annotation.getType(); + annotationTypes = newAnnotationTypes; + + annotationsOnFieldObjectAreOutOfDate = true; + } + + public void removeAnnotation(AnnotationAJ annotation) { + ensureAnnotationTypesRetrieved(); + + int len = annotations.length; + AnnotationAJ[] ret = new AnnotationAJ[len - 1]; + int p = 0; + for (AnnotationAJ anno : annotations) { + if (!anno.getType().equals(annotation.getType())) { + ret[p++] = anno; + } + } + annotations = ret; + + ResolvedType[] newAnnotationTypes = new ResolvedType[len - 1]; + p = 0; + for (ResolvedType anno : annotationTypes) { + if (!anno.equals(annotation.getType())) { + newAnnotationTypes[p++] = anno; + } + } + annotationTypes = newAnnotationTypes; + + annotationsOnFieldObjectAreOutOfDate = true; + } + + /** + * Unpack the generic signature attribute if there is one and we haven't already done so, then find the true field type of this + * field (eg. List). + */ + @Override + public UnresolvedType getGenericReturnType() { + unpackGenericSignature(); + return genericFieldType; + } + + public Field getFieldAsIs() { + return field; + } + + public Field getField(ConstantPool cpool) { + if (!annotationsOnFieldObjectAreOutOfDate) { + return field; + } + FieldGen newFieldGen = new FieldGen(field, cpool); + newFieldGen.removeAnnotations(); + // List alreadyHas = fg.getAnnotations(); + // if (annotations != null) { + // fg.removeAnnotations(); + for (AnnotationAJ annotation : annotations) { + newFieldGen.addAnnotation(new AnnotationGen(((BcelAnnotation) annotation).getBcelAnnotation(), cpool, true)); + } + // for (int i = 0; i < annotations.length; i++) { + // AnnotationAJ array_element = annotations[i]; + // boolean alreadyHasIt = false; + // for (AnnotationGen gen : alreadyHas) { + // if (gen.getTypeName().equals(array_element.getTypeName())) { + // alreadyHasIt = true; + // break; + // } + // } + // if (!alreadyHasIt) { + // fg.addAnnotation(new AnnotationGen(((BcelAnnotation) array_element).getBcelAnnotation(), cpg, true)); + // // } + // // } + // } + field = newFieldGen.getField(); + annotationsOnFieldObjectAreOutOfDate = false; // we are now correct again + return field; + } + + private void unpackGenericSignature() { + if (unpackedGenericSignature) { + return; + } + if (!world.isInJava5Mode()) { + this.genericFieldType = getReturnType(); + return; + } + unpackedGenericSignature = true; + String gSig = field.getGenericSignature(); + if (gSig != null) { + // get from generic + GenericSignature.FieldTypeSignature fts = new GenericSignatureParser().parseAsFieldSignature(gSig); + GenericSignature.ClassSignature genericTypeSig = bcelObjectType.getGenericClassTypeSignature(); + + GenericSignature.FormalTypeParameter[] parentFormals = bcelObjectType.getAllFormals(); + GenericSignature.FormalTypeParameter[] typeVars = ((genericTypeSig == null) ? new GenericSignature.FormalTypeParameter[0] + : genericTypeSig.formalTypeParameters); + GenericSignature.FormalTypeParameter[] formals = new GenericSignature.FormalTypeParameter[parentFormals.length + + typeVars.length]; + // put method formal in front of type formals for overriding in + // lookup + System.arraycopy(typeVars, 0, formals, 0, typeVars.length); + System.arraycopy(parentFormals, 0, formals, typeVars.length, parentFormals.length); + + try { + genericFieldType = BcelGenericSignatureToTypeXConverter.fieldTypeSignature2TypeX(fts, formals, world); + } catch (GenericSignatureFormatException e) { + // development bug, fail fast with good info + throw new IllegalStateException("While determing the generic field type of " + this.toString() + + " with generic signature " + gSig + " the following error was detected: " + e.getMessage()); + } + } else { + genericFieldType = getReturnType(); + } + } + + @Override + public void evictWeavingState() { + if (field != null) { + unpackGenericSignature(); + unpackAttributes(world); + ensureAnnotationTypesRetrieved(); + // this.sourceContext = SourceContextImpl.UNKNOWN_SOURCE_CONTEXT; + field = null; + } + } +} \ No newline at end of file diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/BcelFieldRef.java b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelFieldRef.java new file mode 100644 index 000000000..a5a2a79ec --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelFieldRef.java @@ -0,0 +1,100 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.bcel; + +import org.aspectj.apache.bcel.Constants; +import org.aspectj.apache.bcel.generic.Instruction; +import org.aspectj.apache.bcel.generic.InstructionFactory; +import org.aspectj.apache.bcel.generic.InstructionList; +import org.aspectj.weaver.ResolvedType; + +/** + * XXX Erik and I need to discuss this hierarchy. Having FieldRef extend Var is convenient, but hopefully there's a better design. + * + * This is always a static reference. + */ +public class BcelFieldRef extends BcelVar { + + private String className, fieldName; + + public BcelFieldRef(ResolvedType type, String className, String fieldName) { + super(type, 0); + this.className = className; + this.fieldName = fieldName; + } + + public String toString() { + return "BcelFieldRef(" + getType() + " " + className + "." + fieldName + ")"; + } + + // public int getSlot() { return slot; } + + public Instruction createLoad(InstructionFactory fact) { + return fact.createFieldAccess(className, fieldName, BcelWorld.makeBcelType(getType()), Constants.GETSTATIC); + } + + public Instruction createStore(InstructionFactory fact) { + return fact.createFieldAccess(className, fieldName, BcelWorld.makeBcelType(getType()), Constants.PUTSTATIC); + } + + public InstructionList createCopyFrom(InstructionFactory fact, int oldSlot) { + throw new RuntimeException("unimplemented"); + } + + // this is an array var + // void appendConvertableArrayLoad( + // InstructionList il, + // InstructionFactory fact, + // int index, + // ResolvedType convertTo) + // { + // ResolvedType convertFromType = getType().getResolvedComponentType(); + // appendLoad(il, fact); + // il.append(Utility.createConstant(fact, index)); + // il.append(fact.createArrayLoad(BcelWorld.makeBcelType(convertFromType))); + // Utility.appendConversion(il, fact, convertFromType, convertTo); + // } + // + // void appendConvertableArrayStore( + // InstructionList il, + // InstructionFactory fact, + // int index, + // BcelFieldRef storee) + // { + // ResolvedType convertToType = getType().getResolvedComponentType(); + // appendLoad(il, fact); + // il.append(Utility.createConstant(fact, index)); + // storee.appendLoad(il, fact); + // Utility.appendConversion(il, fact, storee.getType(), convertToType); + // il.append(fact.createArrayStore(BcelWorld.makeBcelType(convertToType))); + // } + // + // InstructionList createConvertableArrayStore( + // InstructionFactory fact, + // int index, + // BcelFieldRef storee) + // { + // InstructionList il = new InstructionList(); + // appendConvertableArrayStore(il, fact, index, storee); + // return il; + // } + // InstructionList createConvertableArrayLoad( + // InstructionFactory fact, + // int index, + // ResolvedType convertTo) + // { + // InstructionList il = new InstructionList(); + // appendConvertableArrayLoad(il, fact, index, convertTo); + // return il; + // } +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/BcelGenericSignatureToTypeXConverter.java b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelGenericSignatureToTypeXConverter.java new file mode 100644 index 000000000..9b4c90cbc --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelGenericSignatureToTypeXConverter.java @@ -0,0 +1,278 @@ +/* ******************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.bcel; + +import java.util.HashMap; +import java.util.Map; + +import org.aspectj.util.GenericSignature; +import org.aspectj.util.GenericSignature.SimpleClassTypeSignature; +import org.aspectj.weaver.BoundedReferenceType; +import org.aspectj.weaver.ReferenceType; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.TypeFactory; +import org.aspectj.weaver.TypeVariable; +import org.aspectj.weaver.TypeVariableReferenceType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.World; +import org.aspectj.weaver.tools.Trace; +import org.aspectj.weaver.tools.TraceFactory; + +/** + * A utility class that assists in unpacking constituent parts of generic signature attributes and returning their equivalents in + * UnresolvedType world. + */ +public class BcelGenericSignatureToTypeXConverter { + + private static Trace trace = TraceFactory.getTraceFactory().getTrace(BcelGenericSignatureToTypeXConverter.class); + + public static ResolvedType classTypeSignature2TypeX(GenericSignature.ClassTypeSignature aClassTypeSignature, + GenericSignature.FormalTypeParameter[] typeParams, World world) throws GenericSignatureFormatException { + Map typeMap = new HashMap(); + ResolvedType ret = classTypeSignature2TypeX(aClassTypeSignature, typeParams, world, typeMap); + fixUpCircularDependencies(ret, typeMap); + return ret; + } + + private static ResolvedType classTypeSignature2TypeX(GenericSignature.ClassTypeSignature aClassTypeSignature, + GenericSignature.FormalTypeParameter[] typeParams, World world, + Map inProgressTypeVariableResolutions) + throws GenericSignatureFormatException { + // class type sig consists of an outer type, and zero or more nested types + // the fully qualified name is outer-type.nested-type1.nested-type2.... + // each type in the hierarchy may have type arguments + + // first build the 'raw type' signature + StringBuffer sig = new StringBuffer(); + sig.append(aClassTypeSignature.outerType.identifier.replace(';', ' ').trim()); + for (int i = 0; i < aClassTypeSignature.nestedTypes.length; i++) { + sig.append("$"); + sig.append(aClassTypeSignature.nestedTypes[i].identifier.replace(';', ' ').trim()); + } + sig.append(";"); + + // now look for any type parameters. + // I *think* we only need to worry about the 'right-most' type... + SimpleClassTypeSignature innerType = aClassTypeSignature.outerType; + if (aClassTypeSignature.nestedTypes.length > 0) { + innerType = aClassTypeSignature.nestedTypes[aClassTypeSignature.nestedTypes.length - 1]; + } + if (innerType.typeArguments.length > 0) { + // we have to create a parameterized type + // type arguments may be array types, class types, or typevariable types + ResolvedType theBaseType = UnresolvedType.forSignature(sig.toString()).resolve(world); + + // Sometimes we may find that when the code is being load-time woven that the types have changed. + // Perhaps an old form of a library jar is being used - this can mean we discover right here + // that a type is not parameterizable (is that a word?). I think in these cases it is ok to + // just return with what we know (the base type). (see pr152848) + if (!(theBaseType.isGenericType() || theBaseType.isRawType())) { + if (trace.isTraceEnabled()) { + trace.event("classTypeSignature2TypeX: this type is not a generic type:", null, new Object[] { theBaseType }); + } + return theBaseType; + } + + ResolvedType[] typeArgumentTypes = new ResolvedType[innerType.typeArguments.length]; + for (int i = 0; i < typeArgumentTypes.length; i++) { + typeArgumentTypes[i] = typeArgument2TypeX(innerType.typeArguments[i], typeParams, world, + inProgressTypeVariableResolutions); + } + return TypeFactory.createParameterizedType(theBaseType, typeArgumentTypes, world); + + // world.resolve(UnresolvedType.forParameterizedTypes( + // UnresolvedType.forSignature(sig.toString()).resolve(world), + // typeArgumentTypes)); + } else { + // we have a non-parameterized type + return world.resolve(UnresolvedType.forSignature(sig.toString())); + } + } + + public static ResolvedType fieldTypeSignature2TypeX(GenericSignature.FieldTypeSignature aFieldTypeSignature, + GenericSignature.FormalTypeParameter[] typeParams, World world) throws GenericSignatureFormatException { + Map typeMap = new HashMap(); + ResolvedType ret = fieldTypeSignature2TypeX(aFieldTypeSignature, typeParams, world, typeMap); + fixUpCircularDependencies(ret, typeMap); + return ret; + } + + private static ResolvedType fieldTypeSignature2TypeX(GenericSignature.FieldTypeSignature aFieldTypeSignature, + GenericSignature.FormalTypeParameter[] typeParams, World world, + Map inProgressTypeVariableResolutions) + throws GenericSignatureFormatException { + if (aFieldTypeSignature.isClassTypeSignature()) { + return classTypeSignature2TypeX((GenericSignature.ClassTypeSignature) aFieldTypeSignature, typeParams, world, + inProgressTypeVariableResolutions); + } else if (aFieldTypeSignature.isArrayTypeSignature()) { + int dims = 0; + GenericSignature.TypeSignature ats = aFieldTypeSignature; + while (ats instanceof GenericSignature.ArrayTypeSignature) { + dims++; + ats = ((GenericSignature.ArrayTypeSignature) ats).typeSig; + } + return world.resolve(UnresolvedType.makeArray( + typeSignature2TypeX(ats, typeParams, world, inProgressTypeVariableResolutions), dims)); + } else if (aFieldTypeSignature.isTypeVariableSignature()) { + ResolvedType rtx = typeVariableSignature2TypeX((GenericSignature.TypeVariableSignature) aFieldTypeSignature, + typeParams, world, inProgressTypeVariableResolutions); + return rtx; + } else { + throw new GenericSignatureFormatException("Cant understand field type signature: " + aFieldTypeSignature); + } + } + + public static TypeVariable formalTypeParameter2TypeVariable(GenericSignature.FormalTypeParameter aFormalTypeParameter, + GenericSignature.FormalTypeParameter[] typeParams, World world) throws GenericSignatureFormatException { + Map typeMap = new HashMap(); + return formalTypeParameter2TypeVariable(aFormalTypeParameter, typeParams, world, typeMap); + } + + private static TypeVariable formalTypeParameter2TypeVariable(GenericSignature.FormalTypeParameter aFormalTypeParameter, + GenericSignature.FormalTypeParameter[] typeParams, World world, + Map inProgressTypeVariableResolutions) + throws GenericSignatureFormatException { + UnresolvedType upperBound = fieldTypeSignature2TypeX(aFormalTypeParameter.classBound, typeParams, world, + inProgressTypeVariableResolutions); + UnresolvedType[] ifBounds = new UnresolvedType[aFormalTypeParameter.interfaceBounds.length]; + for (int i = 0; i < ifBounds.length; i++) { + ifBounds[i] = fieldTypeSignature2TypeX(aFormalTypeParameter.interfaceBounds[i], typeParams, world, + inProgressTypeVariableResolutions); + } + return new TypeVariable(aFormalTypeParameter.identifier, upperBound, ifBounds); + } + + private static ResolvedType typeArgument2TypeX(GenericSignature.TypeArgument aTypeArgument, + GenericSignature.FormalTypeParameter[] typeParams, World world, + Map inProgressTypeVariableResolutions) + throws GenericSignatureFormatException { + if (aTypeArgument.isWildcard) { + return UnresolvedType.SOMETHING.resolve(world); + } + if (aTypeArgument.isMinus) { + UnresolvedType bound = fieldTypeSignature2TypeX(aTypeArgument.signature, typeParams, world, + inProgressTypeVariableResolutions); + ResolvedType resolvedBound = world.resolve(bound); + if (resolvedBound.isMissing()) { + world.getLint().cantFindType.signal("Unable to find type (for bound): " + resolvedBound.getName(), null); + resolvedBound = world.resolve(UnresolvedType.OBJECT); + } + ReferenceType rBound = (ReferenceType) resolvedBound; + return new BoundedReferenceType(rBound, false, world); + } else if (aTypeArgument.isPlus) { + UnresolvedType bound = fieldTypeSignature2TypeX(aTypeArgument.signature, typeParams, world, + inProgressTypeVariableResolutions); + ResolvedType resolvedBound = world.resolve(bound); + if (resolvedBound.isMissing()) { + world.getLint().cantFindType.signal("Unable to find type (for bound): " + resolvedBound.getName(), null); + resolvedBound = world.resolve(UnresolvedType.OBJECT); + } + ReferenceType rBound = (ReferenceType) resolvedBound; + return new BoundedReferenceType(rBound, true, world); + } else { + return fieldTypeSignature2TypeX(aTypeArgument.signature, typeParams, world, inProgressTypeVariableResolutions); + } + } + + public static ResolvedType typeSignature2TypeX(GenericSignature.TypeSignature aTypeSig, + GenericSignature.FormalTypeParameter[] typeParams, World world) throws GenericSignatureFormatException { + Map typeMap = new HashMap(); + ResolvedType ret = typeSignature2TypeX(aTypeSig, typeParams, world, typeMap); + fixUpCircularDependencies(ret, typeMap); + return ret; + } + + private static ResolvedType typeSignature2TypeX(GenericSignature.TypeSignature aTypeSig, + GenericSignature.FormalTypeParameter[] typeParams, World world, + Map inProgressTypeVariableResolutions) + throws GenericSignatureFormatException { + if (aTypeSig.isBaseType()) { + return world.resolve(UnresolvedType.forSignature(((GenericSignature.BaseTypeSignature) aTypeSig).toString())); + } else { + return fieldTypeSignature2TypeX((GenericSignature.FieldTypeSignature) aTypeSig, typeParams, world, + inProgressTypeVariableResolutions); + } + } + + private static ResolvedType typeVariableSignature2TypeX(GenericSignature.TypeVariableSignature aTypeVarSig, + GenericSignature.FormalTypeParameter[] typeParams, World world, + Map inProgressTypeVariableResolutions) + throws GenericSignatureFormatException { + GenericSignature.FormalTypeParameter typeVarBounds = null; + for (int i = 0; i < typeParams.length; i++) { + if (typeParams[i].identifier.equals(aTypeVarSig.typeVariableName)) { + typeVarBounds = typeParams[i]; + break; + } + } + if (typeVarBounds == null) { + // blowing up here breaks the situation with ITDs where the type variable is mentioned in the + // declaring type and used somewhere in the signature. Temporary change to allow it to return just a + // 'dumb' typevariablereference. + return new TypeVariableReferenceType(new TypeVariable(aTypeVarSig.typeVariableName), world); + // throw new GenericSignatureFormatException("Undeclared type variable in signature: " + aTypeVarSig.typeVariableName); + } + if (inProgressTypeVariableResolutions.containsKey(typeVarBounds)) { + return inProgressTypeVariableResolutions.get(typeVarBounds); + } + inProgressTypeVariableResolutions.put(typeVarBounds, new FTPHolder(typeVarBounds, world)); + ReferenceType ret = new TypeVariableReferenceType(formalTypeParameter2TypeVariable(typeVarBounds, typeParams, world, + inProgressTypeVariableResolutions), world); + inProgressTypeVariableResolutions.put(typeVarBounds, ret); + return ret; + } + + private static void fixUpCircularDependencies(ResolvedType aTypeX, + Map typeVariableResolutions) { + if (!(aTypeX instanceof ReferenceType)) { + return; + } + + ReferenceType rt = (ReferenceType) aTypeX; + TypeVariable[] typeVars = rt.getTypeVariables(); + if (typeVars != null) { + for (int i = 0; i < typeVars.length; i++) { + if (typeVars[i].getUpperBound() instanceof FTPHolder) { + GenericSignature.FormalTypeParameter key = ((FTPHolder) typeVars[i].getUpperBound()).ftpToBeSubstituted; + typeVars[i].setUpperBound(typeVariableResolutions.get(key)); + } + } + } + } + + private static class FTPHolder extends ReferenceType { + public GenericSignature.FormalTypeParameter ftpToBeSubstituted; + + public FTPHolder(GenericSignature.FormalTypeParameter ftp, World world) { + super("Ljava/lang/Object;", world); + this.ftpToBeSubstituted = ftp; + } + + public String toString() { + return "placeholder for TypeVariable of " + ftpToBeSubstituted.toString(); + } + + public ResolvedType resolve(World world) { + return this; + } + + public boolean isCacheable() { + return false; + } + } + + public static class GenericSignatureFormatException extends Exception { + public GenericSignatureFormatException(String explanation) { + super(explanation); + } + } +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/BcelMethod.java b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelMethod.java new file mode 100644 index 000000000..d1e60e1c7 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelMethod.java @@ -0,0 +1,714 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.bcel; + +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +import org.aspectj.apache.bcel.classfile.AnnotationDefault; +import org.aspectj.apache.bcel.classfile.Attribute; +import org.aspectj.apache.bcel.classfile.ExceptionTable; +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.classfile.LineNumber; +import org.aspectj.apache.bcel.classfile.LineNumberTable; +import org.aspectj.apache.bcel.classfile.LocalVariable; +import org.aspectj.apache.bcel.classfile.LocalVariableTable; +import org.aspectj.apache.bcel.classfile.Method; +import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; +import org.aspectj.apache.bcel.classfile.annotation.NameValuePair; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.SourceLocation; +import org.aspectj.util.GenericSignature; +import org.aspectj.util.GenericSignature.TypeVariableSignature; +import org.aspectj.util.GenericSignatureParser; +import org.aspectj.weaver.AjAttribute; +import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; +import org.aspectj.weaver.AnnotationAJ; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.MemberKind; +import org.aspectj.weaver.ResolvedMemberImpl; +import org.aspectj.weaver.ResolvedPointcutDefinition; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.ShadowMunger; +import org.aspectj.weaver.TypeVariable; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.World; +import org.aspectj.weaver.bcel.BcelGenericSignatureToTypeXConverter.GenericSignatureFormatException; + +//public final +class BcelMethod extends ResolvedMemberImpl { + + private final static String ASPECTJ_ANNOTATION_PACKAGE = "org.aspectj.lang.annotation"; + private final static char PACKAGE_INITIAL_CHAR = ASPECTJ_ANNOTATION_PACKAGE.charAt(0); + + private Method method; + + // these fields are not set for many BcelMethods... + private ShadowMunger associatedShadowMunger; + private ResolvedPointcutDefinition preResolvedPointcut; // used when ajc has pre-resolved the pointcut of some @Advice + private AjAttribute.EffectiveSignatureAttribute effectiveSignature; + + private AjAttribute.MethodDeclarationLineNumberAttribute declarationLineNumber; + private final BcelObjectType bcelObjectType; + + private int bitflags; + private static final int KNOW_IF_SYNTHETIC = 0x0001; + private static final int PARAMETER_NAMES_INITIALIZED = 0x0002; + private static final int CAN_BE_PARAMETERIZED = 0x0004; + private static final int UNPACKED_GENERIC_SIGNATURE = 0x0008; + private static final int IS_AJ_SYNTHETIC = 0x0040; + private static final int IS_SYNTHETIC = 0x0080; + private static final int IS_SYNTHETIC_INVERSE = 0x7f7f; // all bits but + // IS_SYNTHETIC (and + // topmost bit) + private static final int HAS_ANNOTATIONS = 0x0400; + private static final int HAVE_DETERMINED_ANNOTATIONS = 0x0800; + + // genericized version of return and parameter types + private UnresolvedType genericReturnType = null; + private UnresolvedType[] genericParameterTypes = null; + + BcelMethod(BcelObjectType declaringType, Method method) { + super(method.getName().equals("") ? CONSTRUCTOR : (method.getName().equals("") ? STATIC_INITIALIZATION + : METHOD), declaringType.getResolvedTypeX(), method.getModifiers(), method.getName(), method.getSignature()); + this.method = method; + sourceContext = declaringType.getResolvedTypeX().getSourceContext(); + bcelObjectType = declaringType; + unpackJavaAttributes(); + unpackAjAttributes(bcelObjectType.getWorld()); + } + + /** + * This constructor expects to be passed the attributes, rather than deserializing them. + */ + BcelMethod(BcelObjectType declaringType, Method method, List attributes) { + super(method.getName().equals("") ? CONSTRUCTOR : (method.getName().equals("") ? STATIC_INITIALIZATION + : METHOD), declaringType.getResolvedTypeX(), method.getModifiers(), method.getName(), method.getSignature()); + this.method = method; + sourceContext = declaringType.getResolvedTypeX().getSourceContext(); + bcelObjectType = declaringType; + unpackJavaAttributes(); + processAttributes(bcelObjectType.getWorld(), attributes); + } + + // ---- + + private void unpackJavaAttributes() { + ExceptionTable exnTable = method.getExceptionTable(); + checkedExceptions = (exnTable == null) ? UnresolvedType.NONE : UnresolvedType.forNames(exnTable.getExceptionNames()); + } + + @Override + public String[] getParameterNames() { + determineParameterNames(); + return super.getParameterNames(); + } + + public int getLineNumberOfFirstInstruction() { + LineNumberTable lnt = method.getLineNumberTable(); + if (lnt == null) { + return -1; + } + LineNumber[] lns = lnt.getLineNumberTable(); + if (lns == null || lns.length == 0) { + return -1; + } + return lns[0].getLineNumber(); + } + + public void determineParameterNames() { + if ((bitflags & PARAMETER_NAMES_INITIALIZED) != 0) { + return; + } + bitflags |= PARAMETER_NAMES_INITIALIZED; + LocalVariableTable varTable = method.getLocalVariableTable(); + int len = getArity(); + if (varTable == null) { + // do we have an annotation with the argNames value specified... + AnnotationAJ[] annos = getAnnotations(); + if (annos != null && annos.length != 0) { + AnnotationAJ[] axs = getAnnotations(); + for (int i = 0; i < axs.length; i++) { + AnnotationAJ annotationX = axs[i]; + String typename = annotationX.getTypeName(); + if (typename.charAt(0) == PACKAGE_INITIAL_CHAR) { + if (typename.equals("org.aspectj.lang.annotation.Pointcut") + || typename.equals("org.aspectj.lang.annotation.Before") + || typename.equals("org.aspectj.lang.annotation.Around") + || typename.startsWith("org.aspectj.lang.annotation.After")) { + AnnotationGen a = ((BcelAnnotation) annotationX).getBcelAnnotation(); + if (a != null) { + List values = a.getValues(); + for (NameValuePair nvPair : values) { + if (nvPair.getNameString().equals("argNames")) { + String argNames = nvPair.getValue().stringifyValue(); + StringTokenizer argNameTokenizer = new StringTokenizer(argNames, " ,"); + List argsList = new ArrayList(); + while (argNameTokenizer.hasMoreTokens()) { + argsList.add(argNameTokenizer.nextToken()); + } + int requiredCount = getParameterTypes().length; + while (argsList.size() < requiredCount) { + argsList.add("arg" + argsList.size()); + } + setParameterNames(argsList.toArray(new String[] {})); + return; + } + } + } + } + } + } + } + setParameterNames(Utility.makeArgNames(len)); + } else { + UnresolvedType[] paramTypes = getParameterTypes(); + String[] paramNames = new String[len]; + int index = Modifier.isStatic(modifiers) ? 0 : 1; + for (int i = 0; i < len; i++) { + LocalVariable lv = varTable.getLocalVariable(index); + if (lv == null) { + paramNames[i] = "arg" + i; + } else { + paramNames[i] = lv.getName(); + } + index += paramTypes[i].getSize(); + } + setParameterNames(paramNames); + } + } + + private void unpackAjAttributes(World world) { + associatedShadowMunger = null; + ResolvedType resolvedDeclaringType = getDeclaringType().resolve(world); + WeaverVersionInfo wvinfo = bcelObjectType.getWeaverVersionAttribute(); + List as = Utility.readAjAttributes(resolvedDeclaringType.getClassName(), method.getAttributes(), + resolvedDeclaringType.getSourceContext(), world, wvinfo, new BcelConstantPoolReader(method.getConstantPool())); + processAttributes(world, as); + as = AtAjAttributes.readAj5MethodAttributes(method, this, resolvedDeclaringType, preResolvedPointcut, + resolvedDeclaringType.getSourceContext(), world.getMessageHandler()); + processAttributes(world, as); + } + + private void processAttributes(World world, List as) { + for (AjAttribute attr : as) { + if (attr instanceof AjAttribute.MethodDeclarationLineNumberAttribute) { + declarationLineNumber = (AjAttribute.MethodDeclarationLineNumberAttribute) attr; + } else if (attr instanceof AjAttribute.AdviceAttribute) { + associatedShadowMunger = ((AjAttribute.AdviceAttribute) attr).reify(this, world, (ResolvedType) getDeclaringType()); + } else if (attr instanceof AjAttribute.AjSynthetic) { + bitflags |= IS_AJ_SYNTHETIC; + } else if (attr instanceof AjAttribute.EffectiveSignatureAttribute) { + effectiveSignature = (AjAttribute.EffectiveSignatureAttribute) attr; + } else if (attr instanceof AjAttribute.PointcutDeclarationAttribute) { + // this is an @AspectJ annotated advice method, with pointcut pre-resolved by ajc + preResolvedPointcut = ((AjAttribute.PointcutDeclarationAttribute) attr).reify(); + } else { + throw new BCException("weird method attribute " + attr); + } + } + } + + // + // // for testing - if we have this attribute, return it - will return null + // if + // // it doesnt know anything + // public AjAttribute[] getAttributes(String name) { + // List results = new ArrayList(); + // List l = Utility.readAjAttributes(getDeclaringType().getClassName(), + // method.getAttributes(), + // getSourceContext(bcelObjectType.getWorld()), bcelObjectType.getWorld(), + // bcelObjectType.getWeaverVersionAttribute()); + // for (Iterator iter = l.iterator(); iter.hasNext();) { + // AjAttribute element = (AjAttribute) iter.next(); + // if (element.getNameString().equals(name)) + // results.add(element); + // } + // if (results.size() > 0) { + // return (AjAttribute[]) results.toArray(new AjAttribute[] {}); + // } + // return null; + // } + + @Override + public String getAnnotationDefaultValue() { + Attribute[] attrs = method.getAttributes(); + for (int i = 0; i < attrs.length; i++) { + Attribute attribute = attrs[i]; + if (attribute.getName().equals("AnnotationDefault")) { + AnnotationDefault def = (AnnotationDefault) attribute; + return def.getElementValue().stringifyValue(); + } + } + return null; + } + + // for testing - use with the method above + public String[] getAttributeNames(boolean onlyIncludeAjOnes) { + Attribute[] as = method.getAttributes(); + List names = new ArrayList(); + // String[] strs = new String[as.length]; + for (int j = 0; j < as.length; j++) { + if (!onlyIncludeAjOnes || as[j].getName().startsWith(AjAttribute.AttributePrefix)) { + names.add(as[j].getName()); + } + } + return names.toArray(new String[] {}); + } + + @Override + public boolean isAjSynthetic() { + return (bitflags & IS_AJ_SYNTHETIC) != 0; + } + + @Override + public ShadowMunger getAssociatedShadowMunger() { + return associatedShadowMunger; + } + + @Override + public AjAttribute.EffectiveSignatureAttribute getEffectiveSignature() { + return effectiveSignature; + } + + public boolean hasDeclarationLineNumberInfo() { + return declarationLineNumber != null; + } + + public int getDeclarationLineNumber() { + if (declarationLineNumber != null) { + return declarationLineNumber.getLineNumber(); + } else { + return -1; + } + } + + public int getDeclarationOffset() { + if (declarationLineNumber != null) { + return declarationLineNumber.getOffset(); + } else { + return -1; + } + } + + @Override + public ISourceLocation getSourceLocation() { + ISourceLocation ret = super.getSourceLocation(); + if ((ret == null || ret.getLine() == 0) && hasDeclarationLineNumberInfo()) { + // lets see if we can do better + ISourceContext isc = getSourceContext(); + if (isc != null) { + ret = isc.makeSourceLocation(getDeclarationLineNumber(), getDeclarationOffset()); + } else { + ret = new SourceLocation(null, getDeclarationLineNumber()); + } + } + return ret; + } + + @Override + public MemberKind getKind() { + if (associatedShadowMunger != null) { + return ADVICE; + } else { + return super.getKind(); + } + } + + @Override + public boolean hasAnnotation(UnresolvedType ofType) { + ensureAnnotationsRetrieved(); + for (ResolvedType aType : annotationTypes) { + if (aType.equals(ofType)) { + return true; + } + } + return false; + } + + @Override + public AnnotationAJ[] getAnnotations() { + ensureAnnotationsRetrieved(); + if ((bitflags & HAS_ANNOTATIONS) != 0) { + return annotations; + } else { + return AnnotationAJ.EMPTY_ARRAY; + } + } + + @Override + public ResolvedType[] getAnnotationTypes() { + ensureAnnotationsRetrieved(); + return annotationTypes; + } + + @Override + public AnnotationAJ getAnnotationOfType(UnresolvedType ofType) { + ensureAnnotationsRetrieved(); + if ((bitflags & HAS_ANNOTATIONS) == 0) { + return null; + } + for (int i = 0; i < annotations.length; i++) { + if (annotations[i].getTypeName().equals(ofType.getName())) { + return annotations[i]; + } + } + return null; + } + + @Override + public void addAnnotation(AnnotationAJ annotation) { + ensureAnnotationsRetrieved(); + if ((bitflags & HAS_ANNOTATIONS) == 0) { + annotations = new AnnotationAJ[1]; + annotations[0] = annotation; + annotationTypes = new ResolvedType[1]; + annotationTypes[0] = annotation.getType(); + } else { + // Add it to the set of annotations + int len = annotations.length; + AnnotationAJ[] ret = new AnnotationAJ[len + 1]; + System.arraycopy(annotations, 0, ret, 0, len); + ret[len] = annotation; + annotations = ret; + ResolvedType[] newAnnotationTypes = new ResolvedType[len + 1]; + System.arraycopy(annotationTypes, 0, newAnnotationTypes, 0, len); + newAnnotationTypes[len] = annotation.getType(); + annotationTypes = newAnnotationTypes; + } + bitflags |= HAS_ANNOTATIONS; + } + + public void removeAnnotation(ResolvedType annotationType) { + ensureAnnotationsRetrieved(); + if ((bitflags & HAS_ANNOTATIONS) == 0) { + // nothing to do, why did we get called? + } else { + int len = annotations.length; + if (len == 1) { + bitflags &= ~HAS_ANNOTATIONS; + annotations = null; + annotationTypes = null; + return; + } + AnnotationAJ[] ret = new AnnotationAJ[len - 1]; + int p = 0; + for (AnnotationAJ annotation : annotations) { + if (!annotation.getType().equals(annotationType)) { + ret[p++] = annotation; + } + } + annotations = ret; + + ResolvedType[] newAnnotationTypes = new ResolvedType[len - 1]; + p = 0; + for (AnnotationAJ annotation : annotations) { + if (!annotation.getType().equals(annotationType)) { + newAnnotationTypes[p++] = annotationType; + } + } + annotationTypes = newAnnotationTypes; + } + bitflags |= HAS_ANNOTATIONS; + } + + public static final AnnotationAJ[] NO_PARAMETER_ANNOTATIONS = new AnnotationAJ[] {}; + + public void addParameterAnnotation(int param, AnnotationAJ anno) { + ensureParameterAnnotationsRetrieved(); + if (parameterAnnotations == NO_PARAMETER_ANNOTATIONXS) { + // First time we've added any, so lets set up the array + parameterAnnotations = new AnnotationAJ[getArity()][]; + for (int i = 0; i < getArity(); i++) { + parameterAnnotations[i] = NO_PARAMETER_ANNOTATIONS; + } + } + int existingCount = parameterAnnotations[param].length; + if (existingCount == 0) { + AnnotationAJ[] annoArray = new AnnotationAJ[1]; + annoArray[0] = anno; + parameterAnnotations[param] = annoArray; + } else { + AnnotationAJ[] newAnnoArray = new AnnotationAJ[existingCount + 1]; + System.arraycopy(parameterAnnotations[param], 0, newAnnoArray, 0, existingCount); + newAnnoArray[existingCount] = anno; + parameterAnnotations[param] = newAnnoArray; + } + } + + private void ensureAnnotationsRetrieved() { + if (method == null) { + return; // must be ok, we have evicted it + } + if ((bitflags & HAVE_DETERMINED_ANNOTATIONS) != 0) { + return; + } + bitflags |= HAVE_DETERMINED_ANNOTATIONS; + AnnotationGen annos[] = method.getAnnotations(); + if (annos.length == 0) { + annotationTypes = ResolvedType.NONE; + annotations = AnnotationAJ.EMPTY_ARRAY; + } else { + int annoCount = annos.length; + annotationTypes = new ResolvedType[annoCount]; + annotations = new AnnotationAJ[annoCount]; + for (int i = 0; i < annoCount; i++) { + AnnotationGen annotation = annos[i]; + annotations[i] = new BcelAnnotation(annotation, bcelObjectType.getWorld()); + annotationTypes[i] = annotations[i].getType(); + } + bitflags |= HAS_ANNOTATIONS; + } + } + + private void ensureParameterAnnotationsRetrieved() { + if (method == null) { + return; // must be ok, we have evicted it + } + AnnotationGen[][] pAnns = method.getParameterAnnotations(); + if (parameterAnnotationTypes == null || pAnns.length != parameterAnnotationTypes.length) { + if (pAnns == Method.NO_PARAMETER_ANNOTATIONS) { + parameterAnnotationTypes = BcelMethod.NO_PARAMETER_ANNOTATION_TYPES; + parameterAnnotations = BcelMethod.NO_PARAMETER_ANNOTATIONXS; + } else { + AnnotationGen annos[][] = method.getParameterAnnotations(); + parameterAnnotations = new AnnotationAJ[annos.length][]; + parameterAnnotationTypes = new ResolvedType[annos.length][]; + for (int i = 0; i < annos.length; i++) { + AnnotationGen[] annosOnThisParam = annos[i]; + if (annos[i].length == 0) { + parameterAnnotations[i] = AnnotationAJ.EMPTY_ARRAY; + parameterAnnotationTypes[i] = ResolvedType.NONE; + } else { + parameterAnnotations[i] = new AnnotationAJ[annosOnThisParam.length]; + parameterAnnotationTypes[i] = new ResolvedType[annosOnThisParam.length]; + for (int j = 0; j < annosOnThisParam.length; j++) { + parameterAnnotations[i][j] = new BcelAnnotation(annosOnThisParam[j], bcelObjectType.getWorld()); + parameterAnnotationTypes[i][j] = bcelObjectType.getWorld().resolve( + UnresolvedType.forSignature(annosOnThisParam[j].getTypeSignature())); + } + } + } + } + } + } + + @Override + public AnnotationAJ[][] getParameterAnnotations() { + ensureParameterAnnotationsRetrieved(); + return parameterAnnotations; + } + + @Override + public ResolvedType[][] getParameterAnnotationTypes() { + ensureParameterAnnotationsRetrieved(); + return parameterAnnotationTypes; + } + + /** + * A method can be parameterized if it has one or more generic parameters. A generic parameter (type variable parameter) is + * identified by the prefix "T" + */ + @Override + public boolean canBeParameterized() { + unpackGenericSignature(); + return (bitflags & CAN_BE_PARAMETERIZED) != 0; + } + + @Override + public UnresolvedType[] getGenericParameterTypes() { + unpackGenericSignature(); + return genericParameterTypes; + } + + /** + * Return the parameterized/generic return type or the normal return type if the method is not generic. + */ + @Override + public UnresolvedType getGenericReturnType() { + unpackGenericSignature(); + return genericReturnType; + } + + /** For testing only */ + public Method getMethod() { + return method; + } + + private void unpackGenericSignature() { + if ((bitflags & UNPACKED_GENERIC_SIGNATURE) != 0) { + return; + } + bitflags |= UNPACKED_GENERIC_SIGNATURE; + if (!bcelObjectType.getWorld().isInJava5Mode()) { + genericReturnType = getReturnType(); + genericParameterTypes = getParameterTypes(); + return; + } + String gSig = method.getGenericSignature(); + if (gSig != null) { + GenericSignature.MethodTypeSignature mSig = new GenericSignatureParser().parseAsMethodSignature(gSig);// method + // . + // getGenericSignature + // ()); + if (mSig.formalTypeParameters.length > 0) { + // generic method declaration + bitflags |= CAN_BE_PARAMETERIZED; + } + + typeVariables = new TypeVariable[mSig.formalTypeParameters.length]; + for (int i = 0; i < typeVariables.length; i++) { + GenericSignature.FormalTypeParameter methodFtp = mSig.formalTypeParameters[i]; + try { + typeVariables[i] = BcelGenericSignatureToTypeXConverter.formalTypeParameter2TypeVariable(methodFtp, + mSig.formalTypeParameters, bcelObjectType.getWorld()); + } catch (GenericSignatureFormatException e) { + // this is a development bug, so fail fast with good info + throw new IllegalStateException("While getting the type variables for method " + this.toString() + + " with generic signature " + mSig + " the following error condition was detected: " + e.getMessage()); + } + } + + GenericSignature.FormalTypeParameter[] parentFormals = bcelObjectType.getAllFormals(); + GenericSignature.FormalTypeParameter[] formals = new GenericSignature.FormalTypeParameter[parentFormals.length + + mSig.formalTypeParameters.length]; + // put method formal in front of type formals for overriding in + // lookup + System.arraycopy(mSig.formalTypeParameters, 0, formals, 0, mSig.formalTypeParameters.length); + System.arraycopy(parentFormals, 0, formals, mSig.formalTypeParameters.length, parentFormals.length); + GenericSignature.TypeSignature returnTypeSignature = mSig.returnType; + try { + genericReturnType = BcelGenericSignatureToTypeXConverter.typeSignature2TypeX(returnTypeSignature, formals, + bcelObjectType.getWorld()); + } catch (GenericSignatureFormatException e) { + // development bug, fail fast with good info + throw new IllegalStateException("While determing the generic return type of " + this.toString() + + " with generic signature " + gSig + " the following error was detected: " + e.getMessage()); + } + GenericSignature.TypeSignature[] paramTypeSigs = mSig.parameters; + if (paramTypeSigs.length == 0) { + genericParameterTypes = UnresolvedType.NONE; + } else { + genericParameterTypes = new UnresolvedType[paramTypeSigs.length]; + } + for (int i = 0; i < paramTypeSigs.length; i++) { + try { + genericParameterTypes[i] = BcelGenericSignatureToTypeXConverter.typeSignature2TypeX(paramTypeSigs[i], formals, + bcelObjectType.getWorld()); + } catch (GenericSignatureFormatException e) { + // development bug, fail fast with good info + throw new IllegalStateException("While determining the generic parameter types of " + this.toString() + + " with generic signature " + gSig + " the following error was detected: " + e.getMessage()); + } + if (paramTypeSigs[i] instanceof TypeVariableSignature) { + bitflags |= CAN_BE_PARAMETERIZED; + } + } + } else { + genericReturnType = getReturnType(); + genericParameterTypes = getParameterTypes(); + } + } + + @Override + public void evictWeavingState() { + if (method != null) { + unpackGenericSignature(); + unpackJavaAttributes(); + ensureAnnotationsRetrieved(); + ensureParameterAnnotationsRetrieved(); + determineParameterNames(); + // this.sourceContext = SourceContextImpl.UNKNOWN_SOURCE_CONTEXT; + method = null; + } + } + + @Override + public boolean isSynthetic() { + if ((bitflags & KNOW_IF_SYNTHETIC) == 0) { + workOutIfSynthetic(); + } + return (bitflags & IS_SYNTHETIC) != 0;// isSynthetic; + } + + // Pre Java5 synthetic is an attribute 'Synthetic', post Java5 it is a + // modifier (4096 or 0x1000) + private void workOutIfSynthetic() { + if ((bitflags & KNOW_IF_SYNTHETIC) != 0) { + return; + } + bitflags |= KNOW_IF_SYNTHETIC; + JavaClass jc = bcelObjectType.getJavaClass(); + bitflags &= IS_SYNTHETIC_INVERSE; // unset the bit + if (jc == null) { + return; // what the hell has gone wrong? + } + if (jc.getMajor() < 49/* Java5 */) { + // synthetic is an attribute + String[] synthetics = getAttributeNames(false); + if (synthetics != null) { + for (int i = 0; i < synthetics.length; i++) { + if (synthetics[i].equals("Synthetic")) { + bitflags |= IS_SYNTHETIC; + break; + } + } + } + } else { + // synthetic is a modifier (4096) + if ((modifiers & 4096) != 0) { + bitflags |= IS_SYNTHETIC; + } + } + } + + /** + * Returns whether or not the given object is equivalent to the current one. Returns true if + * getMethod().getCode().getCodeString() are equal. Allows for different line number tables. + */ + // bug 154054: is similar to equals(Object) however + // doesn't require implementing equals in Method and Code + // which proved expensive. Currently used within + // CrosscuttingMembers.replaceWith() to decide if we need + // to do a full build + @Override + public boolean isEquivalentTo(Object other) { + if (!(other instanceof BcelMethod)) { + return false; + } + BcelMethod o = (BcelMethod) other; + return getMethod().getCode().getCodeString().equals(o.getMethod().getCode().getCodeString()); + } + + /** + * Return true if the method represents the default constructor. Hard to determine this from bytecode, but the existence of the + * MethodDeclarationLineNumber attribute should tell us. + * + * @return true if this BcelMethod represents the default constructor + */ + @Override + public boolean isDefaultConstructor() { + boolean mightBe = !hasDeclarationLineNumberInfo() && name.equals("") && parameterTypes.length == 0; + if (mightBe) { + // TODO would be nice to do a check to see if the file was compiled with javac or ajc? + // maybe by checking the constant pool for aspectj strings? + return true; + } else { + return false; + } + } + +} \ No newline at end of file diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/BcelObjectType.java b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelObjectType.java new file mode 100644 index 000000000..710eb6dc7 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelObjectType.java @@ -0,0 +1,1023 @@ +/* ******************************************************************* + * Copyright (c) 2002 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * RonBodkin/AndyClement optimizations for memory consumption/speed + * ******************************************************************/ + +package org.aspectj.weaver.bcel; + +import java.io.PrintStream; +import java.lang.ref.WeakReference; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import org.aspectj.apache.bcel.classfile.Attribute; +import org.aspectj.apache.bcel.classfile.AttributeUtils; +import org.aspectj.apache.bcel.classfile.ConstantClass; +import org.aspectj.apache.bcel.classfile.ConstantPool; +import org.aspectj.apache.bcel.classfile.EnclosingMethod; +import org.aspectj.apache.bcel.classfile.Field; +import org.aspectj.apache.bcel.classfile.InnerClass; +import org.aspectj.apache.bcel.classfile.InnerClasses; +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.classfile.Method; +import org.aspectj.apache.bcel.classfile.Signature; +import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; +import org.aspectj.apache.bcel.classfile.annotation.EnumElementValue; +import org.aspectj.apache.bcel.classfile.annotation.NameValuePair; +import org.aspectj.asm.AsmManager; +import org.aspectj.bridge.IMessageHandler; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.GenericSignature; +import org.aspectj.util.GenericSignature.FormalTypeParameter; +import org.aspectj.weaver.AbstractReferenceTypeDelegate; +import org.aspectj.weaver.AjAttribute; +import org.aspectj.weaver.AjcMemberMaker; +import org.aspectj.weaver.AnnotationAJ; +import org.aspectj.weaver.AnnotationTargetKind; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.BindingScope; +import org.aspectj.weaver.ConcreteTypeMunger; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ReferenceType; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedPointcutDefinition; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.SourceContextImpl; +import org.aspectj.weaver.TypeVariable; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.WeaverStateInfo; +import org.aspectj.weaver.World; +import org.aspectj.weaver.bcel.BcelGenericSignatureToTypeXConverter.GenericSignatureFormatException; +import org.aspectj.weaver.patterns.Declare; +import org.aspectj.weaver.patterns.DeclareErrorOrWarning; +import org.aspectj.weaver.patterns.DeclarePrecedence; +import org.aspectj.weaver.patterns.FormalBinding; +import org.aspectj.weaver.patterns.IScope; +import org.aspectj.weaver.patterns.PerClause; + +public class BcelObjectType extends AbstractReferenceTypeDelegate { + public JavaClass javaClass; + private boolean artificial; // Was the BcelObject built from an artificial set of bytes? Or from the real ondisk stuff? + private LazyClassGen lazyClassGen = null; // set lazily if it's an aspect + + private int modifiers; + private String className; + + private String superclassSignature; + private String superclassName; + private String[] interfaceSignatures; + + private ResolvedMember[] fields = null; + private ResolvedMember[] methods = null; + private ResolvedType[] annotationTypes = null; + private AnnotationAJ[] annotations = null; + private TypeVariable[] typeVars = null; + private String retentionPolicy; + private AnnotationTargetKind[] annotationTargetKinds; + + // Aspect related stuff (pointcuts *could* be in a java class) + private AjAttribute.WeaverVersionInfo wvInfo = AjAttribute.WeaverVersionInfo.UNKNOWN; + private ResolvedPointcutDefinition[] pointcuts = null; + private ResolvedMember[] privilegedAccess = null; + private WeaverStateInfo weaverState = null; + private PerClause perClause = null; + private List typeMungers = Collections.emptyList(); + private List declares = Collections.emptyList(); + + private GenericSignature.FormalTypeParameter[] formalsForResolution = null; + private String declaredSignature = null; + + private boolean hasBeenWoven = false; + private boolean isGenericType = false; + private boolean isInterface; + private boolean isEnum; + private boolean isAnnotation; + private boolean isAnonymous; + private boolean isNested; + private boolean isObject = false; // set upon construction + private boolean isAnnotationStyleAspect = false;// set upon construction + private boolean isCodeStyleAspect = false; // not redundant with field + // above! + + private WeakReference superTypeReference = new WeakReference(null); + private WeakReference superInterfaceReferences = new WeakReference(null); + + private int bitflag = 0x0000; + + // discovery bits + private static final int DISCOVERED_ANNOTATION_RETENTION_POLICY = 0x0001; + private static final int UNPACKED_GENERIC_SIGNATURE = 0x0002; + private static final int UNPACKED_AJATTRIBUTES = 0x0004; // see note(1) + // below + private static final int DISCOVERED_ANNOTATION_TARGET_KINDS = 0x0008; + private static final int DISCOVERED_DECLARED_SIGNATURE = 0x0010; + private static final int DISCOVERED_WHETHER_ANNOTATION_STYLE = 0x0020; + + private static final int ANNOTATION_UNPACK_IN_PROGRESS = 0x0100; + + private static final String[] NO_INTERFACE_SIGS = new String[] {}; + + /* + * Notes: note(1): in some cases (perclause inheritance) we encounter unpacked state when calling getPerClause + * + * note(2): A BcelObjectType is 'damaged' if it has been modified from what was original constructed from the bytecode. This + * currently happens if the parents are modified or an annotation is added - ideally BcelObjectType should be immutable but + * that's a bigger piece of work. XXX + */ + + BcelObjectType(ReferenceType resolvedTypeX, JavaClass javaClass, boolean artificial, boolean exposedToWeaver) { + super(resolvedTypeX, exposedToWeaver); + this.javaClass = javaClass; + this.artificial = artificial; + initializeFromJavaclass(); + + // ATAJ: set the delegate right now for @AJ pointcut, else it is done + // too late to lookup + // @AJ pc refs annotation in class hierarchy + resolvedTypeX.setDelegate(this); + + ISourceContext sourceContext = resolvedTypeX.getSourceContext(); + if (sourceContext == SourceContextImpl.UNKNOWN_SOURCE_CONTEXT) { + sourceContext = new SourceContextImpl(this); + setSourceContext(sourceContext); + } + + // this should only ever be java.lang.Object which is + // the only class in Java-1.4 with no superclasses + isObject = (javaClass.getSuperclassNameIndex() == 0); + ensureAspectJAttributesUnpacked(); + // if (sourceContext instanceof SourceContextImpl) { + // ((SourceContextImpl)sourceContext).setSourceFileName(javaClass. + // getSourceFileName()); + // } + setSourcefilename(javaClass.getSourceFileName()); + } + + // repeat initialization + public void setJavaClass(JavaClass newclass, boolean artificial) { + this.javaClass = newclass; + this.artificial = artificial; + resetState(); + initializeFromJavaclass(); + } + + @Override + public boolean isCacheable() { + return true; + } + + private void initializeFromJavaclass() { + isInterface = javaClass.isInterface(); + isEnum = javaClass.isEnum(); + isAnnotation = javaClass.isAnnotation(); + isAnonymous = javaClass.isAnonymous(); + isNested = javaClass.isNested(); + modifiers = javaClass.getModifiers(); + superclassName = javaClass.getSuperclassName(); + className = javaClass.getClassName(); + cachedGenericClassTypeSignature = null; + } + + // --- getters + + // Java related + public boolean isInterface() { + return isInterface; + } + + public boolean isEnum() { + return isEnum; + } + + public boolean isAnnotation() { + return isAnnotation; + } + + public boolean isAnonymous() { + return isAnonymous; + } + + public boolean isNested() { + return isNested; + } + + public int getModifiers() { + return modifiers; + } + + /** + * Must take into account generic signature + */ + public ResolvedType getSuperclass() { + if (isObject) { + return null; + } + ResolvedType supertype = superTypeReference.get(); + if (supertype == null) { + ensureGenericSignatureUnpacked(); + if (superclassSignature == null) { + if (superclassName == null) { + superclassName = javaClass.getSuperclassName(); + } + superclassSignature = getResolvedTypeX().getWorld().resolve(UnresolvedType.forName(superclassName)).getSignature(); + } + World world = getResolvedTypeX().getWorld(); + supertype = world.resolve(UnresolvedType.forSignature(superclassSignature)); + superTypeReference = new WeakReference(supertype); + } + return supertype; + } + + public World getWorld() { + return getResolvedTypeX().getWorld(); + } + + /** + * Retrieves the declared interfaces - this allows for the generic signature on a type. If specified then the generic signature + * is used to work out the types - this gets around the results of erasure when the class was originally compiled. + */ + public ResolvedType[] getDeclaredInterfaces() { + + ResolvedType[] cachedInterfaceTypes = superInterfaceReferences.get(); + if (cachedInterfaceTypes == null) { + ensureGenericSignatureUnpacked(); + ResolvedType[] interfaceTypes = null; + if (interfaceSignatures == null) { + String[] names = javaClass.getInterfaceNames(); + if (names.length == 0) { + interfaceSignatures = NO_INTERFACE_SIGS; + interfaceTypes = ResolvedType.NONE; + } else { + interfaceSignatures = new String[names.length]; + interfaceTypes = new ResolvedType[names.length]; + for (int i = 0, len = names.length; i < len; i++) { + interfaceTypes[i] = getResolvedTypeX().getWorld().resolve(UnresolvedType.forName(names[i])); + interfaceSignatures[i] = interfaceTypes[i].getSignature(); + } + } + } else { + interfaceTypes = new ResolvedType[interfaceSignatures.length]; + for (int i = 0, len = interfaceSignatures.length; i < len; i++) { + interfaceTypes[i] = getResolvedTypeX().getWorld().resolve(UnresolvedType.forSignature(interfaceSignatures[i])); + } + } + superInterfaceReferences = new WeakReference(interfaceTypes); + return interfaceTypes; + } else { + return cachedInterfaceTypes; + } + } + + public ResolvedMember[] getDeclaredMethods() { + ensureGenericSignatureUnpacked(); + if (methods == null) { + Method[] ms = javaClass.getMethods(); + ResolvedMember[] newMethods = new ResolvedMember[ms.length]; + for (int i = ms.length - 1; i >= 0; i--) { + newMethods[i] = new BcelMethod(this, ms[i]); + } + methods = newMethods; + } + return methods; + } + + public ResolvedMember[] getDeclaredFields() { + ensureGenericSignatureUnpacked(); + if (fields == null) { + Field[] fs = javaClass.getFields(); + ResolvedMember[] newfields = new ResolvedMember[fs.length]; + for (int i = 0, len = fs.length; i < len; i++) { + newfields[i] = new BcelField(this, fs[i]); + } + fields = newfields; + } + return fields; + } + + public TypeVariable[] getTypeVariables() { + if (!isGeneric()) { + return TypeVariable.NONE; + } + + if (typeVars == null) { + GenericSignature.ClassSignature classSig = getGenericClassTypeSignature(); + typeVars = new TypeVariable[classSig.formalTypeParameters.length]; + for (int i = 0; i < typeVars.length; i++) { + GenericSignature.FormalTypeParameter ftp = classSig.formalTypeParameters[i]; + try { + typeVars[i] = BcelGenericSignatureToTypeXConverter.formalTypeParameter2TypeVariable(ftp, + classSig.formalTypeParameters, getResolvedTypeX().getWorld()); + } catch (GenericSignatureFormatException e) { + // this is a development bug, so fail fast with good info + throw new IllegalStateException("While getting the type variables for type " + this.toString() + + " with generic signature " + classSig + " the following error condition was detected: " + + e.getMessage()); + } + } + } + return typeVars; + } + + public Collection getTypeMungers() { + return typeMungers; + } + + public Collection getDeclares() { + return declares; + } + + public Collection getPrivilegedAccesses() { + if (privilegedAccess == null) { + return Collections.emptyList(); + } + return Arrays.asList(privilegedAccess); + } + + public ResolvedMember[] getDeclaredPointcuts() { + return pointcuts; + } + + public boolean isAspect() { + return perClause != null; + } + + /** + * Check if the type is an @AJ aspect (no matter if used from an LTW point of view). Such aspects are annotated with @Aspect + * + * @return true for @AJ aspect + */ + public boolean isAnnotationStyleAspect() { + if ((bitflag & DISCOVERED_WHETHER_ANNOTATION_STYLE) == 0) { + bitflag |= DISCOVERED_WHETHER_ANNOTATION_STYLE; + isAnnotationStyleAspect = !isCodeStyleAspect && hasAnnotation(AjcMemberMaker.ASPECT_ANNOTATION); + } + return isAnnotationStyleAspect; + } + + /** + * Process any org.aspectj.weaver attributes stored against the class. + */ + private void ensureAspectJAttributesUnpacked() { + if ((bitflag & UNPACKED_AJATTRIBUTES) != 0) { + return; + } + bitflag |= UNPACKED_AJATTRIBUTES; + IMessageHandler msgHandler = getResolvedTypeX().getWorld().getMessageHandler(); + // Pass in empty list that can store things for readAj5 to process + List l = null; + try { + l = Utility.readAjAttributes(className, javaClass.getAttributes(), getResolvedTypeX().getSourceContext(), + getResolvedTypeX().getWorld(), AjAttribute.WeaverVersionInfo.UNKNOWN, + new BcelConstantPoolReader(javaClass.getConstantPool())); + } catch (RuntimeException re) { + throw new RuntimeException("Problem processing attributes in " + javaClass.getFileName(), re); + } + List pointcuts = new ArrayList(); + typeMungers = new ArrayList(); + declares = new ArrayList(); + processAttributes(l, pointcuts, false); + ReferenceType type = getResolvedTypeX(); + AsmManager asmManager = ((BcelWorld) type.getWorld()).getModelAsAsmManager(); + l = AtAjAttributes.readAj5ClassAttributes(asmManager, javaClass, type, type.getSourceContext(), msgHandler, + isCodeStyleAspect); + AjAttribute.Aspect deferredAspectAttribute = processAttributes(l, pointcuts, true); + + if (pointcuts.size() == 0) { + this.pointcuts = ResolvedPointcutDefinition.NO_POINTCUTS; + } else { + this.pointcuts = pointcuts.toArray(new ResolvedPointcutDefinition[pointcuts.size()]); + } + + resolveAnnotationDeclares(l); + + if (deferredAspectAttribute != null) { + // we can finally process the aspect and its associated perclause... + perClause = deferredAspectAttribute.reifyFromAtAspectJ(this.getResolvedTypeX()); + } + if (isAspect() && !Modifier.isAbstract(getModifiers()) && isGeneric()) { + msgHandler.handleMessage(MessageUtil.error("The generic aspect '" + getResolvedTypeX().getName() + + "' must be declared abstract", getResolvedTypeX().getSourceLocation())); + } + + } + + private AjAttribute.Aspect processAttributes(List attributeList, List pointcuts, + boolean fromAnnotations) { + AjAttribute.Aspect deferredAspectAttribute = null; + for (AjAttribute a : attributeList) { + if (a instanceof AjAttribute.Aspect) { + if (fromAnnotations) { + deferredAspectAttribute = (AjAttribute.Aspect) a; + } else { + perClause = ((AjAttribute.Aspect) a).reify(this.getResolvedTypeX()); + isCodeStyleAspect = true; + } + } else if (a instanceof AjAttribute.PointcutDeclarationAttribute) { + pointcuts.add(((AjAttribute.PointcutDeclarationAttribute) a).reify()); + } else if (a instanceof AjAttribute.WeaverState) { + weaverState = ((AjAttribute.WeaverState) a).reify(); + } else if (a instanceof AjAttribute.TypeMunger) { + typeMungers.add(((AjAttribute.TypeMunger) a).reify(getResolvedTypeX().getWorld(), getResolvedTypeX())); + } else if (a instanceof AjAttribute.DeclareAttribute) { + declares.add(((AjAttribute.DeclareAttribute) a).getDeclare()); + } else if (a instanceof AjAttribute.PrivilegedAttribute) { + AjAttribute.PrivilegedAttribute privAttribute = (AjAttribute.PrivilegedAttribute) a; + privilegedAccess = privAttribute.getAccessedMembers(); + } else if (a instanceof AjAttribute.SourceContextAttribute) { + if (getResolvedTypeX().getSourceContext() instanceof SourceContextImpl) { + AjAttribute.SourceContextAttribute sca = (AjAttribute.SourceContextAttribute) a; + ((SourceContextImpl) getResolvedTypeX().getSourceContext()).configureFromAttribute(sca.getSourceFileName(), + sca.getLineBreaks()); + + setSourcefilename(sca.getSourceFileName()); + } + } else if (a instanceof AjAttribute.WeaverVersionInfo) { + // Set the weaver version used to build this type + wvInfo = (AjAttribute.WeaverVersionInfo) a; + } else { + throw new BCException("bad attribute " + a); + } + } + return deferredAspectAttribute; + } + + /** + * Extra processing step needed because declares that come from annotations are not pre-resolved. We can't do the resolution + * until *after* the pointcuts have been resolved. + */ + private void resolveAnnotationDeclares(List attributeList) { + FormalBinding[] bindings = new org.aspectj.weaver.patterns.FormalBinding[0]; + IScope bindingScope = new BindingScope(getResolvedTypeX(), getResolvedTypeX().getSourceContext(), bindings); + for (Iterator iter = attributeList.iterator(); iter.hasNext();) { + AjAttribute a = iter.next(); + if (a instanceof AjAttribute.DeclareAttribute) { + Declare decl = (((AjAttribute.DeclareAttribute) a).getDeclare()); + if (decl instanceof DeclareErrorOrWarning) { + decl.resolve(bindingScope); + } else if (decl instanceof DeclarePrecedence) { + ((DeclarePrecedence) decl).setScopeForResolution(bindingScope); + } + } + } + } + + public PerClause getPerClause() { + ensureAspectJAttributesUnpacked(); + return perClause; + } + + public JavaClass getJavaClass() { + return javaClass; + } + + /** + * @return true if built from bytes obtained from somewhere. False if built from bytes retrieved from disk. + */ + public boolean isArtificial() { + return artificial; + } + + public void resetState() { + if (javaClass == null) { + // we might store the classname and allow reloading? + // At this point we are relying on the world to not evict if it + // might want to reweave multiple times + throw new BCException("can't weave evicted type"); + } + + bitflag = 0x0000; + + this.annotationTypes = null; + this.annotations = null; + this.interfaceSignatures = null; + this.superclassSignature = null; + this.superclassName = null; + this.fields = null; + this.methods = null; + this.pointcuts = null; + this.perClause = null; + this.weaverState = null; + this.lazyClassGen = null; + hasBeenWoven = false; + + isObject = (javaClass.getSuperclassNameIndex() == 0); + isAnnotationStyleAspect = false; + ensureAspectJAttributesUnpacked(); + } + + public void finishedWith() { + // memory usage experiments.... + // this.interfaces = null; + // this.superClass = null; + // this.fields = null; + // this.methods = null; + // this.pointcuts = null; + // this.perClause = null; + // this.weaverState = null; + // this.lazyClassGen = null; + // this next line frees up memory, but need to understand incremental + // implications + // before leaving it in. + // getResolvedTypeX().setSourceContext(null); + } + + public WeaverStateInfo getWeaverState() { + return weaverState; + } + + void setWeaverState(WeaverStateInfo weaverState) { + this.weaverState = weaverState; + } + + public void printWackyStuff(PrintStream out) { + if (typeMungers.size() > 0) { + out.println(" TypeMungers: " + typeMungers); + } + if (declares.size() > 0) { + out.println(" declares: " + declares); + } + } + + /** + * Return the lazyClassGen associated with this type. For aspect types, this value will be cached, since it is used to inline + * advice. For non-aspect types, this lazyClassGen is always newly constructed. + */ + public LazyClassGen getLazyClassGen() { + LazyClassGen ret = lazyClassGen; + if (ret == null) { + // System.err.println("creating lazy class gen for: " + this); + ret = new LazyClassGen(this); + // ret.print(System.err); + // System.err.println("made LCG from : " + + // this.getJavaClass().getSuperclassName ); + if (isAspect()) { + lazyClassGen = ret; + } + } + return ret; + } + + public boolean isSynthetic() { + return getResolvedTypeX().isSynthetic(); + } + + public AjAttribute.WeaverVersionInfo getWeaverVersionAttribute() { + return wvInfo; + } + + // -- annotation related + + public ResolvedType[] getAnnotationTypes() { + ensureAnnotationsUnpacked(); + return annotationTypes; + } + + public AnnotationAJ[] getAnnotations() { + ensureAnnotationsUnpacked(); + return annotations; + } + + public boolean hasAnnotations() { + ensureAnnotationsUnpacked(); + return annotations.length != 0; + } + + public boolean hasAnnotation(UnresolvedType ofType) { + // Due to re-entrancy we may be in the middle of unpacking the annotations already... in which case use this slow + // alternative until the stack unwinds itself + if (isUnpackingAnnotations()) { + AnnotationGen annos[] = javaClass.getAnnotations(); + if (annos == null || annos.length == 0) { + return false; + } else { + String lookingForSignature = ofType.getSignature(); + for (int a = 0; a < annos.length; a++) { + AnnotationGen annotation = annos[a]; + if (lookingForSignature.equals(annotation.getTypeSignature())) { + return true; + } + } + } + return false; + } + ensureAnnotationsUnpacked(); + for (int i = 0, max = annotationTypes.length; i < max; i++) { + UnresolvedType ax = annotationTypes[i]; + if (ax == null) { + throw new RuntimeException("Annotation entry " + i + " on type " + this.getResolvedTypeX().getName() + " is null!"); + } + if (ax.equals(ofType)) { + return true; + } + } + return false; + } + + public boolean isAnnotationWithRuntimeRetention() { + return (getRetentionPolicy() == null ? false : getRetentionPolicy().equals("RUNTIME")); + } + + public String getRetentionPolicy() { + if ((bitflag & DISCOVERED_ANNOTATION_RETENTION_POLICY) == 0) { + bitflag |= DISCOVERED_ANNOTATION_RETENTION_POLICY; + retentionPolicy = null; // null means we have no idea + if (isAnnotation()) { + ensureAnnotationsUnpacked(); + for (int i = annotations.length - 1; i >= 0; i--) { + AnnotationAJ ax = annotations[i]; + if (ax.getTypeName().equals(UnresolvedType.AT_RETENTION.getName())) { + List values = ((BcelAnnotation) ax).getBcelAnnotation().getValues(); + for (Iterator it = values.iterator(); it.hasNext();) { + NameValuePair element = it.next(); + EnumElementValue v = (EnumElementValue) element.getValue(); + retentionPolicy = v.getEnumValueString(); + return retentionPolicy; + } + } + } + } + } + return retentionPolicy; + } + + public boolean canAnnotationTargetType() { + AnnotationTargetKind[] targetKinds = getAnnotationTargetKinds(); + if (targetKinds == null) { + return true; + } + for (int i = 0; i < targetKinds.length; i++) { + if (targetKinds[i].equals(AnnotationTargetKind.TYPE)) { + return true; + } + } + return false; + } + + public AnnotationTargetKind[] getAnnotationTargetKinds() { + if ((bitflag & DISCOVERED_ANNOTATION_TARGET_KINDS) != 0) { + return annotationTargetKinds; + } + bitflag |= DISCOVERED_ANNOTATION_TARGET_KINDS; + annotationTargetKinds = null; // null means we have no idea or the + // @Target annotation hasn't been used + List targetKinds = new ArrayList(); + if (isAnnotation()) { + AnnotationAJ[] annotationsOnThisType = getAnnotations(); + for (int i = 0; i < annotationsOnThisType.length; i++) { + AnnotationAJ a = annotationsOnThisType[i]; + if (a.getTypeName().equals(UnresolvedType.AT_TARGET.getName())) { + Set targets = a.getTargets(); + if (targets != null) { + for (String targetKind : targets) { + if (targetKind.equals("ANNOTATION_TYPE")) { + targetKinds.add(AnnotationTargetKind.ANNOTATION_TYPE); + } else if (targetKind.equals("CONSTRUCTOR")) { + targetKinds.add(AnnotationTargetKind.CONSTRUCTOR); + } else if (targetKind.equals("FIELD")) { + targetKinds.add(AnnotationTargetKind.FIELD); + } else if (targetKind.equals("LOCAL_VARIABLE")) { + targetKinds.add(AnnotationTargetKind.LOCAL_VARIABLE); + } else if (targetKind.equals("METHOD")) { + targetKinds.add(AnnotationTargetKind.METHOD); + } else if (targetKind.equals("PACKAGE")) { + targetKinds.add(AnnotationTargetKind.PACKAGE); + } else if (targetKind.equals("PARAMETER")) { + targetKinds.add(AnnotationTargetKind.PARAMETER); + } else if (targetKind.equals("TYPE")) { + targetKinds.add(AnnotationTargetKind.TYPE); + } + } + } + } + } + if (!targetKinds.isEmpty()) { + annotationTargetKinds = new AnnotationTargetKind[targetKinds.size()]; + return targetKinds.toArray(annotationTargetKinds); + } + } + return annotationTargetKinds; + } + + // --- unpacking methods + + private boolean isUnpackingAnnotations() { + return (bitflag & ANNOTATION_UNPACK_IN_PROGRESS) != 0; + } + + private void ensureAnnotationsUnpacked() { + if (isUnpackingAnnotations()) { + throw new BCException("Re-entered weaver instance whilst unpacking annotations on " + this.className); + } + if (annotationTypes == null) { + try { + bitflag |= ANNOTATION_UNPACK_IN_PROGRESS; + AnnotationGen annos[] = javaClass.getAnnotations(); + if (annos == null || annos.length == 0) { + annotationTypes = ResolvedType.NONE; + annotations = AnnotationAJ.EMPTY_ARRAY; + } else { + World w = getResolvedTypeX().getWorld(); + annotationTypes = new ResolvedType[annos.length]; + annotations = new AnnotationAJ[annos.length]; + for (int i = 0; i < annos.length; i++) { + AnnotationGen annotation = annos[i]; + String typeSignature = annotation.getTypeSignature(); + ResolvedType rType = w.resolve(UnresolvedType.forSignature(typeSignature)); + if (rType == null) { + throw new RuntimeException("Whilst unpacking annotations on '" + getResolvedTypeX().getName() + + "', failed to resolve type '" + typeSignature + "'"); + } + annotationTypes[i] = rType; + annotations[i] = new BcelAnnotation(annotation, rType); + } + } + } finally { + bitflag &= ~ANNOTATION_UNPACK_IN_PROGRESS; + } + } + } + + // --- + + public String getDeclaredGenericSignature() { + ensureGenericInfoProcessed(); + return declaredSignature; + } + + private void ensureGenericSignatureUnpacked() { + if ((bitflag & UNPACKED_GENERIC_SIGNATURE) != 0) { + return; + } + bitflag |= UNPACKED_GENERIC_SIGNATURE; + if (!getResolvedTypeX().getWorld().isInJava5Mode()) { + return; + } + GenericSignature.ClassSignature cSig = getGenericClassTypeSignature(); + if (cSig != null) { + formalsForResolution = cSig.formalTypeParameters; + if (isNested()) { + // we have to find any type variables from the outer type before + // proceeding with resolution. + GenericSignature.FormalTypeParameter[] extraFormals = getFormalTypeParametersFromOuterClass(); + if (extraFormals.length > 0) { + List allFormals = new ArrayList(); + for (int i = 0; i < formalsForResolution.length; i++) { + allFormals.add(formalsForResolution[i]); + } + for (int i = 0; i < extraFormals.length; i++) { + allFormals.add(extraFormals[i]); + } + formalsForResolution = new GenericSignature.FormalTypeParameter[allFormals.size()]; + allFormals.toArray(formalsForResolution); + } + } + GenericSignature.ClassTypeSignature superSig = cSig.superclassSignature; + try { + // this.superClass = + // BcelGenericSignatureToTypeXConverter.classTypeSignature2TypeX( + // superSig, formalsForResolution, + // getResolvedTypeX().getWorld()); + + ResolvedType rt = BcelGenericSignatureToTypeXConverter.classTypeSignature2TypeX(superSig, formalsForResolution, + getResolvedTypeX().getWorld()); + this.superclassSignature = rt.getSignature(); + this.superclassName = rt.getName(); + + } catch (GenericSignatureFormatException e) { + // development bug, fail fast with good info + throw new IllegalStateException("While determining the generic superclass of " + this.className + + " with generic signature " + getDeclaredGenericSignature() + " the following error was detected: " + + e.getMessage()); + } + // this.interfaces = new + // ResolvedType[cSig.superInterfaceSignatures.length]; + if (cSig.superInterfaceSignatures.length == 0) { + this.interfaceSignatures = NO_INTERFACE_SIGS; + } else { + this.interfaceSignatures = new String[cSig.superInterfaceSignatures.length]; + for (int i = 0; i < cSig.superInterfaceSignatures.length; i++) { + try { + // this.interfaces[i] = + // BcelGenericSignatureToTypeXConverter. + // classTypeSignature2TypeX( + // cSig.superInterfaceSignatures[i], + // formalsForResolution, + // getResolvedTypeX().getWorld()); + this.interfaceSignatures[i] = BcelGenericSignatureToTypeXConverter.classTypeSignature2TypeX( + cSig.superInterfaceSignatures[i], formalsForResolution, getResolvedTypeX().getWorld()) + .getSignature(); + } catch (GenericSignatureFormatException e) { + // development bug, fail fast with good info + throw new IllegalStateException("While determing the generic superinterfaces of " + this.className + + " with generic signature " + getDeclaredGenericSignature() + + " the following error was detected: " + e.getMessage()); + } + } + } + } + if (isGeneric()) { + // update resolved typex to point at generic type not raw type. + ReferenceType genericType = (ReferenceType) this.resolvedTypeX.getGenericType(); + // genericType.setSourceContext(this.resolvedTypeX.getSourceContext()); + // Can be null if unpacking whilst building the bcel delegate (in call hierarchy from BcelWorld.addSourceObjectType() + // line 453) - see 317139 + if (genericType != null) { + genericType.setStartPos(this.resolvedTypeX.getStartPos()); + this.resolvedTypeX = genericType; + } + } + } + + public GenericSignature.FormalTypeParameter[] getAllFormals() { + ensureGenericSignatureUnpacked(); + if (formalsForResolution == null) { + return new GenericSignature.FormalTypeParameter[0]; + } else { + return formalsForResolution; + } + } + + public ResolvedType getOuterClass() { + if (!isNested()) { + throw new IllegalStateException("Can't get the outer class of non-nested type: " + className); + } + + // try finding outer class name from InnerClasses attribute assigned to this class + for (Attribute attr : javaClass.getAttributes()) { + if (attr instanceof InnerClasses) { + // search for InnerClass entry that has current class as inner and some other class as outer + InnerClass[] innerClss = ((InnerClasses) attr).getInnerClasses(); + ConstantPool cpool = javaClass.getConstantPool(); + for (InnerClass innerCls : innerClss) { + + // skip entries that miss any necessary component, 0 index means "undefined", from JVM Spec 2nd ed. par. 4.7.5 + if (innerCls.getInnerClassIndex() == 0 || innerCls.getOuterClassIndex() == 0) { + continue; + } + + // resolve inner class name, check if it matches current class name + ConstantClass innerClsInfo = (ConstantClass) cpool.getConstant(innerCls.getInnerClassIndex()); + + // class names in constant pool use '/' instead of '.', from JVM Spec 2nd ed. par. 4.2 + String innerClsName = cpool.getConstantUtf8(innerClsInfo.getNameIndex()).getValue().replace('/', '.'); + + if (innerClsName.compareTo(className) == 0) { + // resolve outer class name + ConstantClass outerClsInfo = (ConstantClass) cpool.getConstant(innerCls.getOuterClassIndex()); + + // class names in constant pool use '/' instead of '.', from JVM Spec 2nd ed. par. 4.2 + String outerClsName = cpool.getConstantUtf8(outerClsInfo.getNameIndex()).getValue().replace('/', '.'); + + UnresolvedType outer = UnresolvedType.forName(outerClsName); + return outer.resolve(getResolvedTypeX().getWorld()); + } + } + } + } + + for (Attribute attr : javaClass.getAttributes()) { // bug339300 + ConstantPool cpool = javaClass.getConstantPool(); + if (attr instanceof EnclosingMethod) { + EnclosingMethod enclosingMethodAttribute = (EnclosingMethod) attr; + if (enclosingMethodAttribute.getEnclosingClassIndex() != 0) { + ConstantClass outerClassInfo = enclosingMethodAttribute.getEnclosingClass(); + String outerClassName = cpool.getConstantUtf8(outerClassInfo.getNameIndex()).getValue().replace('/', '.'); + UnresolvedType outer = UnresolvedType.forName(outerClassName); + return outer.resolve(getResolvedTypeX().getWorld()); + } + } + } + + // try finding outer class name by assuming standard class name mangling convention of javac for this class + int lastDollar = className.lastIndexOf('$'); + if (lastDollar == -1) { + // Is this class damaged/obfuscated? Why did we think it was nested but couldn't find the parent using + // the attributes above. For now just ignore it... I wonder when ignoring this will come back to bite! + return null; + } + String superClassName = className.substring(0, lastDollar); + UnresolvedType outer = UnresolvedType.forName(superClassName); + return outer.resolve(getResolvedTypeX().getWorld()); + } + + private void ensureGenericInfoProcessed() { + if ((bitflag & DISCOVERED_DECLARED_SIGNATURE) != 0) { + return; + } + bitflag |= DISCOVERED_DECLARED_SIGNATURE; + Signature sigAttr = AttributeUtils.getSignatureAttribute(javaClass.getAttributes()); + declaredSignature = (sigAttr == null ? null : sigAttr.getSignature()); + if (declaredSignature != null) { + isGenericType = (declaredSignature.charAt(0) == '<'); + } + } + + public boolean isGeneric() { + ensureGenericInfoProcessed(); + return isGenericType; + } + + @Override + public String toString() { + return (javaClass == null ? "BcelObjectType" : "BcelObjectTypeFor:" + className); + } + + // --- state management + + public void evictWeavingState() { + // Can't chuck all this away + if (getResolvedTypeX().getWorld().couldIncrementalCompileFollow()) { + return; + } + + if (javaClass != null) { + // Force retrieval of any lazy information + ensureAnnotationsUnpacked(); + ensureGenericInfoProcessed(); + + getDeclaredInterfaces(); + getDeclaredFields(); + getDeclaredMethods(); + // The lazyClassGen is preserved for aspects - it exists to enable + // around advice + // inlining since the method will need 'injecting' into the affected + // class. If + // XnoInline is on, we can chuck away the lazyClassGen since it + // won't be required + // later. + if (getResolvedTypeX().getWorld().isXnoInline()) { + lazyClassGen = null; + } + + // discard expensive bytecode array containing reweavable info + if (weaverState != null) { + weaverState.setReweavable(false); + weaverState.setUnwovenClassFileData(null); + } + for (int i = methods.length - 1; i >= 0; i--) { + methods[i].evictWeavingState(); + } + for (int i = fields.length - 1; i >= 0; i--) { + fields[i].evictWeavingState(); + } + javaClass = null; + this.artificial = true; + // setSourceContext(SourceContextImpl.UNKNOWN_SOURCE_CONTEXT); // + // bit naughty + // interfaces=null; // force reinit - may get us the right + // instances! + // superClass=null; + } + } + + public void weavingCompleted() { + hasBeenWoven = true; + if (getResolvedTypeX().getWorld().isRunMinimalMemory()) { + evictWeavingState(); + } + if (getSourceContext() != null && !getResolvedTypeX().isAspect()) { + getSourceContext().tidy(); + } + } + + public boolean hasBeenWoven() { + return hasBeenWoven; + } + + @Override + public boolean copySourceContext() { + return false; + } + + public void setExposedToWeaver(boolean b) { + exposedToWeaver = b; + } + + @Override + public int getCompilerVersion() { + return wvInfo.getMajorVersion(); + } + + public void ensureConsistent() { + superTypeReference.clear(); + superInterfaceReferences.clear(); + } + + public boolean isWeavable() { + return true; + } +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/BcelPerClauseAspectAdder.java b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelPerClauseAspectAdder.java new file mode 100644 index 000000000..b8ede0a9a --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelPerClauseAspectAdder.java @@ -0,0 +1,560 @@ +/******************************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * initial implementation Alexandre Vasseur + *******************************************************************************/ +package org.aspectj.weaver.bcel; + +import org.aspectj.apache.bcel.Constants; +import org.aspectj.apache.bcel.generic.InstructionBranch; +import org.aspectj.apache.bcel.generic.InstructionConstants; +import org.aspectj.apache.bcel.generic.InstructionFactory; +import org.aspectj.apache.bcel.generic.InstructionHandle; +import org.aspectj.apache.bcel.generic.InstructionList; +import org.aspectj.apache.bcel.generic.ObjectType; +import org.aspectj.apache.bcel.generic.ReferenceType; +import org.aspectj.apache.bcel.generic.Type; +import org.aspectj.weaver.AjAttribute; +import org.aspectj.weaver.AjcMemberMaker; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.NameMangler; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.patterns.PerClause; + +/** + * Adds aspectOf(), hasAspect() etc to the annotation defined aspects + * + * @author Alexandre Vasseur + * @author Andy Clement + */ +public class BcelPerClauseAspectAdder extends BcelTypeMunger { + + private final PerClause.Kind kind; + + private boolean hasGeneratedInner = false; + + public BcelPerClauseAspectAdder(ResolvedType aspect, PerClause.Kind kind) { + super(null, aspect); + this.kind = kind; + if (kind == PerClause.SINGLETON || kind == PerClause.PERTYPEWITHIN || kind == PerClause.PERCFLOW) { + // no inner needed + hasGeneratedInner = true; + } + } + + public boolean munge(BcelClassWeaver weaver) { + LazyClassGen gen = weaver.getLazyClassGen(); + + doAggressiveInner(gen); + + // Only munge the aspect type + if (!gen.getType().equals(aspectType)) { + return false; + } + + return doMunge(gen, true); + } + + public boolean forceMunge(LazyClassGen gen, boolean checkAlreadyThere) { + doAggressiveInner(gen); + return doMunge(gen, checkAlreadyThere); + } + + private void doAggressiveInner(LazyClassGen gen) { + // agressively generate the inner interface if any + // Note: we do so because of the bug #75442 that leads to have this interface implemented by all classes and not + // only those matched by the per clause, which fails under LTW since the very first class + // gets weaved and impl this interface that is still not defined. + if (!hasGeneratedInner) { + if (kind == PerClause.PEROBJECT) {// redundant test - see constructor, but safer + // inner class + UnresolvedType interfaceTypeX = AjcMemberMaker.perObjectInterfaceType(aspectType); + LazyClassGen interfaceGen = new LazyClassGen(interfaceTypeX.getName(), "java.lang.Object", null, + Constants.ACC_INTERFACE + Constants.ACC_PUBLIC + Constants.ACC_ABSTRACT, new String[0], getWorld()); + interfaceGen.addMethodGen(makeMethodGen(interfaceGen, AjcMemberMaker.perObjectInterfaceGet(aspectType))); + interfaceGen.addMethodGen(makeMethodGen(interfaceGen, AjcMemberMaker.perObjectInterfaceSet(aspectType))); + // not really an inner class of it but that does not matter, we pass back to the LTW + gen.addGeneratedInner(interfaceGen); + } + hasGeneratedInner = true; + } + } + + private boolean doMunge(LazyClassGen gen, boolean checkAlreadyThere) { + if (checkAlreadyThere && hasPerClauseMembersAlready(gen)) { + return false; + } + + generatePerClauseMembers(gen); + + if (kind == PerClause.SINGLETON) { + generatePerSingletonAspectOfMethod(gen); + generatePerSingletonHasAspectMethod(gen); + generatePerSingletonAjcClinitMethod(gen); + } else if (kind == PerClause.PEROBJECT) { + generatePerObjectAspectOfMethod(gen); + generatePerObjectHasAspectMethod(gen); + generatePerObjectBindMethod(gen); + // these will be added by the PerObjectInterface munger that affects the type - pr144602 + // generatePerObjectGetSetMethods(gen); + } else if (kind == PerClause.PERCFLOW) { + generatePerCflowAspectOfMethod(gen); + generatePerCflowHasAspectMethod(gen); + generatePerCflowPushMethod(gen); + generatePerCflowAjcClinitMethod(gen); + } else if (kind == PerClause.PERTYPEWITHIN) { + generatePerTWAspectOfMethod(gen); + generatePerTWHasAspectMethod(gen); + generatePerTWGetInstanceMethod(gen); + generatePerTWCreateAspectInstanceMethod(gen); + generatePerTWGetWithinTypeNameMethod(gen); + } else { + throw new Error("should not happen - not such kind " + kind.getName()); + } + return true; + } + + public ResolvedMember getMatchingSyntheticMember(Member member) { + return null; + } + + public ResolvedMember getSignature() { + return null; + } + + public boolean matches(ResolvedType onType) { + // cannot always do the right thing because may need to eagerly generate ajcMightHaveAspect interface for LTW (says Alex) + if (hasGeneratedInner) { // pr237419 - not always going to generate the marker interface + return aspectType.equals(onType); + } else { + return true; + } + } + + private boolean hasPerClauseMembersAlready(LazyClassGen classGen) { + ResolvedMember[] methods = classGen.getBcelObjectType().getDeclaredMethods(); + for (int i = 0; i < methods.length; i++) { + ResolvedMember method = methods[i]; + if ("aspectOf".equals(method.getName())) { + if ("()".equals(method.getParameterSignature()) && (kind == PerClause.SINGLETON || kind == PerClause.PERCFLOW)) { + return true; + } else if ("(Ljava/lang/Object;)".equals(method.getParameterSignature()) && kind == PerClause.PEROBJECT) { + return true; + } else if ("(Ljava/lang/Class;)".equals(method.getParameterSignature()) && kind == PerClause.PERTYPEWITHIN) { + return true; + } + } + } + return false; + } + + private void generatePerClauseMembers(LazyClassGen classGen) { + // FIXME Alex handle when field already there - or handle it with / similar to isAnnotationDefinedAspect() + // for that use aspectType and iterate on the fields. + + // FIXME Alex percflowX is not using this one but AJ code style does generate it so.. + ResolvedMember failureFieldInfo = AjcMemberMaker.initFailureCauseField(aspectType); + if (kind == PerClause.SINGLETON) { + classGen.addField(makeFieldGen(classGen, failureFieldInfo), null); + } + + if (kind == PerClause.SINGLETON) { + ResolvedMember perSingletonFieldInfo = AjcMemberMaker.perSingletonField(aspectType); + classGen.addField(makeFieldGen(classGen, perSingletonFieldInfo), null); + // pr144602 - don't need to do this, PerObjectInterface munger will do it + // } else if (kind == PerClause.PEROBJECT) { + // ResolvedMember perObjectFieldInfo = AjcMemberMaker.perObjectField(aspectType, aspectType); + // classGen.addField(makeFieldGen(classGen, perObjectFieldInfo).(), null); + // // if lazy generation of the inner interface MayHaveAspect works on LTW (see previous note) + // // it should be done here. + } else if (kind == PerClause.PERCFLOW) { + ResolvedMember perCflowFieldInfo = AjcMemberMaker.perCflowField(aspectType); + classGen.addField(makeFieldGen(classGen, perCflowFieldInfo), null); + } else if (kind == PerClause.PERTYPEWITHIN) { + ResolvedMember perTypeWithinForField = AjcMemberMaker.perTypeWithinWithinTypeField(aspectType, aspectType); + classGen.addField(makeFieldGen(classGen, perTypeWithinForField), null); + } + } + + private void generatePerSingletonAspectOfMethod(LazyClassGen classGen) { + InstructionFactory factory = classGen.getFactory(); + LazyMethodGen method = makeMethodGen(classGen, AjcMemberMaker.perSingletonAspectOfMethod(aspectType)); + flagAsSynthetic(method, false); + classGen.addMethodGen(method); + + InstructionList il = method.getBody(); + il.append(Utility.createGet(factory, AjcMemberMaker.perSingletonField(aspectType))); + InstructionBranch ifNotNull = InstructionFactory.createBranchInstruction(Constants.IFNONNULL, null); + il.append(ifNotNull); + il.append(factory.createNew(AjcMemberMaker.NO_ASPECT_BOUND_EXCEPTION.getName())); + il.append(InstructionConstants.DUP); + il.append(InstructionFactory.PUSH(classGen.getConstantPool(), aspectType.getName())); + il.append(Utility.createGet(factory, AjcMemberMaker.initFailureCauseField(aspectType))); + il.append(factory.createInvoke(AjcMemberMaker.NO_ASPECT_BOUND_EXCEPTION.getName(), "", Type.VOID, new Type[] { + Type.STRING, new ObjectType("java.lang.Throwable") }, Constants.INVOKESPECIAL)); + il.append(InstructionConstants.ATHROW); + InstructionHandle ifElse = il.append(Utility.createGet(factory, AjcMemberMaker.perSingletonField(aspectType))); + il.append(InstructionFactory.createReturn(Type.OBJECT)); + ifNotNull.setTarget(ifElse); + } + + private void generatePerSingletonHasAspectMethod(LazyClassGen classGen) { + InstructionFactory factory = classGen.getFactory(); + LazyMethodGen method = makeMethodGen(classGen, AjcMemberMaker.perSingletonHasAspectMethod(aspectType)); + flagAsSynthetic(method, false); + classGen.addMethodGen(method); + + InstructionList il = method.getBody(); + il.append(Utility.createGet(factory, AjcMemberMaker.perSingletonField(aspectType))); + InstructionBranch ifNull = InstructionFactory.createBranchInstruction(Constants.IFNULL, null); + il.append(ifNull); + il.append(InstructionFactory.PUSH(classGen.getConstantPool(), true)); + il.append(InstructionFactory.createReturn(Type.INT)); + InstructionHandle ifElse = il.append(InstructionFactory.PUSH(classGen.getConstantPool(), false)); + il.append(InstructionFactory.createReturn(Type.INT)); + ifNull.setTarget(ifElse); + } + + private void generatePerSingletonAjcClinitMethod(LazyClassGen classGen) { + InstructionFactory factory = classGen.getFactory(); + LazyMethodGen method = makeMethodGen(classGen, AjcMemberMaker.ajcPostClinitMethod(aspectType)); + flagAsSynthetic(method, true); + classGen.addMethodGen(method); + + InstructionList il = method.getBody(); + il.append(factory.createNew(aspectType.getName())); + il.append(InstructionConstants.DUP); + il.append(factory.createInvoke(aspectType.getName(), "", Type.VOID, Type.NO_ARGS, Constants.INVOKESPECIAL)); + il.append(Utility.createSet(factory, AjcMemberMaker.perSingletonField(aspectType))); + il.append(InstructionFactory.createReturn(Type.VOID)); + + // patch to delegate to ajc$postClinit at the end + LazyMethodGen clinit = classGen.getStaticInitializer(); + il = new InstructionList(); + InstructionHandle tryStart = il.append(factory.createInvoke(aspectType.getName(), NameMangler.AJC_POST_CLINIT_NAME, + Type.VOID, Type.NO_ARGS, Constants.INVOKESTATIC)); + InstructionBranch tryEnd = InstructionFactory.createBranchInstruction(Constants.GOTO, null); + il.append(tryEnd); + InstructionHandle handler = il.append(InstructionConstants.ASTORE_0); + il.append(InstructionConstants.ALOAD_0); + il.append(Utility.createSet(factory, AjcMemberMaker.initFailureCauseField(aspectType))); + il.append(InstructionFactory.createReturn(Type.VOID)); + tryEnd.setTarget(il.getEnd()); + + // replace the original "return" with a "nop" + // TODO AV - a bit odd, looks like Bcel alters bytecode and has a IMPDEP1 in its representation + if (clinit.getBody().getEnd().getInstruction().opcode == Constants.IMPDEP1) { + clinit.getBody().getEnd().getPrev().setInstruction(InstructionConstants.NOP); + } + clinit.getBody().getEnd().setInstruction(InstructionConstants.NOP); + clinit.getBody().append(il); + + clinit.addExceptionHandler(tryStart, handler.getPrev(), handler, new ObjectType("java.lang.Throwable"), false); + } + + private void generatePerObjectAspectOfMethod(LazyClassGen classGen) { + InstructionFactory factory = classGen.getFactory(); + ReferenceType interfaceType = (ReferenceType) BcelWorld.makeBcelType(AjcMemberMaker.perObjectInterfaceType(aspectType)); + LazyMethodGen method = makeMethodGen(classGen, AjcMemberMaker.perObjectAspectOfMethod(aspectType)); + flagAsSynthetic(method, false); + classGen.addMethodGen(method); + + InstructionList il = method.getBody(); + il.append(InstructionConstants.ALOAD_0); + il.append(factory.createInstanceOf(interfaceType)); + InstructionBranch ifEq = InstructionFactory.createBranchInstruction(Constants.IFEQ, null); + il.append(ifEq); + il.append(InstructionConstants.ALOAD_0); + il.append(factory.createCheckCast(interfaceType)); + il.append(Utility.createInvoke(factory, Constants.INVOKEINTERFACE, AjcMemberMaker.perObjectInterfaceGet(aspectType))); + il.append(InstructionConstants.DUP); + InstructionBranch ifNull = InstructionFactory.createBranchInstruction(Constants.IFNULL, null); + il.append(ifNull); + il.append(InstructionFactory.createReturn(BcelWorld.makeBcelType(aspectType))); + InstructionHandle ifNullElse = il.append(InstructionConstants.POP); + ifNull.setTarget(ifNullElse); + InstructionHandle ifEqElse = il.append(factory.createNew(AjcMemberMaker.NO_ASPECT_BOUND_EXCEPTION.getName())); + ifEq.setTarget(ifEqElse); + il.append(InstructionConstants.DUP); + il.append(factory.createInvoke(AjcMemberMaker.NO_ASPECT_BOUND_EXCEPTION.getName(), "", Type.VOID, Type.NO_ARGS, + Constants.INVOKESPECIAL)); + il.append(InstructionConstants.ATHROW); + } + + private void generatePerObjectHasAspectMethod(LazyClassGen classGen) { + InstructionFactory factory = classGen.getFactory(); + ReferenceType interfaceType = (ReferenceType) BcelWorld.makeBcelType(AjcMemberMaker.perObjectInterfaceType(aspectType)); + LazyMethodGen method = makeMethodGen(classGen, AjcMemberMaker.perObjectHasAspectMethod(aspectType)); + flagAsSynthetic(method, false); + classGen.addMethodGen(method); + + InstructionList il = method.getBody(); + il.append(InstructionConstants.ALOAD_0); + il.append(factory.createInstanceOf(interfaceType)); + InstructionBranch ifEq = InstructionFactory.createBranchInstruction(Constants.IFEQ, null); + il.append(ifEq); + il.append(InstructionConstants.ALOAD_0); + il.append(factory.createCheckCast(interfaceType)); + il.append(Utility.createInvoke(factory, Constants.INVOKEINTERFACE, AjcMemberMaker.perObjectInterfaceGet(aspectType))); + InstructionBranch ifNull = InstructionFactory.createBranchInstruction(Constants.IFNULL, null); + il.append(ifNull); + il.append(InstructionConstants.ICONST_1); + il.append(InstructionFactory.createReturn(Type.INT)); + InstructionHandle ifEqElse = il.append(InstructionConstants.ICONST_0); + ifEq.setTarget(ifEqElse); + ifNull.setTarget(ifEqElse); + il.append(InstructionFactory.createReturn(Type.INT)); + } + + private void generatePerObjectBindMethod(LazyClassGen classGen) { + InstructionFactory factory = classGen.getFactory(); + ReferenceType interfaceType = (ReferenceType) BcelWorld.makeBcelType(AjcMemberMaker.perObjectInterfaceType(aspectType)); + LazyMethodGen method = makeMethodGen(classGen, AjcMemberMaker.perObjectBind(aspectType)); + flagAsSynthetic(method, true); + classGen.addMethodGen(method); + + InstructionList il = method.getBody(); + il.append(InstructionConstants.ALOAD_0); + il.append(factory.createInstanceOf(interfaceType)); + InstructionBranch ifEq = InstructionFactory.createBranchInstruction(Constants.IFEQ, null); + il.append(ifEq); + il.append(InstructionConstants.ALOAD_0); + il.append(factory.createCheckCast(interfaceType)); + il.append(Utility.createInvoke(factory, Constants.INVOKEINTERFACE, AjcMemberMaker.perObjectInterfaceGet(aspectType))); + InstructionBranch ifNonNull = InstructionFactory.createBranchInstruction(Constants.IFNONNULL, null); + il.append(ifNonNull); + il.append(InstructionConstants.ALOAD_0); + il.append(factory.createCheckCast(interfaceType)); + il.append(factory.createNew(aspectType.getName())); + il.append(InstructionConstants.DUP); + il.append(factory.createInvoke(aspectType.getName(), "", Type.VOID, Type.NO_ARGS, Constants.INVOKESPECIAL)); + il.append(Utility.createInvoke(factory, Constants.INVOKEINTERFACE, AjcMemberMaker.perObjectInterfaceSet(aspectType))); + InstructionHandle end = il.append(InstructionFactory.createReturn(Type.VOID)); + ifEq.setTarget(end); + ifNonNull.setTarget(end); + } + + // private void generatePerObjectGetSetMethods(LazyClassGen classGen) { + // InstructionFactory factory = classGen.getFactory(); + // + // LazyMethodGen methodGet = makeMethodGen(classGen, AjcMemberMaker.perObjectInterfaceGet(aspectType)); + // flagAsSynthetic(methodGet, true); + // classGen.addMethodGen(methodGet); + // InstructionList ilGet = methodGet.getBody(); + // ilGet = new InstructionList(); + // ilGet.append(InstructionConstants.ALOAD_0); + // ilGet.append(Utility.createGet(factory, AjcMemberMaker.perObjectField(aspectType, aspectType))); + // ilGet.append(InstructionFactory.createReturn(Type.OBJECT)); + // + // LazyMethodGen methodSet = makeMethodGen(classGen, AjcMemberMaker.perObjectInterfaceSet(aspectType)); + // flagAsSynthetic(methodSet, true); + // classGen.addMethodGen(methodSet); + // InstructionList ilSet = methodSet.getBody(); + // ilSet = new InstructionList(); + // ilSet.append(InstructionConstants.ALOAD_0); + // ilSet.append(InstructionConstants.ALOAD_1); + // ilSet.append(Utility.createSet(factory, AjcMemberMaker.perObjectField(aspectType, aspectType))); + // ilSet.append(InstructionFactory.createReturn(Type.VOID)); + // } + + private void generatePerCflowAspectOfMethod(LazyClassGen classGen) { + InstructionFactory factory = classGen.getFactory(); + LazyMethodGen method = makeMethodGen(classGen, AjcMemberMaker.perCflowAspectOfMethod(aspectType)); + flagAsSynthetic(method, false); + classGen.addMethodGen(method); + + InstructionList il = method.getBody(); + il.append(Utility.createGet(factory, AjcMemberMaker.perCflowField(aspectType))); + il.append(Utility.createInvoke(factory, Constants.INVOKEVIRTUAL, AjcMemberMaker.cflowStackPeekInstance())); + il.append(factory.createCheckCast((ReferenceType) BcelWorld.makeBcelType(aspectType))); + il.append(InstructionFactory.createReturn(Type.OBJECT)); + } + + private void generatePerCflowHasAspectMethod(LazyClassGen classGen) { + InstructionFactory factory = classGen.getFactory(); + LazyMethodGen method = makeMethodGen(classGen, AjcMemberMaker.perCflowHasAspectMethod(aspectType)); + flagAsSynthetic(method, false); + classGen.addMethodGen(method); + + InstructionList il = method.getBody(); + il.append(Utility.createGet(factory, AjcMemberMaker.perCflowField(aspectType))); + il.append(Utility.createInvoke(factory, Constants.INVOKEVIRTUAL, AjcMemberMaker.cflowStackIsValid())); + il.append(InstructionFactory.createReturn(Type.INT)); + } + + private void generatePerCflowPushMethod(LazyClassGen classGen) { + InstructionFactory factory = classGen.getFactory(); + LazyMethodGen method = makeMethodGen(classGen, AjcMemberMaker.perCflowPush(aspectType)); + flagAsSynthetic(method, true); + classGen.addMethodGen(method); + + InstructionList il = method.getBody(); + il.append(Utility.createGet(factory, AjcMemberMaker.perCflowField(aspectType))); + il.append(factory.createNew(aspectType.getName())); + il.append(InstructionConstants.DUP); + il.append(factory.createInvoke(aspectType.getName(), "", Type.VOID, Type.NO_ARGS, Constants.INVOKESPECIAL)); + il.append(Utility.createInvoke(factory, Constants.INVOKEVIRTUAL, AjcMemberMaker.cflowStackPushInstance())); + il.append(InstructionFactory.createReturn(Type.VOID)); + } + + private void generatePerCflowAjcClinitMethod(LazyClassGen classGen) { + InstructionFactory factory = classGen.getFactory(); + + LazyMethodGen method = classGen.getAjcPreClinit(); // Creates a clinit if there isn't one + + InstructionList il = new InstructionList(); + il.append(factory.createNew(AjcMemberMaker.CFLOW_STACK_TYPE.getName())); + il.append(InstructionConstants.DUP); + il.append(factory.createInvoke(AjcMemberMaker.CFLOW_STACK_TYPE.getName(), "", Type.VOID, Type.NO_ARGS, + Constants.INVOKESPECIAL)); + il.append(Utility.createSet(factory, AjcMemberMaker.perCflowField(aspectType))); + method.getBody().insert(il); + } + + private void generatePerTWAspectOfMethod(LazyClassGen classGen) { + InstructionFactory factory = classGen.getFactory(); + LazyMethodGen method = makeMethodGen(classGen, AjcMemberMaker.perTypeWithinAspectOfMethod(aspectType, classGen.getWorld() + .isInJava5Mode())); + flagAsSynthetic(method, false); + classGen.addMethodGen(method); + + InstructionList il = method.getBody(); + InstructionHandle tryStart = il.append(InstructionConstants.ALOAD_0); + + il.append(Utility.createInvoke(factory, Constants.INVOKESTATIC, AjcMemberMaker.perTypeWithinGetInstance(aspectType))); + il.append(InstructionConstants.ASTORE_1); + il.append(InstructionConstants.ALOAD_1); + InstructionBranch ifNonNull = InstructionFactory.createBranchInstruction(Constants.IFNONNULL, null); + il.append(ifNonNull); + il.append(factory.createNew(AjcMemberMaker.NO_ASPECT_BOUND_EXCEPTION.getName())); + il.append(InstructionConstants.DUP); + il.append(InstructionFactory.PUSH(classGen.getConstantPool(), aspectType.getName())); + il.append(InstructionConstants.ACONST_NULL); + il.append(factory.createInvoke(AjcMemberMaker.NO_ASPECT_BOUND_EXCEPTION.getName(), "", Type.VOID, new Type[] { + Type.STRING, new ObjectType("java.lang.Throwable") }, Constants.INVOKESPECIAL)); + il.append(InstructionConstants.ATHROW); + InstructionHandle ifElse = il.append(InstructionConstants.ALOAD_1); + ifNonNull.setTarget(ifElse); + il.append(InstructionFactory.createReturn(Type.OBJECT)); + + InstructionHandle handler = il.append(InstructionConstants.ASTORE_1); + il.append(factory.createNew(AjcMemberMaker.NO_ASPECT_BOUND_EXCEPTION.getName())); + il.append(InstructionConstants.DUP); + il.append(factory.createInvoke(AjcMemberMaker.NO_ASPECT_BOUND_EXCEPTION.getName(), "", Type.VOID, Type.NO_ARGS, + Constants.INVOKESPECIAL)); + il.append(InstructionConstants.ATHROW); + + method.addExceptionHandler(tryStart, handler.getPrev(), handler, new ObjectType("java.lang.Exception"), false); + } + + // Create 'public String getWithinTypeName() { return ajc$withinType;}' + private void generatePerTWGetWithinTypeNameMethod(LazyClassGen classGen) { + InstructionFactory factory = classGen.getFactory(); + LazyMethodGen method = makeMethodGen(classGen, AjcMemberMaker.perTypeWithinGetWithinTypeNameMethod(aspectType, classGen + .getWorld().isInJava5Mode())); + flagAsSynthetic(method, false); + classGen.addMethodGen(method); + // 0: aload_0 + // 1: getfield #14; //Field ajc$withinType:Ljava/lang/String; + // 4: areturn + InstructionList il = method.getBody(); + il.append(InstructionConstants.ALOAD_0); + il.append(Utility.createGet(factory, AjcMemberMaker.perTypeWithinWithinTypeField(aspectType, aspectType))); + il.append(InstructionConstants.ARETURN); + } + + private void generatePerTWHasAspectMethod(LazyClassGen classGen) { + InstructionFactory factory = classGen.getFactory(); + LazyMethodGen method = makeMethodGen(classGen, AjcMemberMaker.perTypeWithinHasAspectMethod(aspectType, classGen.getWorld() + .isInJava5Mode())); + flagAsSynthetic(method, false); + classGen.addMethodGen(method); + + InstructionList il = method.getBody(); + InstructionHandle tryStart = il.append(InstructionConstants.ALOAD_0); + il.append(Utility.createInvoke(factory, Constants.INVOKESTATIC, AjcMemberMaker.perTypeWithinGetInstance(aspectType))); + InstructionBranch ifNull = InstructionFactory.createBranchInstruction(Constants.IFNULL, null); + il.append(ifNull); + il.append(InstructionConstants.ICONST_1); + il.append(InstructionConstants.IRETURN); + InstructionHandle ifElse = il.append(InstructionConstants.ICONST_0); + ifNull.setTarget(ifElse); + il.append(InstructionConstants.IRETURN); + + InstructionHandle handler = il.append(InstructionConstants.ASTORE_1); + il.append(InstructionConstants.ICONST_0); + il.append(InstructionConstants.IRETURN); + + method.addExceptionHandler(tryStart, handler.getPrev(), handler, new ObjectType("java.lang.Exception"), false); + } + + private void generatePerTWGetInstanceMethod(LazyClassGen classGen) { + InstructionFactory factory = classGen.getFactory(); + LazyMethodGen method = makeMethodGen(classGen, AjcMemberMaker.perTypeWithinGetInstance(aspectType)); + flagAsSynthetic(method, true); + classGen.addMethodGen(method); + + InstructionList il = method.getBody(); + InstructionHandle tryStart = il.append(InstructionConstants.ALOAD_0); + il.append(InstructionFactory.PUSH(factory.getConstantPool(), NameMangler.perTypeWithinLocalAspectOf(aspectType))); + il.append(InstructionConstants.ACONST_NULL);// Class[] for "getDeclaredMethod" + il.append(factory.createInvoke("java/lang/Class", "getDeclaredMethod", Type.getType("Ljava/lang/reflect/Method;"), + new Type[] { Type.getType("Ljava/lang/String;"), Type.getType("[Ljava/lang/Class;") }, Constants.INVOKEVIRTUAL)); + il.append(InstructionConstants.ACONST_NULL);// Object for "invoke", static method + il.append(InstructionConstants.ACONST_NULL);// Object[] for "invoke", no arg + il.append(factory.createInvoke("java/lang/reflect/Method", "invoke", Type.OBJECT, new Type[] { + Type.getType("Ljava/lang/Object;"), Type.getType("[Ljava/lang/Object;") }, Constants.INVOKEVIRTUAL)); + il.append(factory.createCheckCast((ReferenceType) BcelWorld.makeBcelType(aspectType))); + il.append(InstructionConstants.ARETURN); + + InstructionHandle handler = il.append(InstructionConstants.ASTORE_1); + il.append(InstructionConstants.ACONST_NULL); + il.append(InstructionConstants.ARETURN); + + method.addExceptionHandler(tryStart, handler.getPrev(), handler, new ObjectType("java.lang.Exception"), false); + } + + private void generatePerTWCreateAspectInstanceMethod(LazyClassGen classGen) { + InstructionFactory factory = classGen.getFactory(); + LazyMethodGen method = makeMethodGen(classGen, AjcMemberMaker.perTypeWithinCreateAspectInstance(aspectType)); + flagAsSynthetic(method, true); + classGen.addMethodGen(method); + + InstructionList il = method.getBody(); + il.append(factory.createNew(aspectType.getName())); + il.append(InstructionConstants.DUP); + il.append(factory.createInvoke(aspectType.getName(), "", Type.VOID, Type.NO_ARGS, Constants.INVOKESPECIAL)); + il.append(InstructionConstants.ASTORE_1); + il.append(InstructionConstants.ALOAD_1); + il.append(InstructionConstants.ALOAD_0); + il.append(Utility.createSet(factory, AjcMemberMaker.perTypeWithinWithinTypeField(aspectType, aspectType))); + il.append(InstructionConstants.ALOAD_1); + il.append(InstructionConstants.ARETURN); + } + + /** + * Add standard Synthetic (if wished) and AjSynthetic (always) attributes + * + * @param methodGen + * @param makeJavaSynthetic true if standard Synthetic attribute must be set as well (invisible to user) + */ + private static void flagAsSynthetic(LazyMethodGen methodGen, boolean makeJavaSynthetic) { + if (makeJavaSynthetic) { + methodGen.makeSynthetic(); + } + methodGen.addAttribute(Utility + .bcelAttribute(new AjAttribute.AjSynthetic(), methodGen.getEnclosingClass().getConstantPool())); + } + + // public boolean isLateTypeMunger() { + // return true; + // } +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/BcelRenderer.java b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelRenderer.java new file mode 100644 index 000000000..e1f99439f --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelRenderer.java @@ -0,0 +1,270 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.bcel; + +import java.lang.reflect.Modifier; + +import org.aspectj.apache.bcel.Constants; +import org.aspectj.apache.bcel.generic.InstructionFactory; +import org.aspectj.apache.bcel.generic.InstructionHandle; +import org.aspectj.apache.bcel.generic.InstructionList; +import org.aspectj.apache.bcel.generic.ObjectType; +import org.aspectj.apache.bcel.generic.ReferenceType; +import org.aspectj.apache.bcel.generic.Type; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.MemberImpl; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.ast.And; +import org.aspectj.weaver.ast.Call; +import org.aspectj.weaver.ast.CallExpr; +import org.aspectj.weaver.ast.Expr; +import org.aspectj.weaver.ast.FieldGet; +import org.aspectj.weaver.ast.FieldGetCall; +import org.aspectj.weaver.ast.HasAnnotation; +import org.aspectj.weaver.ast.IExprVisitor; +import org.aspectj.weaver.ast.ITestVisitor; +import org.aspectj.weaver.ast.Instanceof; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Not; +import org.aspectj.weaver.ast.Or; +import org.aspectj.weaver.ast.Test; +import org.aspectj.weaver.ast.Var; +import org.aspectj.weaver.internal.tools.MatchingContextBasedTest; + +// we generate right to left, btw. +public final class BcelRenderer implements ITestVisitor, IExprVisitor { + + private InstructionList instructions; + private InstructionFactory fact; + private BcelWorld world; + + InstructionHandle sk, fk, next = null; + + private BcelRenderer(InstructionFactory fact, BcelWorld world) { + super(); + this.fact = fact; + this.world = world; + this.instructions = new InstructionList(); + } + + // ---- renderers + + public static InstructionList renderExpr(InstructionFactory fact, BcelWorld world, Expr e) { + BcelRenderer renderer = new BcelRenderer(fact, world); + e.accept(renderer); + return renderer.instructions; + } + + public static InstructionList renderExpr(InstructionFactory fact, BcelWorld world, Expr e, Type desiredType) { + BcelRenderer renderer = new BcelRenderer(fact, world); + e.accept(renderer); + InstructionList il = renderer.instructions; + il.append(Utility.createConversion(fact, BcelWorld.makeBcelType(e.getType()), desiredType)); + return il; + } + + public static InstructionList renderExprs(InstructionFactory fact, BcelWorld world, Expr[] es) { + BcelRenderer renderer = new BcelRenderer(fact, world); + for (int i = es.length - 1; i >= 0; i--) { + es[i].accept(renderer); + } + return renderer.instructions; + } + + /* + * Get the instructions representing this test. + * + * @param e test to render + * + * @param sk instructionHandle to jump to if our rendered check succeeds (typically start of advice) + * + * @param fk instructionHandle to jump to if our rendered check fails (typically after end of advice) + * + * @param next instructionHandle that will follow this generated code. Passing in null will generate one unnecessary GOTO + * instruction. + * + * @returns the instruction list representing this expression + */ + public static InstructionList renderTest(InstructionFactory fact, BcelWorld world, Test e, InstructionHandle sk, + InstructionHandle fk, InstructionHandle next) { + BcelRenderer renderer = new BcelRenderer(fact, world); + renderer.recur(e, sk, fk, next); + return renderer.instructions; + } + + // ---- recurrers + + private void recur(Test e, InstructionHandle sk, InstructionHandle fk, InstructionHandle next) { + this.sk = sk; + this.fk = fk; + this.next = next; + e.accept(this); + } + + // ---- test visitors + + public void visit(And e) { + InstructionHandle savedFk = fk; + recur(e.getRight(), sk, fk, next); + InstructionHandle ning = instructions.getStart(); + recur(e.getLeft(), ning, savedFk, ning); + } + + public void visit(Or e) { + InstructionHandle savedSk = sk; + recur(e.getRight(), sk, fk, next); + recur(e.getLeft(), savedSk, instructions.getStart(), instructions.getStart()); + } + + public void visit(Not e) { + recur(e.getBody(), fk, sk, next); + } + + public void visit(Instanceof i) { + instructions.insert(createJumpBasedOnBooleanOnStack()); + instructions.insert(Utility.createInstanceof(fact, (ReferenceType) BcelWorld.makeBcelType(i.getType()))); + i.getVar().accept(this); + } + + public void visit(HasAnnotation hasAnnotation) { + // in Java: + // foo.class.isAnnotationPresent(annotationClass); + // in bytecode: + + // ifnull? skip to the end if it is as getClass() will fail (see pr 257833) + + // load var onto the stack (done for us later) + // invokevirtual java/lang/Object.getClass:()Ljava/lang/Class + // ldc_w annotationClass + // invokevirtual java/lang/Class.isAnnotationPresent:(Ljava/lang/Class;)Z + InstructionList il = new InstructionList(); + + // If it is null jump past the advice call + il.append(InstructionFactory.createBranchInstruction(Constants.IFNULL, fk)); + + // Load up the var again + il.append(((BcelVar) hasAnnotation.getVar()).createLoad(fact)); + + Member getClass = MemberImpl.method(UnresolvedType.OBJECT, 0, UnresolvedType.JL_CLASS, "getClass", UnresolvedType.NONE); + il.append(Utility.createInvoke(fact, world, getClass)); + // aload annotationClass + il.append(fact.createConstant(new ObjectType(hasAnnotation.getAnnotationType().getName()))); + // int annClassIndex = fact.getConstantPool().addClass(hasAnnotation.getAnnotationType().getSignature()); + // il.append(new LDC_W(annClassIndex)); + Member isAnnotationPresent = MemberImpl.method(UnresolvedType.JL_CLASS, 0, UnresolvedType.BOOLEAN, "isAnnotationPresent", + new UnresolvedType[] { UnresolvedType.JL_CLASS }); + il.append(Utility.createInvoke(fact, world, isAnnotationPresent)); + il.append(createJumpBasedOnBooleanOnStack()); + instructions.insert(il); + hasAnnotation.getVar().accept(this); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ast.ITestVisitor#visit(org.aspectj.weaver.internal.tools.MatchingContextBasedTest) + */ + public void visit(MatchingContextBasedTest matchingContextTest) { + throw new UnsupportedOperationException("matching context extension not supported in bytecode weaving"); + } + + private InstructionList createJumpBasedOnBooleanOnStack() { + InstructionList il = new InstructionList(); + if (sk == fk) { + // don't bother generating if it doesn't matter + if (sk != next) { + il.insert(InstructionFactory.createBranchInstruction(Constants.GOTO, sk)); + } + return il; + } + + if (fk == next) { + il.insert(InstructionFactory.createBranchInstruction(Constants.IFNE, sk)); + } else if (sk == next) { + il.insert(InstructionFactory.createBranchInstruction(Constants.IFEQ, fk)); + } else { + il.insert(InstructionFactory.createBranchInstruction(Constants.GOTO, sk)); + il.insert(InstructionFactory.createBranchInstruction(Constants.IFEQ, fk)); + } + return il; + } + + public void visit(Literal literal) { + if (literal == Literal.FALSE) { + throw new BCException("visiting a false expression"); + } + } + + public void visit(Call call) { + Member method = call.getMethod(); + // assert method.isStatic() + Expr[] args = call.getArgs(); + InstructionList callIl = new InstructionList(); + for (int i = 0, len = args.length; i < len; i++) { + // XXX only correct for static method calls + Type desiredType = BcelWorld.makeBcelType(method.getParameterTypes()[i]); + Expr arg = args[i]; + // if arg is null it is because we couldn't bind it properly, for example see 162135 + if (arg == null) { + InstructionList iList = new InstructionList(); + iList.append(InstructionFactory.createNull(desiredType)); + callIl.append(iList); + } else { + callIl.append(renderExpr(fact, world, arg, desiredType)); + } + } + // System.out.println("rendered args: " + callIl); + callIl.append(Utility.createInvoke(fact, world, method)); + callIl.append(createJumpBasedOnBooleanOnStack()); + instructions.insert(callIl); + } + + public void visit(FieldGetCall fieldGetCall) { + Member field = fieldGetCall.getField(); + Member method = fieldGetCall.getMethod(); + InstructionList il = new InstructionList(); + il.append(Utility.createGet(fact, field)); + // assert !method.isStatic() + Expr[] args = fieldGetCall.getArgs(); + // System.out.println("args: " + Arrays.asList(args)); + il.append(renderExprs(fact, world, args)); + // System.out.println("rendered args: " + callIl); + il.append(Utility.createInvoke(fact, world, method)); + il.append(createJumpBasedOnBooleanOnStack()); + instructions.insert(il); + } + + // ---- expr visitors + + public void visit(Var var) { + BcelVar bvar = (BcelVar) var; + bvar.insertLoad(instructions, fact); + } + + public void visit(FieldGet fieldGet) { + Member field = fieldGet.getField(); + // assert field.isStatic() + instructions.insert(Utility.createGet(fact, field)); + } + + public void visit(CallExpr call) { + Member method = call.getMethod(); + // assert method.isStatic() + Expr[] args = call.getArgs(); + InstructionList callIl = renderExprs(fact, world, args); + callIl.append(Utility.createInvoke(fact, world, method)); + instructions.insert(callIl); + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/BcelShadow.java b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelShadow.java new file mode 100644 index 000000000..c93a0f26e --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelShadow.java @@ -0,0 +1,3425 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * Alexandre Vasseur support for @AJ aspects + * ******************************************************************/ + +package org.aspectj.weaver.bcel; + +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.aspectj.apache.bcel.Constants; +import org.aspectj.apache.bcel.classfile.ConstantPool; +import org.aspectj.apache.bcel.classfile.Field; +import org.aspectj.apache.bcel.generic.ArrayType; +import org.aspectj.apache.bcel.generic.FieldInstruction; +import org.aspectj.apache.bcel.generic.INVOKEINTERFACE; +import org.aspectj.apache.bcel.generic.Instruction; +import org.aspectj.apache.bcel.generic.InstructionBranch; +import org.aspectj.apache.bcel.generic.InstructionConstants; +import org.aspectj.apache.bcel.generic.InstructionFactory; +import org.aspectj.apache.bcel.generic.InstructionHandle; +import org.aspectj.apache.bcel.generic.InstructionLV; +import org.aspectj.apache.bcel.generic.InstructionList; +import org.aspectj.apache.bcel.generic.InstructionTargeter; +import org.aspectj.apache.bcel.generic.InvokeInstruction; +import org.aspectj.apache.bcel.generic.LineNumberTag; +import org.aspectj.apache.bcel.generic.LocalVariableTag; +import org.aspectj.apache.bcel.generic.MULTIANEWARRAY; +import org.aspectj.apache.bcel.generic.ObjectType; +import org.aspectj.apache.bcel.generic.TargetLostException; +import org.aspectj.apache.bcel.generic.Type; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.weaver.Advice; +import org.aspectj.weaver.AdviceKind; +import org.aspectj.weaver.AjcMemberMaker; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.ConcreteTypeMunger; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.MemberImpl; +import org.aspectj.weaver.NameMangler; +import org.aspectj.weaver.NewConstructorTypeMunger; +import org.aspectj.weaver.NewFieldTypeMunger; +import org.aspectj.weaver.NewMethodTypeMunger; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedMemberImpl; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.ShadowMunger; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Var; +import org.aspectj.weaver.patterns.AbstractPatternNodeVisitor; +import org.aspectj.weaver.patterns.AndPointcut; +import org.aspectj.weaver.patterns.NotPointcut; +import org.aspectj.weaver.patterns.OrPointcut; +import org.aspectj.weaver.patterns.ThisOrTargetPointcut; + +/* + * Some fun implementation stuff: + * + * * expressionKind advice is non-execution advice + * * may have a target. + * * if the body is extracted, it will be extracted into + * a static method. The first argument to the static + * method is the target + * * advice may expose a this object, but that's the advice's + * consideration, not ours. This object will NOT be cached in another + * local, but will always come from frame zero. + * + * * non-expressionKind advice is execution advice + * * may have a this. + * * target is same as this, and is exposed that way to advice + * (i.e., target will not be cached, will always come from frame zero) + * * if the body is extracted, it will be extracted into a method + * with same static/dynamic modifier as enclosing method. If non-static, + * target of callback call will be this. + * + * * because of these two facts, the setup of the actual arguments (including + * possible target) callback method is the same for both kinds of advice: + * push the targetVar, if it exists (it will not exist for advice on static + * things), then push all the argVars. + * + * Protected things: + * + * * the above is sufficient for non-expressionKind advice for protected things, + * since the target will always be this. + * + * * For expressionKind things, we have to modify the signature of the callback + * method slightly. For non-static expressionKind things, we modify + * the first argument of the callback method NOT to be the type specified + * by the method/field signature (the owner), but rather we type it to + * the currentlyEnclosing type. We are guaranteed this will be fine, + * since the verifier verifies that the target is a subtype of the currently + * enclosingType. + * + * Worries: + * + * * ConstructorCalls will be weirder than all of these, since they + * supposedly don't have a target (according to AspectJ), but they clearly + * do have a target of sorts, just one that needs to be pushed on the stack, + * dupped, and not touched otherwise until the constructor runs. + * + * @author Jim Hugunin + * @author Erik Hilsdale + * + */ + +public class BcelShadow extends Shadow { + + private static final String[] NoDeclaredExceptions = new String[0]; + + private ShadowRange range; + private final BcelWorld world; + private final LazyMethodGen enclosingMethod; + + // TESTING this will tell us if the optimisation succeeded *on the last shadow processed* + public static boolean appliedLazyTjpOptimization; + + // Some instructions have a target type that will vary + // from the signature (pr109728) (1.4 declaring type issue) + private String actualInstructionTargetType; + + /** + * This generates an unassociated shadow, rooted in a particular method but not rooted to any particular point in the code. It + * should be given to a rooted ShadowRange in the {@link ShadowRange#associateWithShadow(BcelShadow)} method. + */ + public BcelShadow(BcelWorld world, Kind kind, Member signature, LazyMethodGen enclosingMethod, BcelShadow enclosingShadow) { + super(kind, signature, enclosingShadow); + this.world = world; + this.enclosingMethod = enclosingMethod; + } + + // ---- copies all state, including Shadow's mungers... + + public BcelShadow copyInto(LazyMethodGen recipient, BcelShadow enclosing) { + BcelShadow s = new BcelShadow(world, getKind(), getSignature(), recipient, enclosing); + if (mungers.size() > 0) { + List src = mungers; + if (s.mungers == Collections.EMPTY_LIST) { + s.mungers = new ArrayList(); + } + List dest = s.mungers; + for (Iterator i = src.iterator(); i.hasNext();) { + dest.add(i.next()); + } + } + return s; + } + + // ---- overridden behaviour + + @Override + public World getIWorld() { + return world; + } + + // see comment in deleteNewAndDup + // } else if (inst.opcode == Constants.DUP_X2) { + // // This code seen in the wild (by Brad): + // // 40: new #12; //class java/lang/StringBuffer + // // STACK: STRINGBUFFER + // // 43: dup + // // STACK: STRINGBUFFER/STRINGBUFFER + // // 44: aload_0 + // // STACK: STRINGBUFFER/STRINGBUFFER/THIS + // // 45: dup_x2 + // // STACK: THIS/STRINGBUFFER/STRINGBUFFER/THIS + // // 46: getfield #36; //Field value:Ljava/lang/String; + // // STACK: THIS/STRINGBUFFER/STRINGBUFFER/STRING + // // 49: invokestatic #37; //Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String; + // // STACK: THIS/STRINGBUFFER/STRINGBUFFER/STRING + // // 52: invokespecial #19; //Method java/lang/StringBuffer."":(Ljava/lang/String;)V + // // STACK: THIS/STRINGBUFFER + // // 55: aload_1 + // // STACK: THIS/STRINGBUFFER/LOCAL1 + // // 56: invokevirtual #22; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer; + // // STACK: THIS/STRINGBUFFER + // // 59: invokevirtual #34; //Method java/lang/StringBuffer.toString:()Ljava/lang/String; + // // STACK: THIS/STRING + // // 62: putfield #36; //Field value:Ljava/lang/String; + // // STACK: + // // 65: return + // + // // if we attempt to match on the ctor call to StringBuffer. then we get into trouble. + // // if we simply delete the new/dup pair without fixing up the dup_x2 then the dup_x2 will fail due to there + // // not being 3 elements on the stack for it to work with. The fix *in this situation* is to change it to + // // a simple 'dup' + // + // // this fix is *not* very clean - but a general purpose decent solution will take much longer and this + // // bytecode sequence has only been seen once in the wild. + // ih.setInstruction(InstructionConstants.DUP); + + /** + * The new/dup (or new/dup_x1/swap) are removed and will be readded later (after the advice call) by the caller of this method. + * The groovy compiler produces unusual code where the new/dup isn't visible (when making a this() call from an existing ctor), + * an aload_0 is used to load the uninitialized object (as an example see the ctors in grails.util.BuildSettings). + * + * @return true if managed to remove them + */ + private boolean deleteNewAndDup() { + final ConstantPool cpool = getEnclosingClass().getConstantPool(); + int depth = 1; + InstructionHandle ih = range.getStart(); + + // Go back from where we are looking for 'NEW' that takes us to a stack depth of 0. INVOKESPECIAL + while (ih != null) { + Instruction inst = ih.getInstruction(); + if (inst.opcode == Constants.INVOKESPECIAL && ((InvokeInstruction) inst).getName(cpool).equals("")) { + depth++; + } else if (inst.opcode == Constants.NEW) { + depth--; + if (depth == 0) { + break; + } + // need a testcase to show this can really happen in a modern compiler - removed due to 315398 - moved this out to + // comment proceeding this method: + + } + ih = ih.getPrev(); + } + if (ih == null) { + return false; + } + // now IH points to the NEW. We're followed by the DUP, and that is followed + // by the actual instruction we care about. + InstructionHandle newHandle = ih; + InstructionHandle endHandle = newHandle.getNext(); + InstructionHandle nextHandle; + if (endHandle.getInstruction().opcode == Constants.DUP) { + nextHandle = endHandle.getNext(); + retargetFrom(newHandle, nextHandle); + retargetFrom(endHandle, nextHandle); + } else if (endHandle.getInstruction().opcode == Constants.DUP_X1) { + InstructionHandle dupHandle = endHandle; + endHandle = endHandle.getNext(); + nextHandle = endHandle.getNext(); + boolean skipEndRepositioning = false; + if (endHandle.getInstruction().opcode == Constants.SWAP) { + } else if (endHandle.getInstruction().opcode == Constants.IMPDEP1) { + skipEndRepositioning = true; // pr186884 + } else { + // XXX see next XXX comment + throw new RuntimeException("Unhandled kind of new " + endHandle); + } + // Now make any jumps to the 'new', the 'dup' or the 'end' now target the nextHandle + retargetFrom(newHandle, nextHandle); + retargetFrom(dupHandle, nextHandle); + if (!skipEndRepositioning) { + retargetFrom(endHandle, nextHandle); + } + } else { + endHandle = newHandle; + nextHandle = endHandle.getNext(); + retargetFrom(newHandle, nextHandle); + // add a POP here... we found a NEW w/o a dup or anything else, so + // we must be in statement context. + getRange().insert(InstructionConstants.POP, Range.OutsideAfter); + } + // assert (dupHandle.getInstruction() instanceof DUP); + + try { + range.getBody().delete(newHandle, endHandle); + } catch (TargetLostException e) { + throw new BCException("shouldn't happen"); + } + return true; + } + + private void retargetFrom(InstructionHandle old, InstructionHandle fresh) { + for (InstructionTargeter targeter : old.getTargetersCopy()) { + if (targeter instanceof ExceptionRange) { + ExceptionRange it = (ExceptionRange) targeter; + it.updateTarget(old, fresh, it.getBody()); + } else { + targeter.updateTarget(old, fresh); + } + } + } + + // records advice that is stopping us doing the lazyTjp optimization + private List badAdvice = null; + + public void addAdvicePreventingLazyTjp(BcelAdvice advice) { + if (badAdvice == null) { + badAdvice = new ArrayList(); + } + badAdvice.add(advice); + } + + @Override + protected void prepareForMungers() { + // if we're a constructor call, we need to remove the new:dup or the new:dup_x1:swap, + // and store all our arguments on the frame. + + // ??? This is a bit of a hack (for the Java langauge). We do this because + // we sometime add code "outsideBefore" when dealing with weaving join points. We only + // do this for exposing state that is on the stack. It turns out to just work for + // everything except for constructor calls and exception handlers. If we were to clean + // this up, every ShadowRange would have three instructionHandle points, the start of + // the arg-setup code, the start of the running code, and the end of the running code. + boolean deletedNewAndDup = true; + if (getKind() == ConstructorCall) { + if (!world.isJoinpointArrayConstructionEnabled() || !this.getSignature().getDeclaringType().isArray()) { + deletedNewAndDup = deleteNewAndDup(); // no new/dup for new array construction + } + initializeArgVars(); + } else if (getKind() == PreInitialization) { // pr74952 + ShadowRange range = getRange(); + range.insert(InstructionConstants.NOP, Range.InsideAfter); + } else if (getKind() == ExceptionHandler) { + + ShadowRange range = getRange(); + InstructionList body = range.getBody(); + InstructionHandle start = range.getStart(); + + // Create a store instruction to put the value from the top of the + // stack into a local variable slot. This is a trimmed version of + // what is in initializeArgVars() (since there is only one argument + // at a handler jp and only before advice is supported) (pr46298) + argVars = new BcelVar[1]; + // int positionOffset = (hasTarget() ? 1 : 0) + ((hasThis() && !getKind().isTargetSameAsThis()) ? 1 : 0); + UnresolvedType tx = getArgType(0); + argVars[0] = genTempVar(tx, "ajc$arg0"); + InstructionHandle insertedInstruction = range.insert(argVars[0].createStore(getFactory()), Range.OutsideBefore); + + // Now the exception range starts just after our new instruction. + // The next bit of code changes the exception range to point at + // the store instruction + for (InstructionTargeter t : start.getTargetersCopy()) { + if (t instanceof ExceptionRange) { + ExceptionRange er = (ExceptionRange) t; + er.updateTarget(start, insertedInstruction, body); + } + } + } + + // now we ask each munger to request our state + isThisJoinPointLazy = true;// world.isXlazyTjp(); // lazy is default now + + badAdvice = null; + for (ShadowMunger munger : mungers) { + munger.specializeOn(this); + } + + initializeThisJoinPoint(); + + if (thisJoinPointVar != null && !isThisJoinPointLazy && badAdvice != null && badAdvice.size() > 1) { + // something stopped us making it a lazy tjp + // can't build tjp lazily, no suitable test... + int valid = 0; + for (Iterator iter = badAdvice.iterator(); iter.hasNext();) { + BcelAdvice element = iter.next(); + ISourceLocation sLoc = element.getSourceLocation(); + if (sLoc != null && sLoc.getLine() > 0) { + valid++; + } + } + if (valid != 0) { + ISourceLocation[] badLocs = new ISourceLocation[valid]; + int i = 0; + for (Iterator iter = badAdvice.iterator(); iter.hasNext();) { + BcelAdvice element = iter.next(); + ISourceLocation sLoc = element.getSourceLocation(); + if (sLoc != null) { + badLocs[i++] = sLoc; + } + } + world.getLint().multipleAdviceStoppingLazyTjp + .signal(new String[] { this.toString() }, getSourceLocation(), badLocs); + } + } + badAdvice = null; + + // If we are an expression kind, we require our target/arguments on the stack + // before we do our actual thing. However, they may have been removed + // from the stack as the shadowMungers have requested state. + // if any of our shadowMungers requested either the arguments or target, + // the munger will have added code + // to pop the target/arguments into temporary variables, represented by + // targetVar and argVars. In such a case, we must make sure to re-push the + // values. + + // If we are nonExpressionKind, we don't expect arguments on the stack + // so this is moot. If our argVars happen to be null, then we know that + // no ShadowMunger has squirrelled away our arguments, so they're still + // on the stack. + InstructionFactory fact = getFactory(); + if (getKind().argsOnStack() && argVars != null) { + + // Special case first (pr46298). If we are an exception handler and the instruction + // just after the shadow is a POP then we should remove the pop. The code + // above which generated the store instruction has already cleared the stack. + // We also don't generate any code for the arguments in this case as it would be + // an incorrect aload. + if (getKind() == ExceptionHandler && range.getEnd().getNext().getInstruction().equals(InstructionConstants.POP)) { + // easier than deleting it ... + range.getEnd().getNext().setInstruction(InstructionConstants.NOP); + } else { + range.insert(BcelRenderer.renderExprs(fact, world, argVars), Range.InsideBefore); + if (targetVar != null) { + range.insert(BcelRenderer.renderExpr(fact, world, targetVar), Range.InsideBefore); + } + if (getKind() == ConstructorCall) { + if (!world.isJoinpointArrayConstructionEnabled() || !this.getSignature().getDeclaringType().isArray()) { + if (deletedNewAndDup) { // if didnt delete them, dont insert any! + range.insert(InstructionFactory.createDup(1), Range.InsideBefore); + range.insert(fact.createNew((ObjectType) BcelWorld.makeBcelType(getSignature().getDeclaringType())), + Range.InsideBefore); + } + } + } + } + } + } + + // ---- getters + + public ShadowRange getRange() { + return range; + } + + public void setRange(ShadowRange range) { + this.range = range; + } + + private int sourceline = -1; + + public int getSourceLine() { + // if the kind of join point for which we are a shadow represents + // a method or constructor execution, then the best source line is + // the one from the enclosingMethod declarationLineNumber if available. + if (sourceline != -1) { + return sourceline; + } + Kind kind = getKind(); + if ((kind == MethodExecution) || (kind == ConstructorExecution) || (kind == AdviceExecution) + || (kind == StaticInitialization) || (kind == PreInitialization) || (kind == Initialization)) { + if (getEnclosingMethod().hasDeclaredLineNumberInfo()) { + sourceline = getEnclosingMethod().getDeclarationLineNumber(); + return sourceline; + } + } + + if (range == null) { + if (getEnclosingMethod().hasBody()) { + sourceline = Utility.getSourceLine(getEnclosingMethod().getBody().getStart()); + return sourceline; + } else { + sourceline = 0; + return sourceline; + } + } + sourceline = Utility.getSourceLine(range.getStart()); + if (sourceline < 0) { + sourceline = 0; + } + return sourceline; + } + + @Override + public ResolvedType getEnclosingType() { + return getEnclosingClass().getType(); + } + + public LazyClassGen getEnclosingClass() { + return enclosingMethod.getEnclosingClass(); + } + + public BcelWorld getWorld() { + return world; + } + + // ---- factory methods + + public static BcelShadow makeConstructorExecution(BcelWorld world, LazyMethodGen enclosingMethod, + InstructionHandle justBeforeStart) { + final InstructionList body = enclosingMethod.getBody(); + BcelShadow s = new BcelShadow(world, ConstructorExecution, world.makeJoinPointSignatureFromMethod(enclosingMethod, + Member.CONSTRUCTOR), enclosingMethod, null); + ShadowRange r = new ShadowRange(body); + r.associateWithShadow(s); + r.associateWithTargets(Range.genStart(body, justBeforeStart.getNext()), Range.genEnd(body)); + return s; + } + + public static BcelShadow makeStaticInitialization(BcelWorld world, LazyMethodGen enclosingMethod) { + InstructionList body = enclosingMethod.getBody(); + // move the start past ajc$preClinit + InstructionHandle clinitStart = body.getStart(); + if (clinitStart.getInstruction() instanceof InvokeInstruction) { + InvokeInstruction ii = (InvokeInstruction) clinitStart.getInstruction(); + if (ii.getName(enclosingMethod.getEnclosingClass().getConstantPool()).equals(NameMangler.AJC_PRE_CLINIT_NAME)) { + clinitStart = clinitStart.getNext(); + } + } + + InstructionHandle clinitEnd = body.getEnd(); + + // XXX should move the end before the postClinit, but the return is then tricky... + // if (clinitEnd.getInstruction() instanceof InvokeInstruction) { + // InvokeInstruction ii = (InvokeInstruction)clinitEnd.getInstruction(); + // if (ii.getName(enclosingMethod.getEnclosingClass().getConstantPool()).equals(NameMangler.AJC_POST_CLINIT_NAME)) { + // clinitEnd = clinitEnd.getPrev(); + // } + // } + + BcelShadow s = new BcelShadow(world, StaticInitialization, world.makeJoinPointSignatureFromMethod(enclosingMethod, + Member.STATIC_INITIALIZATION), enclosingMethod, null); + ShadowRange r = new ShadowRange(body); + r.associateWithShadow(s); + r.associateWithTargets(Range.genStart(body, clinitStart), Range.genEnd(body, clinitEnd)); + return s; + } + + /** + * Make the shadow for an exception handler. Currently makes an empty shadow that only allows before advice to be woven into it. + */ + + public static BcelShadow makeExceptionHandler(BcelWorld world, ExceptionRange exceptionRange, LazyMethodGen enclosingMethod, + InstructionHandle startOfHandler, BcelShadow enclosingShadow) { + InstructionList body = enclosingMethod.getBody(); + UnresolvedType catchType = exceptionRange.getCatchType(); + UnresolvedType inType = enclosingMethod.getEnclosingClass().getType(); + + ResolvedMemberImpl sig = MemberImpl.makeExceptionHandlerSignature(inType, catchType); + sig.setParameterNames(new String[] { findHandlerParamName(startOfHandler) }); + + BcelShadow s = new BcelShadow(world, ExceptionHandler, sig, enclosingMethod, enclosingShadow); + ShadowRange r = new ShadowRange(body); + r.associateWithShadow(s); + InstructionHandle start = Range.genStart(body, startOfHandler); + InstructionHandle end = Range.genEnd(body, start); + + r.associateWithTargets(start, end); + exceptionRange.updateTarget(startOfHandler, start, body); + return s; + } + + private static String findHandlerParamName(InstructionHandle startOfHandler) { + if (startOfHandler.getInstruction().isStoreInstruction() && startOfHandler.getNext() != null) { + int slot = startOfHandler.getInstruction().getIndex(); + // System.out.println("got store: " + startOfHandler.getInstruction() + ", " + index); + Iterator tIter = startOfHandler.getNext().getTargeters().iterator(); + while (tIter.hasNext()) { + InstructionTargeter targeter = tIter.next(); + if (targeter instanceof LocalVariableTag) { + LocalVariableTag t = (LocalVariableTag) targeter; + if (t.getSlot() == slot) { + return t.getName(); + } + } + } + } + + return ""; + } + + /** create an init join point associated w/ an interface in the body of a constructor */ + + public static BcelShadow makeIfaceInitialization(BcelWorld world, LazyMethodGen constructor, + Member interfaceConstructorSignature) { + // this call marks the instruction list as changed + constructor.getBody(); + // UnresolvedType inType = constructor.getEnclosingClass().getType(); + BcelShadow s = new BcelShadow(world, Initialization, interfaceConstructorSignature, constructor, null); + // s.fallsThrough = true; + // ShadowRange r = new ShadowRange(body); + // r.associateWithShadow(s); + // InstructionHandle start = Range.genStart(body, handle); + // InstructionHandle end = Range.genEnd(body, handle); + // + // r.associateWithTargets(start, end); + return s; + } + + public void initIfaceInitializer(InstructionHandle end) { + final InstructionList body = enclosingMethod.getBody(); + ShadowRange r = new ShadowRange(body); + r.associateWithShadow(this); + InstructionHandle nop = body.insert(end, InstructionConstants.NOP); + + r.associateWithTargets(Range.genStart(body, nop), Range.genEnd(body, nop)); + } + + // public static BcelShadow makeIfaceConstructorExecution( + // BcelWorld world, + // LazyMethodGen constructor, + // InstructionHandle next, + // Member interfaceConstructorSignature) + // { + // // final InstructionFactory fact = constructor.getEnclosingClass().getFactory(); + // InstructionList body = constructor.getBody(); + // // UnresolvedType inType = constructor.getEnclosingClass().getType(); + // BcelShadow s = + // new BcelShadow( + // world, + // ConstructorExecution, + // interfaceConstructorSignature, + // constructor, + // null); + // s.fallsThrough = true; + // ShadowRange r = new ShadowRange(body); + // r.associateWithShadow(s); + // // ??? this may or may not work + // InstructionHandle start = Range.genStart(body, next); + // //InstructionHandle end = Range.genEnd(body, body.append(start, fact.NOP)); + // InstructionHandle end = Range.genStart(body, next); + // //body.append(start, fact.NOP); + // + // r.associateWithTargets(start, end); + // return s; + // } + + /** + * Create an initialization join point associated with a constructor, but not with any body of code yet. If this is actually + * matched, it's range will be set when we inline self constructors. + * + * @param constructor The constructor starting this initialization. + */ + public static BcelShadow makeUnfinishedInitialization(BcelWorld world, LazyMethodGen constructor) { + BcelShadow ret = new BcelShadow(world, Initialization, world.makeJoinPointSignatureFromMethod(constructor, + Member.CONSTRUCTOR), constructor, null); + if (constructor.getEffectiveSignature() != null) { + ret.setMatchingSignature(constructor.getEffectiveSignature().getEffectiveSignature()); + } + return ret; + } + + public static BcelShadow makeUnfinishedPreinitialization(BcelWorld world, LazyMethodGen constructor) { + BcelShadow ret = new BcelShadow(world, PreInitialization, world.makeJoinPointSignatureFromMethod(constructor, + Member.CONSTRUCTOR), constructor, null); + if (constructor.getEffectiveSignature() != null) { + ret.setMatchingSignature(constructor.getEffectiveSignature().getEffectiveSignature()); + } + return ret; + } + + public static BcelShadow makeMethodExecution(BcelWorld world, LazyMethodGen enclosingMethod, boolean lazyInit) { + if (!lazyInit) { + return makeMethodExecution(world, enclosingMethod); + } + + BcelShadow s = new BcelShadow(world, MethodExecution, enclosingMethod.getMemberView(), enclosingMethod, null); + + return s; + } + + public void init() { + if (range != null) { + return; + } + + final InstructionList body = enclosingMethod.getBody(); + ShadowRange r = new ShadowRange(body); + r.associateWithShadow(this); + r.associateWithTargets(Range.genStart(body), Range.genEnd(body)); + } + + public static BcelShadow makeMethodExecution(BcelWorld world, LazyMethodGen enclosingMethod) { + return makeShadowForMethod(world, enclosingMethod, MethodExecution, enclosingMethod.getMemberView()); + } + + public static BcelShadow makeShadowForMethod(BcelWorld world, LazyMethodGen enclosingMethod, Shadow.Kind kind, Member sig) { + final InstructionList body = enclosingMethod.getBody(); + BcelShadow s = new BcelShadow(world, kind, sig, enclosingMethod, null); + ShadowRange r = new ShadowRange(body); + r.associateWithShadow(s); + r.associateWithTargets(// OPTIMIZE this occurs lots of times for all jp kinds... + Range.genStart(body), Range.genEnd(body)); + return s; + } + + public static BcelShadow makeAdviceExecution(BcelWorld world, LazyMethodGen enclosingMethod) { + final InstructionList body = enclosingMethod.getBody(); + BcelShadow s = new BcelShadow(world, AdviceExecution, + world.makeJoinPointSignatureFromMethod(enclosingMethod, Member.ADVICE), enclosingMethod, null); + ShadowRange r = new ShadowRange(body); + r.associateWithShadow(s); + r.associateWithTargets(Range.genStart(body), Range.genEnd(body)); + return s; + } + + // constructor call shadows are initially just around the + // call to the constructor. If ANY advice gets put on it, we move + // the NEW instruction inside the join point, which involves putting + // all the arguments in temps. + public static BcelShadow makeConstructorCall(BcelWorld world, LazyMethodGen enclosingMethod, InstructionHandle callHandle, + BcelShadow enclosingShadow) { + final InstructionList body = enclosingMethod.getBody(); + + Member sig = world.makeJoinPointSignatureForMethodInvocation(enclosingMethod.getEnclosingClass(), + (InvokeInstruction) callHandle.getInstruction()); + + BcelShadow s = new BcelShadow(world, ConstructorCall, sig, enclosingMethod, enclosingShadow); + ShadowRange r = new ShadowRange(body); + r.associateWithShadow(s); + r.associateWithTargets(Range.genStart(body, callHandle), Range.genEnd(body, callHandle)); + retargetAllBranches(callHandle, r.getStart()); + return s; + } + + public static BcelShadow makeArrayConstructorCall(BcelWorld world, LazyMethodGen enclosingMethod, + InstructionHandle arrayInstruction, BcelShadow enclosingShadow) { + final InstructionList body = enclosingMethod.getBody(); + Member sig = world.makeJoinPointSignatureForArrayConstruction(enclosingMethod.getEnclosingClass(), arrayInstruction); + BcelShadow s = new BcelShadow(world, ConstructorCall, sig, enclosingMethod, enclosingShadow); + ShadowRange r = new ShadowRange(body); + r.associateWithShadow(s); + r.associateWithTargets(Range.genStart(body, arrayInstruction), Range.genEnd(body, arrayInstruction)); + retargetAllBranches(arrayInstruction, r.getStart()); + return s; + } + + public static BcelShadow makeMonitorEnter(BcelWorld world, LazyMethodGen enclosingMethod, InstructionHandle monitorInstruction, + BcelShadow enclosingShadow) { + final InstructionList body = enclosingMethod.getBody(); + Member sig = world.makeJoinPointSignatureForMonitorEnter(enclosingMethod.getEnclosingClass(), monitorInstruction); + BcelShadow s = new BcelShadow(world, SynchronizationLock, sig, enclosingMethod, enclosingShadow); + ShadowRange r = new ShadowRange(body); + r.associateWithShadow(s); + r.associateWithTargets(Range.genStart(body, monitorInstruction), Range.genEnd(body, monitorInstruction)); + retargetAllBranches(monitorInstruction, r.getStart()); + return s; + } + + public static BcelShadow makeMonitorExit(BcelWorld world, LazyMethodGen enclosingMethod, InstructionHandle monitorInstruction, + BcelShadow enclosingShadow) { + final InstructionList body = enclosingMethod.getBody(); + Member sig = world.makeJoinPointSignatureForMonitorExit(enclosingMethod.getEnclosingClass(), monitorInstruction); + BcelShadow s = new BcelShadow(world, SynchronizationUnlock, sig, enclosingMethod, enclosingShadow); + ShadowRange r = new ShadowRange(body); + r.associateWithShadow(s); + r.associateWithTargets(Range.genStart(body, monitorInstruction), Range.genEnd(body, monitorInstruction)); + retargetAllBranches(monitorInstruction, r.getStart()); + return s; + } + + // see pr77166 + // public static BcelShadow makeArrayLoadCall( + // BcelWorld world, + // LazyMethodGen enclosingMethod, + // InstructionHandle arrayInstruction, + // BcelShadow enclosingShadow) + // { + // final InstructionList body = enclosingMethod.getBody(); + // Member sig = world.makeJoinPointSignatureForArrayLoad(enclosingMethod.getEnclosingClass(),arrayInstruction); + // BcelShadow s = + // new BcelShadow( + // world, + // MethodCall, + // sig, + // enclosingMethod, + // enclosingShadow); + // ShadowRange r = new ShadowRange(body); + // r.associateWithShadow(s); + // r.associateWithTargets( + // Range.genStart(body, arrayInstruction), + // Range.genEnd(body, arrayInstruction)); + // retargetAllBranches(arrayInstruction, r.getStart()); + // return s; + // } + + public static BcelShadow makeMethodCall(BcelWorld world, LazyMethodGen enclosingMethod, InstructionHandle callHandle, + BcelShadow enclosingShadow) { + final InstructionList body = enclosingMethod.getBody(); + BcelShadow s = new BcelShadow(world, MethodCall, world.makeJoinPointSignatureForMethodInvocation( + enclosingMethod.getEnclosingClass(), (InvokeInstruction) callHandle.getInstruction()), enclosingMethod, + enclosingShadow); + ShadowRange r = new ShadowRange(body); + r.associateWithShadow(s); + r.associateWithTargets(Range.genStart(body, callHandle), Range.genEnd(body, callHandle)); + retargetAllBranches(callHandle, r.getStart()); + return s; + } + + public static BcelShadow makeShadowForMethodCall(BcelWorld world, LazyMethodGen enclosingMethod, InstructionHandle callHandle, + BcelShadow enclosingShadow, Kind kind, ResolvedMember sig) { + final InstructionList body = enclosingMethod.getBody(); + BcelShadow s = new BcelShadow(world, kind, sig, enclosingMethod, enclosingShadow); + ShadowRange r = new ShadowRange(body); + r.associateWithShadow(s); + r.associateWithTargets(Range.genStart(body, callHandle), Range.genEnd(body, callHandle)); + retargetAllBranches(callHandle, r.getStart()); + return s; + } + + public static BcelShadow makeFieldGet(BcelWorld world, ResolvedMember field, LazyMethodGen enclosingMethod, + InstructionHandle getHandle, BcelShadow enclosingShadow) { + final InstructionList body = enclosingMethod.getBody(); + BcelShadow s = new BcelShadow(world, FieldGet, field, + // BcelWorld.makeFieldSignature( + // enclosingMethod.getEnclosingClass(), + // (FieldInstruction) getHandle.getInstruction()), + enclosingMethod, enclosingShadow); + ShadowRange r = new ShadowRange(body); + r.associateWithShadow(s); + r.associateWithTargets(Range.genStart(body, getHandle), Range.genEnd(body, getHandle)); + retargetAllBranches(getHandle, r.getStart()); + return s; + } + + public static BcelShadow makeFieldSet(BcelWorld world, ResolvedMember field, LazyMethodGen enclosingMethod, + InstructionHandle setHandle, BcelShadow enclosingShadow) { + final InstructionList body = enclosingMethod.getBody(); + BcelShadow s = new BcelShadow(world, FieldSet, field, + // BcelWorld.makeFieldJoinPointSignature( + // enclosingMethod.getEnclosingClass(), + // (FieldInstruction) setHandle.getInstruction()), + enclosingMethod, enclosingShadow); + ShadowRange r = new ShadowRange(body); + r.associateWithShadow(s); + r.associateWithTargets(Range.genStart(body, setHandle), Range.genEnd(body, setHandle)); + retargetAllBranches(setHandle, r.getStart()); + return s; + } + + public static void retargetAllBranches(InstructionHandle from, InstructionHandle to) { + for (InstructionTargeter source : from.getTargetersCopy()) { + if (source instanceof InstructionBranch) { + source.updateTarget(from, to); + } + } + } + + // // ---- type access methods + // private ObjectType getTargetBcelType() { + // return (ObjectType) BcelWorld.makeBcelType(getTargetType()); + // } + // private Type getArgBcelType(int arg) { + // return BcelWorld.makeBcelType(getArgType(arg)); + // } + + // ---- kinding + + /** + * If the end of my range has no real instructions following then my context needs a return at the end. + */ + public boolean terminatesWithReturn() { + return getRange().getRealNext() == null; + } + + /** + * Is arg0 occupied with the value of this + */ + public boolean arg0HoldsThis() { + if (getKind().isEnclosingKind()) { + return !Modifier.isStatic(getSignature().getModifiers()); + } else if (enclosingShadow == null) { + // XXX this is mostly right + // this doesn't do the right thing for calls in the pre part of introduced constructors. + return !enclosingMethod.isStatic(); + } else { + return ((BcelShadow) enclosingShadow).arg0HoldsThis(); + } + } + + // ---- argument getting methods + + private BcelVar thisVar = null; + private BcelVar targetVar = null; + private BcelVar[] argVars = null; + private Map kindedAnnotationVars = null; + private Map thisAnnotationVars = null; + private Map targetAnnotationVars = null; + // private Map/* */[] argAnnotationVars = null; + private Map withinAnnotationVars = null; + private Map withincodeAnnotationVars = null; + private boolean allArgVarsInitialized = false; + + @Override + public Var getThisVar() { + if (!hasThis()) { + throw new IllegalStateException("no this"); + } + initializeThisVar(); + return thisVar; + } + + @Override + public Var getThisAnnotationVar(UnresolvedType forAnnotationType) { + if (!hasThis()) { + throw new IllegalStateException("no this"); + } + initializeThisAnnotationVars(); // FIXME asc Why bother with this if we always return one? + // Even if we can't find one, we have to return one as we might have this annotation at runtime + Var v = thisAnnotationVars.get(forAnnotationType); + if (v == null) { + v = new TypeAnnotationAccessVar(forAnnotationType.resolve(world), (BcelVar) getThisVar()); + } + return v; + } + + @Override + public Var getTargetVar() { + if (!hasTarget()) { + throw new IllegalStateException("no target"); + } + initializeTargetVar(); + return targetVar; + } + + @Override + public Var getTargetAnnotationVar(UnresolvedType forAnnotationType) { + if (!hasTarget()) { + throw new IllegalStateException("no target"); + } + initializeTargetAnnotationVars(); // FIXME asc why bother with this if we always return one? + Var v = targetAnnotationVars.get(forAnnotationType); + // Even if we can't find one, we have to return one as we might have this annotation at runtime + if (v == null) { + v = new TypeAnnotationAccessVar(forAnnotationType.resolve(world), (BcelVar) getTargetVar()); + } + return v; + } + + @Override + public Var getArgVar(int i) { + ensureInitializedArgVar(i); + return argVars[i]; + } + + @Override + public Var getArgAnnotationVar(int i, UnresolvedType forAnnotationType) { + return new TypeAnnotationAccessVar(forAnnotationType.resolve(world), (BcelVar) getArgVar(i)); + // initializeArgAnnotationVars(); + // + // Var v = (Var) argAnnotationVars[i].get(forAnnotationType); + // if (v == null) { + // v = new TypeAnnotationAccessVar(forAnnotationType.resolve(world), (BcelVar) getArgVar(i)); + // } + // return v; + } + + @Override + public Var getKindedAnnotationVar(UnresolvedType forAnnotationType) { + initializeKindedAnnotationVars(); + return kindedAnnotationVars.get(forAnnotationType); + } + + @Override + public Var getWithinAnnotationVar(UnresolvedType forAnnotationType) { + initializeWithinAnnotationVars(); + return withinAnnotationVars.get(forAnnotationType); + } + + @Override + public Var getWithinCodeAnnotationVar(UnresolvedType forAnnotationType) { + initializeWithinCodeAnnotationVars(); + return withincodeAnnotationVars.get(forAnnotationType); + } + + // reflective thisJoinPoint support + private BcelVar thisJoinPointVar = null; + private boolean isThisJoinPointLazy; + private int lazyTjpConsumers = 0; + private BcelVar thisJoinPointStaticPartVar = null; + + // private BcelVar thisEnclosingJoinPointStaticPartVar = null; + + @Override + public final Var getThisJoinPointStaticPartVar() { + return getThisJoinPointStaticPartBcelVar(); + } + + @Override + public final Var getThisEnclosingJoinPointStaticPartVar() { + return getThisEnclosingJoinPointStaticPartBcelVar(); + } + + public void requireThisJoinPoint(boolean hasGuardTest, boolean isAround) { + if (!isAround) { + if (!hasGuardTest) { + isThisJoinPointLazy = false; + } else { + lazyTjpConsumers++; + } + } + // if (!hasGuardTest) { + // isThisJoinPointLazy = false; + // } else { + // lazyTjpConsumers++; + // } + if (thisJoinPointVar == null) { + thisJoinPointVar = genTempVar(UnresolvedType.forName("org.aspectj.lang.JoinPoint")); + } + } + + @Override + public Var getThisJoinPointVar() { + requireThisJoinPoint(false, false); + return thisJoinPointVar; + } + + void initializeThisJoinPoint() { + if (thisJoinPointVar == null) { + return; + } + + if (isThisJoinPointLazy) { + isThisJoinPointLazy = checkLazyTjp(); + } + + if (isThisJoinPointLazy) { + appliedLazyTjpOptimization = true; + createThisJoinPoint(); // make sure any state needed is initialized, but throw the instructions out + + if (lazyTjpConsumers == 1) { + return; // special case only one lazyTjpUser + } + + InstructionFactory fact = getFactory(); + InstructionList il = new InstructionList(); + il.append(InstructionConstants.ACONST_NULL); + il.append(thisJoinPointVar.createStore(fact)); + range.insert(il, Range.OutsideBefore); + } else { + appliedLazyTjpOptimization = false; + InstructionFactory fact = getFactory(); + InstructionList il = createThisJoinPoint(); + il.append(thisJoinPointVar.createStore(fact)); + range.insert(il, Range.OutsideBefore); + } + } + + private boolean checkLazyTjp() { + // check for around advice + for (Iterator i = mungers.iterator(); i.hasNext();) { + ShadowMunger munger = i.next(); + if (munger instanceof Advice) { + if (((Advice) munger).getKind() == AdviceKind.Around) { + if (munger.getSourceLocation() != null) { // do we know enough to bother reporting? + if (world.getLint().canNotImplementLazyTjp.isEnabled()) { + world.getLint().canNotImplementLazyTjp.signal(new String[] { toString() }, getSourceLocation(), + new ISourceLocation[] { munger.getSourceLocation() }); + } + } + return false; + } + } + } + + return true; + } + + InstructionList loadThisJoinPoint() { + InstructionFactory fact = getFactory(); + InstructionList il = new InstructionList(); + + if (isThisJoinPointLazy) { + // If we're lazy, build the join point right here. + il.append(createThisJoinPoint()); + + // Does someone else need it? If so, store it for later retrieval + if (lazyTjpConsumers > 1) { + il.append(thisJoinPointVar.createStore(fact)); + + InstructionHandle end = il.append(thisJoinPointVar.createLoad(fact)); + + il.insert(InstructionFactory.createBranchInstruction(Constants.IFNONNULL, end)); + il.insert(thisJoinPointVar.createLoad(fact)); + } + } else { + // If not lazy, its already been built and stored, just retrieve it + thisJoinPointVar.appendLoad(il, fact); + } + + return il; + } + + InstructionList createThisJoinPoint() { + InstructionFactory fact = getFactory(); + InstructionList il = new InstructionList(); + + BcelVar staticPart = getThisJoinPointStaticPartBcelVar(); + staticPart.appendLoad(il, fact); + if (hasThis()) { + ((BcelVar) getThisVar()).appendLoad(il, fact); + } else { + il.append(InstructionConstants.ACONST_NULL); + } + if (hasTarget()) { + ((BcelVar) getTargetVar()).appendLoad(il, fact); + } else { + il.append(InstructionConstants.ACONST_NULL); + } + + switch (getArgCount()) { + case 0: + il.append(fact.createInvoke("org.aspectj.runtime.reflect.Factory", "makeJP", LazyClassGen.tjpType, new Type[] { + LazyClassGen.staticTjpType, Type.OBJECT, Type.OBJECT }, Constants.INVOKESTATIC)); + break; + case 1: + ((BcelVar) getArgVar(0)).appendLoadAndConvert(il, fact, world.getCoreType(ResolvedType.OBJECT)); + il.append(fact.createInvoke("org.aspectj.runtime.reflect.Factory", "makeJP", LazyClassGen.tjpType, new Type[] { + LazyClassGen.staticTjpType, Type.OBJECT, Type.OBJECT, Type.OBJECT }, Constants.INVOKESTATIC)); + break; + case 2: + ((BcelVar) getArgVar(0)).appendLoadAndConvert(il, fact, world.getCoreType(ResolvedType.OBJECT)); + ((BcelVar) getArgVar(1)).appendLoadAndConvert(il, fact, world.getCoreType(ResolvedType.OBJECT)); + il.append(fact.createInvoke("org.aspectj.runtime.reflect.Factory", "makeJP", LazyClassGen.tjpType, new Type[] { + LazyClassGen.staticTjpType, Type.OBJECT, Type.OBJECT, Type.OBJECT, Type.OBJECT }, Constants.INVOKESTATIC)); + break; + default: + il.append(makeArgsObjectArray()); + il.append(fact.createInvoke("org.aspectj.runtime.reflect.Factory", "makeJP", LazyClassGen.tjpType, new Type[] { + LazyClassGen.staticTjpType, Type.OBJECT, Type.OBJECT, new ArrayType(Type.OBJECT, 1) }, Constants.INVOKESTATIC)); + break; + } + + return il; + } + + public BcelVar getThisJoinPointStaticPartBcelVar() { + return getThisJoinPointStaticPartBcelVar(false); + } + + @Override + public BcelVar getThisAspectInstanceVar(ResolvedType aspectType) { + return new AspectInstanceVar(aspectType); + } + + /** + * Get the Var for the xxxxJpStaticPart, xxx = this or enclosing + * + * @param isEnclosingJp true to have the enclosingJpStaticPart + * @return + */ + public BcelVar getThisJoinPointStaticPartBcelVar(final boolean isEnclosingJp) { + if (thisJoinPointStaticPartVar == null) { + Field field = getEnclosingClass().getTjpField(this, isEnclosingJp); + ResolvedType sjpType = null; + if (world.isTargettingAspectJRuntime12()) { // TAG:SUPPORTING12: We didn't have different jpsp types in 1.2 + sjpType = world.getCoreType(UnresolvedType.JOINPOINT_STATICPART); + } else { + sjpType = isEnclosingJp ? world.getCoreType(UnresolvedType.JOINPOINT_ENCLOSINGSTATICPART) : world + .getCoreType(UnresolvedType.JOINPOINT_STATICPART); + } + thisJoinPointStaticPartVar = new BcelFieldRef(sjpType, getEnclosingClass().getClassName(), field.getName()); + // getEnclosingClass().warnOnAddedStaticInitializer(this,munger.getSourceLocation()); + } + return thisJoinPointStaticPartVar; + } + + /** + * Get the Var for the enclosingJpStaticPart + * + * @return + */ + public BcelVar getThisEnclosingJoinPointStaticPartBcelVar() { + if (enclosingShadow == null) { + // the enclosing of an execution is itself + return getThisJoinPointStaticPartBcelVar(true); + } else { + return ((BcelShadow) enclosingShadow).getThisJoinPointStaticPartBcelVar(true); + } + } + + // ??? need to better understand all the enclosing variants + @Override + public Member getEnclosingCodeSignature() { + if (getKind().isEnclosingKind()) { + return getSignature(); + } else if (getKind() == Shadow.PreInitialization) { + // PreInit doesn't enclose code but its signature + // is correctly the signature of the ctor. + return getSignature(); + } else if (enclosingShadow == null) { + return getEnclosingMethod().getMemberView(); + } else { + return enclosingShadow.getSignature(); + } + } + + public Member getRealEnclosingCodeSignature() { + return enclosingMethod.getMemberView(); + } + + // public Member getEnclosingCodeSignatureForModel() { + // if (getKind().isEnclosingKind()) { + // return getSignature(); + // } else if (getKind() == Shadow.PreInitialization) { + // // PreInit doesn't enclose code but its signature + // // is correctly the signature of the ctor. + // return getSignature(); + // } else if (enclosingShadow == null) { + // return getEnclosingMethod().getMemberView(); + // } else { + // if (enclosingShadow.getKind() == Shadow.MethodExecution && enclosingMethod.getEffectiveSignature() != null) { + // + // } else { + // return enclosingShadow.getSignature(); + // } + // } + // } + + private InstructionList makeArgsObjectArray() { + InstructionFactory fact = getFactory(); + BcelVar arrayVar = genTempVar(UnresolvedType.OBJECTARRAY); + final InstructionList il = new InstructionList(); + int alen = getArgCount(); + il.append(Utility.createConstant(fact, alen)); + il.append(fact.createNewArray(Type.OBJECT, (short) 1)); + arrayVar.appendStore(il, fact); + + int stateIndex = 0; + for (int i = 0, len = getArgCount(); i < len; i++) { + arrayVar.appendConvertableArrayStore(il, fact, stateIndex, (BcelVar) getArgVar(i)); + stateIndex++; + } + arrayVar.appendLoad(il, fact); + return il; + } + + // ---- initializing var tables + + /* + * initializing this is doesn't do anything, because this is protected from side-effects, so we don't need to copy its location + */ + + private void initializeThisVar() { + if (thisVar != null) { + return; + } + thisVar = new BcelVar(getThisType().resolve(world), 0); + thisVar.setPositionInAroundState(0); + } + + public void initializeTargetVar() { + InstructionFactory fact = getFactory(); + if (targetVar != null) { + return; + } + if (getKind().isTargetSameAsThis()) { + if (hasThis()) { + initializeThisVar(); + } + targetVar = thisVar; + } else { + initializeArgVars(); // gotta pop off the args before we find the target + UnresolvedType type = getTargetType(); + type = ensureTargetTypeIsCorrect(type); + targetVar = genTempVar(type, "ajc$target"); + range.insert(targetVar.createStore(fact), Range.OutsideBefore); + targetVar.setPositionInAroundState(hasThis() ? 1 : 0); + } + } + + /* + * PR 72528 This method double checks the target type under certain conditions. The Java 1.4 compilers seem to take calls to + * clone methods on array types and create bytecode that looks like clone is being called on Object. If we advise a clone call + * with around advice we extract the call into a helper method which we can then refer to. Because the type in the bytecode for + * the call to clone is Object we create a helper method with an Object parameter - this is not correct as we have lost the fact + * that the actual type is an array type. If we don't do the check below we will create code that fails java verification. This + * method checks for the peculiar set of conditions and if they are true, it has a sneak peek at the code before the call to see + * what is on the stack. + */ + public UnresolvedType ensureTargetTypeIsCorrect(UnresolvedType tx) { + + Member msig = getSignature(); + if (msig.getArity() == 0 && getKind() == MethodCall && msig.getName().charAt(0) == 'c' && tx.equals(ResolvedType.OBJECT) + && msig.getReturnType().equals(ResolvedType.OBJECT) && msig.getName().equals("clone")) { + + // Lets go back through the code from the start of the shadow + InstructionHandle searchPtr = range.getStart().getPrev(); + while (Range.isRangeHandle(searchPtr) || searchPtr.getInstruction().isStoreInstruction()) { // ignore this instruction - + // it doesnt give us the + // info we want + searchPtr = searchPtr.getPrev(); + } + + // A load instruction may tell us the real type of what the clone() call is on + if (searchPtr.getInstruction().isLoadInstruction()) { + LocalVariableTag lvt = LazyMethodGen.getLocalVariableTag(searchPtr, searchPtr.getInstruction().getIndex()); + if (lvt != null) { + return UnresolvedType.forSignature(lvt.getType()); + } + } + // A field access instruction may tell us the real type of what the clone() call is on + if (searchPtr.getInstruction() instanceof FieldInstruction) { + FieldInstruction si = (FieldInstruction) searchPtr.getInstruction(); + Type t = si.getFieldType(getEnclosingClass().getConstantPool()); + return BcelWorld.fromBcel(t); + } + // A new array instruction obviously tells us it is an array type ! + if (searchPtr.getInstruction().opcode == Constants.ANEWARRAY) { + // ANEWARRAY ana = (ANEWARRAY)searchPoint.getInstruction(); + // Type t = ana.getType(getEnclosingClass().getConstantPool()); + // Just use a standard java.lang.object array - that will work fine + return BcelWorld.fromBcel(new ArrayType(Type.OBJECT, 1)); + } + // A multi new array instruction obviously tells us it is an array type ! + if (searchPtr.getInstruction() instanceof MULTIANEWARRAY) { + MULTIANEWARRAY ana = (MULTIANEWARRAY) searchPtr.getInstruction(); + // Type t = ana.getType(getEnclosingClass().getConstantPool()); + // t = new ArrayType(t,ana.getDimensions()); + // Just use a standard java.lang.object array - that will work fine + return BcelWorld.fromBcel(new ArrayType(Type.OBJECT, ana.getDimensions())); + } + throw new BCException("Can't determine real target of clone() when processing instruction " + + searchPtr.getInstruction() + ". Perhaps avoid selecting clone with your pointcut?"); + } + return tx; + } + + public void ensureInitializedArgVar(int argNumber) { + if (allArgVarsInitialized || (argVars != null && argVars[argNumber] != null)) { + return; + } + InstructionFactory fact = getFactory(); + int len = getArgCount(); + if (argVars == null) { + argVars = new BcelVar[len]; + } + + // Need to initialize argument i + int positionOffset = (hasTarget() ? 1 : 0) + ((hasThis() && !getKind().isTargetSameAsThis()) ? 1 : 0); + + if (getKind().argsOnStack()) { + // Let's just do them all now since they are on the stack + // we move backwards because we're popping off the stack + for (int i = len - 1; i >= 0; i--) { + UnresolvedType type = getArgType(i); + BcelVar tmp = genTempVar(type, "ajc$arg" + i); + range.insert(tmp.createStore(getFactory()), Range.OutsideBefore); + int position = i; + position += positionOffset; + tmp.setPositionInAroundState(position); + argVars[i] = tmp; + } + allArgVarsInitialized = true; + } else { + int index = 0; + if (arg0HoldsThis()) { + index++; + } + boolean allInited = true; + for (int i = 0; i < len; i++) { + UnresolvedType type = getArgType(i); + if (i == argNumber) { + argVars[argNumber] = genTempVar(type, "ajc$arg" + argNumber); + range.insert(argVars[argNumber].createCopyFrom(fact, index), Range.OutsideBefore); + argVars[argNumber].setPositionInAroundState(argNumber + positionOffset); + } + allInited = allInited && argVars[i] != null; + index += type.getSize(); + } + if (allInited && (argNumber + 1) == len) { + allArgVarsInitialized = true; + } + } + } + + /** + * Initialize all the available arguments at the shadow. This means creating a copy of them that we can then use for advice + * calls (the copy ensures we are not affected by other advice changing the values). This method initializes all arguments + * whereas the method ensureInitializedArgVar will only ensure a single argument is setup. + */ + public void initializeArgVars() { + if (allArgVarsInitialized) { + return; + } + InstructionFactory fact = getFactory(); + int len = getArgCount(); + if (argVars == null) { + argVars = new BcelVar[len]; + } + int positionOffset = (hasTarget() ? 1 : 0) + ((hasThis() && !getKind().isTargetSameAsThis()) ? 1 : 0); + + if (getKind().argsOnStack()) { + // we move backwards because we're popping off the stack + for (int i = len - 1; i >= 0; i--) { + UnresolvedType type = getArgType(i); + BcelVar tmp = genTempVar(type, "ajc$arg" + i); + range.insert(tmp.createStore(getFactory()), Range.OutsideBefore); + int position = i; + position += positionOffset; + tmp.setPositionInAroundState(position); + argVars[i] = tmp; + } + } else { + int index = 0; + if (arg0HoldsThis()) { + index++; + } + + for (int i = 0; i < len; i++) { + UnresolvedType type = getArgType(i); + if (argVars[i] == null) { + BcelVar tmp = genTempVar(type, "ajc$arg" + i); + range.insert(tmp.createCopyFrom(fact, index), Range.OutsideBefore); + argVars[i] = tmp; + tmp.setPositionInAroundState(i + positionOffset); + } + index += type.resolve(world).getSize(); + } + } + allArgVarsInitialized = true; + + } + + public void initializeForAroundClosure() { + initializeArgVars(); + if (hasTarget()) { + initializeTargetVar(); + } + if (hasThis()) { + initializeThisVar(); + // System.out.println("initialized: " + this + " thisVar = " + thisVar); + } + } + + public void initializeThisAnnotationVars() { + if (thisAnnotationVars != null) { + return; + } + thisAnnotationVars = new HashMap(); + // populate.. + } + + public void initializeTargetAnnotationVars() { + if (targetAnnotationVars != null) { + return; + } + if (getKind().isTargetSameAsThis()) { + if (hasThis()) { + initializeThisAnnotationVars(); + } + targetAnnotationVars = thisAnnotationVars; + } else { + targetAnnotationVars = new HashMap(); + ResolvedType[] rtx = this.getTargetType().resolve(world).getAnnotationTypes(); // what about annotations we havent + // gotten yet but we will get in + // subclasses? + for (int i = 0; i < rtx.length; i++) { + ResolvedType typeX = rtx[i]; + targetAnnotationVars.put(typeX, new TypeAnnotationAccessVar(typeX, (BcelVar) getTargetVar())); + } + // populate. + } + } + + // public void initializeArgAnnotationVars() { + // if (argAnnotationVars != null) { + // return; + // } + // int numArgs = getArgCount(); + // argAnnotationVars = new Map[numArgs]; + // for (int i = 0; i < argAnnotationVars.length; i++) { + // argAnnotationVars[i] = new HashMap(); + // // FIXME asc just delete this logic - we always build the Var on demand, as we don't know at weave time + // // what the full set of annotations could be (due to static/dynamic type differences...) + // } + // } + + protected ResolvedMember getRelevantMember(ResolvedMember foundMember, Member relevantMember, ResolvedType relevantType) { + if (foundMember != null) { + return foundMember; + } + + foundMember = getSignature().resolve(world); + if (foundMember == null && relevantMember != null) { + foundMember = relevantType.lookupMemberWithSupersAndITDs(relevantMember); + } + + // check the ITD'd dooberries + List mungers = relevantType.resolve(world).getInterTypeMungers(); + for (ConcreteTypeMunger typeMunger : mungers) { + if (typeMunger.getMunger() instanceof NewMethodTypeMunger || typeMunger.getMunger() instanceof NewConstructorTypeMunger) { + ResolvedMember fakerm = typeMunger.getSignature(); + if (fakerm.getName().equals(getSignature().getName()) + && fakerm.getParameterSignature().equals(getSignature().getParameterSignature())) { + if (foundMember.getKind() == ResolvedMember.CONSTRUCTOR) { + foundMember = AjcMemberMaker.interConstructor(relevantType, foundMember, typeMunger.getAspectType()); + } else { + foundMember = AjcMemberMaker.interMethod(foundMember, typeMunger.getAspectType(), false); + // ResolvedMember o = AjcMemberMaker.interMethodBody(fakerm, typeMunger.getAspectType()); + // // Object os = o.getAnnotations(); + // ResolvedMember foundMember2 = findMethod(typeMunger.getAspectType(), o); + // Object os2 = foundMember2.getAnnotations(); + // int stop = 1; + // foundMember = foundMember2; + // foundMember = AjcMemberMaker.interMethod(foundMember, typeMunger.getAspectType()); + } + // in the above.. what about if it's on an Interface? Can that happen? + // then the last arg of the above should be true + return foundMember; + } + } + } + return foundMember; + } + + protected ResolvedType[] getAnnotations(ResolvedMember foundMember, Member relevantMember, ResolvedType relevantType) { + if (foundMember == null) { + // check the ITD'd dooberries + List mungers = relevantType.resolve(world).getInterTypeMungers(); + for (Iterator iter = mungers.iterator(); iter.hasNext();) { + Object munger = iter.next(); + ConcreteTypeMunger typeMunger = (ConcreteTypeMunger) munger; + if (typeMunger.getMunger() instanceof NewMethodTypeMunger + || typeMunger.getMunger() instanceof NewConstructorTypeMunger) { + ResolvedMember fakerm = typeMunger.getSignature(); + // if (fakerm.hasAnnotations()) + + ResolvedMember ajcMethod = (getSignature().getKind() == ResolvedMember.CONSTRUCTOR ? AjcMemberMaker + .postIntroducedConstructor(typeMunger.getAspectType(), fakerm.getDeclaringType(), + fakerm.getParameterTypes()) : AjcMemberMaker.interMethodDispatcher(fakerm, + typeMunger.getAspectType())); + // AjcMemberMaker.interMethodBody(fakerm,typeMunger.getAspectType())); + ResolvedMember rmm = findMethod(typeMunger.getAspectType(), ajcMethod); + if (fakerm.getName().equals(getSignature().getName()) + && fakerm.getParameterSignature().equals(getSignature().getParameterSignature())) { + relevantType = typeMunger.getAspectType(); + foundMember = rmm; + return foundMember.getAnnotationTypes(); + } + } + } + // didn't find in ITDs, look in supers + foundMember = relevantType.lookupMemberWithSupersAndITDs(relevantMember); + if (foundMember == null) { + throw new IllegalStateException("Couldn't find member " + relevantMember + " for type " + relevantType); + } + } + return foundMember.getAnnotationTypes(); + } + + /** + * By determining what "kind" of shadow we are, we can find out the annotations on the appropriate element (method, field, + * constructor, type). Then create one BcelVar entry in the map for each annotation, keyed by annotation type. + */ + public void initializeKindedAnnotationVars() { + if (kindedAnnotationVars != null) { + return; + } + kindedAnnotationVars = new HashMap(); + + ResolvedType[] annotations = null; + Member shadowSignature = getSignature(); + Member annotationHolder = getSignature(); + ResolvedType relevantType = shadowSignature.getDeclaringType().resolve(world); + + if (relevantType.isRawType() || relevantType.isParameterizedType()) { + relevantType = relevantType.getGenericType(); + } + + // Determine the annotations that are of interest + if (getKind() == Shadow.StaticInitialization) { + annotations = relevantType.resolve(world).getAnnotationTypes(); + } else if (getKind() == Shadow.MethodCall || getKind() == Shadow.ConstructorCall) { + ResolvedMember foundMember = findMethod2(relevantType.resolve(world).getDeclaredMethods(), getSignature()); + annotations = getAnnotations(foundMember, shadowSignature, relevantType); + annotationHolder = getRelevantMember(foundMember, shadowSignature, relevantType); + relevantType = annotationHolder.getDeclaringType().resolve(world); + } else if (getKind() == Shadow.FieldSet || getKind() == Shadow.FieldGet) { + annotationHolder = findField(relevantType.getDeclaredFields(), getSignature()); + + if (annotationHolder == null) { + // check the ITD'd dooberries + List mungers = relevantType.resolve(world).getInterTypeMungers(); + for (ConcreteTypeMunger typeMunger : mungers) { + if (typeMunger.getMunger() instanceof NewFieldTypeMunger) { + ResolvedMember fakerm = typeMunger.getSignature(); + // if (fakerm.hasAnnotations()) + ResolvedMember ajcMethod = AjcMemberMaker.interFieldInitializer(fakerm, typeMunger.getAspectType()); + ResolvedMember rmm = findMethod(typeMunger.getAspectType(), ajcMethod); + if (fakerm.equals(getSignature())) { + relevantType = typeMunger.getAspectType(); + annotationHolder = rmm; + } + } + } + } + annotations = ((ResolvedMember) annotationHolder).getAnnotationTypes(); + + } else if (getKind() == Shadow.MethodExecution || getKind() == Shadow.ConstructorExecution + || getKind() == Shadow.AdviceExecution) { + + ResolvedMember foundMember = findMethod2(relevantType.getDeclaredMethods(), getSignature()); + annotations = getAnnotations(foundMember, shadowSignature, relevantType); + annotationHolder = getRelevantMember(foundMember, annotationHolder, relevantType); + UnresolvedType ut = annotationHolder.getDeclaringType(); + relevantType = ut.resolve(world); + + } else if (getKind() == Shadow.ExceptionHandler) { + relevantType = getSignature().getParameterTypes()[0].resolve(world); + annotations = relevantType.getAnnotationTypes(); + + } else if (getKind() == Shadow.PreInitialization || getKind() == Shadow.Initialization) { + ResolvedMember found = findMethod2(relevantType.getDeclaredMethods(), getSignature()); + annotations = found.getAnnotationTypes(); + } + + if (annotations == null) { + // We can't have recognized the shadow - should blow up now to be on the safe side + throw new BCException("Could not discover annotations for shadow: " + getKind()); + } + + for (ResolvedType annotationType : annotations) { + AnnotationAccessVar accessVar = new AnnotationAccessVar(this, getKind(), annotationType.resolve(world), relevantType, + annotationHolder, false); + kindedAnnotationVars.put(annotationType, accessVar); + } + } + + private ResolvedMember findMethod2(ResolvedMember members[], Member sig) { + String signatureName = sig.getName(); + String parameterSignature = sig.getParameterSignature(); + for (ResolvedMember member : members) { + if (member.getName().equals(signatureName) && member.getParameterSignature().equals(parameterSignature)) { + return member; + } + } + return null; + } + + private ResolvedMember findMethod(ResolvedType aspectType, ResolvedMember ajcMethod) { + ResolvedMember decMethods[] = aspectType.getDeclaredMethods(); + for (int i = 0; i < decMethods.length; i++) { + ResolvedMember member = decMethods[i]; + if (member.equals(ajcMethod)) { + return member; + } + } + return null; + } + + private ResolvedMember findField(ResolvedMember[] members, Member lookingFor) { + for (int i = 0; i < members.length; i++) { + ResolvedMember member = members[i]; + if (member.getName().equals(getSignature().getName()) && member.getType().equals(getSignature().getType())) { + return member; + } + } + return null; + } + + public void initializeWithinAnnotationVars() { + if (withinAnnotationVars != null) { + return; + } + withinAnnotationVars = new HashMap(); + + ResolvedType[] annotations = getEnclosingType().resolve(world).getAnnotationTypes(); + for (int i = 0; i < annotations.length; i++) { + ResolvedType ann = annotations[i]; + Kind k = Shadow.StaticInitialization; + withinAnnotationVars.put(ann, new AnnotationAccessVar(this, k, ann, getEnclosingType(), null, true)); + } + } + + public void initializeWithinCodeAnnotationVars() { + if (withincodeAnnotationVars != null) { + return; + } + withincodeAnnotationVars = new HashMap(); + + // For some shadow we are interested in annotations on the method containing that shadow. + ResolvedType[] annotations = getEnclosingMethod().getMemberView().getAnnotationTypes(); + for (int i = 0; i < annotations.length; i++) { + ResolvedType ann = annotations[i]; + Kind k = (getEnclosingMethod().getMemberView().getKind() == Member.CONSTRUCTOR ? Shadow.ConstructorExecution + : Shadow.MethodExecution); + withincodeAnnotationVars.put(ann, new AnnotationAccessVar(this, k, ann, getEnclosingType(), + getEnclosingCodeSignature(), true)); + } + } + + // ---- weave methods + + void weaveBefore(BcelAdvice munger) { + range.insert(munger.getAdviceInstructions(this, null, range.getRealStart()), Range.InsideBefore); + } + + public void weaveAfter(BcelAdvice munger) { + weaveAfterThrowing(munger, UnresolvedType.THROWABLE); + weaveAfterReturning(munger); + } + + /** + * The basic strategy here is to add a set of instructions at the end of the shadow range that dispatch the advice, and then + * return whatever the shadow was going to return anyway. + * + * To achieve this, we note all the return statements in the advice, and replace them with code that: 1) stores the return value + * on top of the stack in a temp var 2) jumps to the start of our advice block 3) restores the return value at the end of the + * advice block before ultimately returning + * + * We also need to bind the return value into a returning parameter, if the advice specified one. + */ + public void weaveAfterReturning(BcelAdvice munger) { + List returns = findReturnInstructions(); + boolean hasReturnInstructions = !returns.isEmpty(); + + // list of instructions that handle the actual return from the join point + InstructionList retList = new InstructionList(); + + // variable that holds the return value + BcelVar returnValueVar = null; + + if (hasReturnInstructions) { + returnValueVar = generateReturnInstructions(returns, retList); + } else { + // we need at least one instruction, as the target for jumps + retList.append(InstructionConstants.NOP); + } + + // list of instructions for dispatching to the advice itself + InstructionList advice = getAfterReturningAdviceDispatchInstructions(munger, retList.getStart()); + + if (hasReturnInstructions) { + InstructionHandle gotoTarget = advice.getStart(); + for (Iterator i = returns.iterator(); i.hasNext();) { + InstructionHandle ih = i.next(); + retargetReturnInstruction(munger.hasExtraParameter(), returnValueVar, gotoTarget, ih); + } + } + + range.append(advice); + range.append(retList); + } + + /** + * @return a list of all the return instructions in the range of this shadow + */ + private List findReturnInstructions() { + List returns = new ArrayList(); + for (InstructionHandle ih = range.getStart(); ih != range.getEnd(); ih = ih.getNext()) { + if (ih.getInstruction().isReturnInstruction()) { + returns.add(ih); + } + } + return returns; + } + + /** + * Given a list containing all the return instruction handles for this shadow, finds the last return instruction and copies it, + * making this the ultimate return. If the shadow has a non-void return type, we also create a temporary variable to hold the + * return value, and load the value from this var before returning (see pr148007 for why we do this - it works around a JRockit + * bug, and is also closer to what javac generates) + * + * Sometimes the 'last return' isnt the right one - some rogue code can include the real return from the body of a subroutine + * that exists at the end of the method. In this case the last return is RETURN but that may not be correct for a method with a + * non-void return type... pr151673 + * + * @param returns list of all the return instructions in the shadow + * @param returnInstructions instruction list into which the return instructions should be generated + * @return the variable holding the return value, if needed + */ + private BcelVar generateReturnInstructions(List returns, InstructionList returnInstructions) { + BcelVar returnValueVar = null; + if (this.hasANonVoidReturnType()) { + // Find the last *correct* return - this is a method with a non-void return type + // so ignore RETURN + Instruction newReturnInstruction = null; + int i = returns.size() - 1; + while (newReturnInstruction == null && i >= 0) { + InstructionHandle ih = returns.get(i); + if (ih.getInstruction().opcode != Constants.RETURN) { + newReturnInstruction = Utility.copyInstruction(ih.getInstruction()); + } + i--; + } + returnValueVar = genTempVar(this.getReturnType()); + returnValueVar.appendLoad(returnInstructions, getFactory()); + returnInstructions.append(newReturnInstruction); + } else { + InstructionHandle lastReturnHandle = returns.get(returns.size() - 1); + Instruction newReturnInstruction = Utility.copyInstruction(lastReturnHandle.getInstruction()); + returnInstructions.append(newReturnInstruction); + } + return returnValueVar; + } + + /** + * @return true, iff this shadow returns a value + */ + private boolean hasANonVoidReturnType() { + return !this.getReturnType().equals(UnresolvedType.VOID); + } + + /** + * Get the list of instructions used to dispatch to the after advice + * + * @param munger + * @param firstInstructionInReturnSequence + * @return + */ + private InstructionList getAfterReturningAdviceDispatchInstructions(BcelAdvice munger, + InstructionHandle firstInstructionInReturnSequence) { + InstructionList advice = new InstructionList(); + + BcelVar tempVar = null; + if (munger.hasExtraParameter()) { + tempVar = insertAdviceInstructionsForBindingReturningParameter(advice); + } + advice.append(munger.getAdviceInstructions(this, tempVar, firstInstructionInReturnSequence)); + return advice; + } + + /** + * If the after() returning(Foo f) form is used, bind the return value to the parameter. If the shadow returns void, bind null. + * + * @param advice + * @return + */ + private BcelVar insertAdviceInstructionsForBindingReturningParameter(InstructionList advice) { + BcelVar tempVar; + UnresolvedType tempVarType = getReturnType(); + if (tempVarType.equals(UnresolvedType.VOID)) { + tempVar = genTempVar(UnresolvedType.OBJECT); + advice.append(InstructionConstants.ACONST_NULL); + tempVar.appendStore(advice, getFactory()); + } else { + tempVar = genTempVar(tempVarType); + advice.append(InstructionFactory.createDup(tempVarType.getSize())); + tempVar.appendStore(advice, getFactory()); + } + return tempVar; + } + + /** + * Helper method for weaveAfterReturning + * + * Each return instruction in the method body is retargeted by calling this method. The return instruction is replaced by up to + * three instructions: 1) if the shadow returns a value, and that value is bound to an after returning parameter, then we DUP + * the return value on the top of the stack 2) if the shadow returns a value, we store it in the returnValueVar (it will be + * retrieved from here when we ultimately return after the advice dispatch) 3) if the return was the last instruction, we add a + * NOP (it will fall through to the advice dispatch), otherwise we add a GOTO that branches to the supplied gotoTarget (start of + * the advice dispatch) + */ + private void retargetReturnInstruction(boolean hasReturningParameter, BcelVar returnValueVar, InstructionHandle gotoTarget, + InstructionHandle returnHandle) { + // pr148007, work around JRockit bug + // replace ret with store into returnValueVar, followed by goto if not + // at the end of the instruction list... + InstructionList newInstructions = new InstructionList(); + if (returnValueVar != null) { + if (hasReturningParameter) { + // we have to dup the return val before consuming it... + newInstructions.append(InstructionFactory.createDup(this.getReturnType().getSize())); + } + // store the return value into this var + returnValueVar.appendStore(newInstructions, getFactory()); + } + if (!isLastInstructionInRange(returnHandle, range)) { + newInstructions.append(InstructionFactory.createBranchInstruction(Constants.GOTO, gotoTarget)); + } + if (newInstructions.isEmpty()) { + newInstructions.append(InstructionConstants.NOP); + } + Utility.replaceInstruction(returnHandle, newInstructions, enclosingMethod); + } + + private boolean isLastInstructionInRange(InstructionHandle ih, ShadowRange aRange) { + return ih.getNext() == aRange.getEnd(); + } + + public void weaveAfterThrowing(BcelAdvice munger, UnresolvedType catchType) { + // a good optimization would be not to generate anything here + // if the shadow is GUARANTEED empty (i.e., there's NOTHING, not even + // a shadow, inside me). + if (getRange().getStart().getNext() == getRange().getEnd()) { + return; + } + InstructionFactory fact = getFactory(); + InstructionList handler = new InstructionList(); + BcelVar exceptionVar = genTempVar(catchType); + exceptionVar.appendStore(handler, fact); + + // pr62642 + // I will now jump through some firey BCEL hoops to generate a trivial bit of code: + // if (exc instanceof ExceptionInInitializerError) + // throw (ExceptionInInitializerError)exc; + if (this.getEnclosingMethod().getName().equals("")) { + ResolvedType eiieType = world.resolve("java.lang.ExceptionInInitializerError"); + ObjectType eiieBcelType = (ObjectType) BcelWorld.makeBcelType(eiieType); + InstructionList ih = new InstructionList(InstructionConstants.NOP); + handler.append(exceptionVar.createLoad(fact)); + handler.append(fact.createInstanceOf(eiieBcelType)); + InstructionBranch bi = InstructionFactory.createBranchInstruction(Constants.IFEQ, ih.getStart()); + handler.append(bi); + handler.append(exceptionVar.createLoad(fact)); + handler.append(fact.createCheckCast(eiieBcelType)); + handler.append(InstructionConstants.ATHROW); + handler.append(ih); + } + + InstructionList endHandler = new InstructionList(exceptionVar.createLoad(fact)); + handler.append(munger.getAdviceInstructions(this, exceptionVar, endHandler.getStart())); + handler.append(endHandler); + handler.append(InstructionConstants.ATHROW); + InstructionHandle handlerStart = handler.getStart(); + + if (isFallsThrough()) { + InstructionHandle jumpTarget = handler.append(InstructionConstants.NOP); + handler.insert(InstructionFactory.createBranchInstruction(Constants.GOTO, jumpTarget)); + } + InstructionHandle protectedEnd = handler.getStart(); + range.insert(handler, Range.InsideAfter); + + enclosingMethod.addExceptionHandler(range.getStart().getNext(), protectedEnd.getPrev(), handlerStart, + (ObjectType) BcelWorld.makeBcelType(catchType), // ???Type.THROWABLE, + // high priority if our args are on the stack + getKind().hasHighPriorityExceptions()); + } + + // ??? this shares a lot of code with the above weaveAfterThrowing + // ??? would be nice to abstract that to say things only once + public void weaveSoftener(BcelAdvice munger, UnresolvedType catchType) { + // a good optimization would be not to generate anything here + // if the shadow is GUARANTEED empty (i.e., there's NOTHING, not even + // a shadow, inside me). + if (getRange().getStart().getNext() == getRange().getEnd()) { + return; + } + + InstructionFactory fact = getFactory(); + InstructionList handler = new InstructionList(); + InstructionList rtExHandler = new InstructionList(); + BcelVar exceptionVar = genTempVar(catchType); + + handler.append(fact.createNew(NameMangler.SOFT_EXCEPTION_TYPE)); + handler.append(InstructionFactory.createDup(1)); + handler.append(exceptionVar.createLoad(fact)); + handler.append(fact.createInvoke(NameMangler.SOFT_EXCEPTION_TYPE, "", Type.VOID, new Type[] { Type.THROWABLE }, + Constants.INVOKESPECIAL)); // ??? special + handler.append(InstructionConstants.ATHROW); + + // ENH 42737 + exceptionVar.appendStore(rtExHandler, fact); + // aload_1 + rtExHandler.append(exceptionVar.createLoad(fact)); + // instanceof class java/lang/RuntimeException + rtExHandler.append(fact.createInstanceOf(new ObjectType("java.lang.RuntimeException"))); + // ifeq go to new SOFT_EXCEPTION_TYPE instruction + rtExHandler.append(InstructionFactory.createBranchInstruction(Constants.IFEQ, handler.getStart())); + // aload_1 + rtExHandler.append(exceptionVar.createLoad(fact)); + // athrow + rtExHandler.append(InstructionFactory.ATHROW); + + InstructionHandle handlerStart = rtExHandler.getStart(); + + if (isFallsThrough()) { + InstructionHandle jumpTarget = range.getEnd();// handler.append(fact.NOP); + rtExHandler.insert(InstructionFactory.createBranchInstruction(Constants.GOTO, jumpTarget)); + } + + rtExHandler.append(handler); + + InstructionHandle protectedEnd = rtExHandler.getStart(); + range.insert(rtExHandler, Range.InsideAfter); + + enclosingMethod.addExceptionHandler(range.getStart().getNext(), protectedEnd.getPrev(), handlerStart, + (ObjectType) BcelWorld.makeBcelType(catchType), + // high priority if our args are on the stack + getKind().hasHighPriorityExceptions()); + } + + public void weavePerObjectEntry(final BcelAdvice munger, final BcelVar onVar) { + final InstructionFactory fact = getFactory(); + + InstructionList entryInstructions = new InstructionList(); + InstructionList entrySuccessInstructions = new InstructionList(); + onVar.appendLoad(entrySuccessInstructions, fact); + + entrySuccessInstructions + .append(Utility.createInvoke(fact, world, AjcMemberMaker.perObjectBind(munger.getConcreteAspect()))); + + InstructionList testInstructions = munger.getTestInstructions(this, entrySuccessInstructions.getStart(), + range.getRealStart(), entrySuccessInstructions.getStart()); + + entryInstructions.append(testInstructions); + entryInstructions.append(entrySuccessInstructions); + + range.insert(entryInstructions, Range.InsideBefore); + } + + // PTWIMPL Create static initializer to call the aspect factory + /** + * Causes the aspect instance to be *set* for later retrievable through localAspectof()/aspectOf() + */ + public void weavePerTypeWithinAspectInitialization(final BcelAdvice munger, UnresolvedType t) { + ResolvedType tResolved = t.resolve(world); + if (tResolved.isInterface()) { + return; // Don't initialize statics in interfaces + } + ResolvedType aspectRT = munger.getConcreteAspect(); + BcelWorld.getBcelObjectType(aspectRT); + + // Although matched, if the visibility rules prevent the aspect from seeing this type, don't + // insert any code (easier to do it here than try to affect the matching logic, unfortunately) + if (!(tResolved.canBeSeenBy(aspectRT) || aspectRT.isPrivilegedAspect())) { + return; + } + + final InstructionFactory fact = getFactory(); + + InstructionList entryInstructions = new InstructionList(); + InstructionList entrySuccessInstructions = new InstructionList(); + + String aspectname = munger.getConcreteAspect().getName(); + + String ptwField = NameMangler.perTypeWithinFieldForTarget(munger.getConcreteAspect()); + entrySuccessInstructions.append(InstructionFactory.PUSH(fact.getConstantPool(), t.getName())); + + entrySuccessInstructions.append(fact.createInvoke(aspectname, "ajc$createAspectInstance", new ObjectType(aspectname), + new Type[] { new ObjectType("java.lang.String") }, Constants.INVOKESTATIC)); + entrySuccessInstructions.append(fact.createPutStatic(t.getName(), ptwField, new ObjectType(aspectname))); + + entryInstructions.append(entrySuccessInstructions); + + range.insert(entryInstructions, Range.InsideBefore); + } + + public void weaveCflowEntry(final BcelAdvice munger, final Member cflowField) { + final boolean isPer = munger.getKind() == AdviceKind.PerCflowBelowEntry || munger.getKind() == AdviceKind.PerCflowEntry; + if (!isPer && getKind() == PreInitialization) { + return; + } + final Type objectArrayType = new ArrayType(Type.OBJECT, 1); + final InstructionFactory fact = getFactory(); + + final BcelVar testResult = genTempVar(UnresolvedType.BOOLEAN); + + InstructionList entryInstructions = new InstructionList(); + { + InstructionList entrySuccessInstructions = new InstructionList(); + + if (munger.hasDynamicTests()) { + entryInstructions.append(Utility.createConstant(fact, 0)); + testResult.appendStore(entryInstructions, fact); + + entrySuccessInstructions.append(Utility.createConstant(fact, 1)); + testResult.appendStore(entrySuccessInstructions, fact); + } + + if (isPer) { + entrySuccessInstructions.append(fact.createInvoke(munger.getConcreteAspect().getName(), + NameMangler.PERCFLOW_PUSH_METHOD, Type.VOID, new Type[] {}, Constants.INVOKESTATIC)); + } else { + BcelVar[] cflowStateVars = munger.getExposedStateAsBcelVars(false); + + if (cflowStateVars.length == 0) { + // This should be getting managed by a counter - lets make sure. + if (!cflowField.getType().getName().endsWith("CFlowCounter")) { + throw new RuntimeException("Incorrectly attempting counter operation on stacked cflow"); + } + entrySuccessInstructions.append(Utility.createGet(fact, cflowField)); + // arrayVar.appendLoad(entrySuccessInstructions, fact); + entrySuccessInstructions.append(fact.createInvoke(NameMangler.CFLOW_COUNTER_TYPE, "inc", Type.VOID, + new Type[] {}, Constants.INVOKEVIRTUAL)); + } else { + BcelVar arrayVar = genTempVar(UnresolvedType.OBJECTARRAY); + + int alen = cflowStateVars.length; + entrySuccessInstructions.append(Utility.createConstant(fact, alen)); + entrySuccessInstructions.append(fact.createNewArray(Type.OBJECT, (short) 1)); + arrayVar.appendStore(entrySuccessInstructions, fact); + + for (int i = 0; i < alen; i++) { + arrayVar.appendConvertableArrayStore(entrySuccessInstructions, fact, i, cflowStateVars[i]); + } + + entrySuccessInstructions.append(Utility.createGet(fact, cflowField)); + arrayVar.appendLoad(entrySuccessInstructions, fact); + + entrySuccessInstructions.append(fact.createInvoke(NameMangler.CFLOW_STACK_TYPE, "push", Type.VOID, + new Type[] { objectArrayType }, Constants.INVOKEVIRTUAL)); + } + } + + InstructionList testInstructions = munger.getTestInstructions(this, entrySuccessInstructions.getStart(), + range.getRealStart(), entrySuccessInstructions.getStart()); + entryInstructions.append(testInstructions); + entryInstructions.append(entrySuccessInstructions); + } + + BcelAdvice exitAdvice = new BcelAdvice(null, null, null, 0, 0, 0, null, munger.getConcreteAspect()) { + @Override + public InstructionList getAdviceInstructions(BcelShadow s, BcelVar extraArgVar, InstructionHandle ifNoAdvice) { + InstructionList exitInstructions = new InstructionList(); + if (munger.hasDynamicTests()) { + testResult.appendLoad(exitInstructions, fact); + exitInstructions.append(InstructionFactory.createBranchInstruction(Constants.IFEQ, ifNoAdvice)); + } + exitInstructions.append(Utility.createGet(fact, cflowField)); + if (munger.getKind() != AdviceKind.PerCflowEntry && munger.getKind() != AdviceKind.PerCflowBelowEntry + && munger.getExposedStateAsBcelVars(false).length == 0) { + exitInstructions.append(fact.createInvoke(NameMangler.CFLOW_COUNTER_TYPE, "dec", Type.VOID, new Type[] {}, + Constants.INVOKEVIRTUAL)); + } else { + exitInstructions.append(fact.createInvoke(NameMangler.CFLOW_STACK_TYPE, "pop", Type.VOID, new Type[] {}, + Constants.INVOKEVIRTUAL)); + } + return exitInstructions; + } + }; +// if (getKind() == PreInitialization) { +// weaveAfterReturning(exitAdvice); +// } +// else { + weaveAfter(exitAdvice); +// } + + range.insert(entryInstructions, Range.InsideBefore); + } + + /* + * Implementation notes: + * + * AroundInline still extracts the instructions of the original shadow into an extracted method. This allows inlining of even + * that advice that doesn't call proceed or calls proceed more than once. + * + * It extracts the instructions of the original shadow into a method. + * + * Then it extracts the instructions of the advice into a new method defined on this enclosing class. This new method can then + * be specialized as below. + * + * Then it searches in the instructions of the advice for any call to the proceed method. + * + * At such a call, there is stuff on the stack representing the arguments to proceed. Pop these into the frame. + * + * Now build the stack for the call to the extracted method, taking values either from the join point state or from the new + * frame locs from proceed. Now call the extracted method. The right return value should be on the stack, so no cast is + * necessary. + * + * If only one call to proceed is made, we can re-inline the original shadow. We are not doing that presently. + * + * If the body of the advice can be determined to not alter the stack, or if this shadow doesn't care about the stack, i.e. + * method-execution, then the new method for the advice can also be re-lined. We are not doing that presently. + */ + public void weaveAroundInline(BcelAdvice munger, boolean hasDynamicTest) { + // !!! THIS BLOCK OF CODE SHOULD BE IN A METHOD CALLED weaveAround(...); + Member mungerSig = munger.getSignature(); + // Member originalSig = mungerSig; // If mungerSig is on a parameterized type, originalSig is the member on the generic type + if (mungerSig instanceof ResolvedMember) { + ResolvedMember rm = (ResolvedMember) mungerSig; + if (rm.hasBackingGenericMember()) { + mungerSig = rm.getBackingGenericMember(); + } + } + ResolvedType declaringAspectType = world.resolve(mungerSig.getDeclaringType(), true); + if (declaringAspectType.isMissing()) { + world.getLint().cantFindType.signal( + new String[] { WeaverMessages.format(WeaverMessages.CANT_FIND_TYPE_DURING_AROUND_WEAVE, + declaringAspectType.getClassName()) }, getSourceLocation(), + new ISourceLocation[] { munger.getSourceLocation() }); + } + + // ??? might want some checks here to give better errors + ResolvedType rt = (declaringAspectType.isParameterizedType() ? declaringAspectType.getGenericType() : declaringAspectType); + BcelObjectType ot = BcelWorld.getBcelObjectType(rt); + LazyMethodGen adviceMethod = ot.getLazyClassGen().getLazyMethodGen(mungerSig); + if (!adviceMethod.getCanInline()) { + weaveAroundClosure(munger, hasDynamicTest); + return; + } + + // specific test for @AJ proceedInInners + if (isAnnotationStylePassingProceedingJoinPointOutOfAdvice(munger, hasDynamicTest, adviceMethod)) { + return; + } + + // We can't inline around methods if they have around advice on them, this + // is because the weaving will extract the body and hence the proceed call. + + // TODO should consider optimizations to recognize simple cases that don't require body extraction + + enclosingMethod.setCanInline(false); + + LazyClassGen shadowClass = getEnclosingClass(); + + // Extract the shadow into a new method. For example: + // "private static final void method_aroundBody0(M, M, String, org.aspectj.lang.JoinPoint)" + // Parameters are: this if there is one, target if there is one and its different to this, then original arguments + // at the shadow, then tjp + String extractedShadowMethodName = NameMangler.aroundShadowMethodName(getSignature(), shadowClass.getNewGeneratedNameTag()); + List parameterNames = new ArrayList(); + boolean shadowClassIsInterface = shadowClass.isInterface(); + LazyMethodGen extractedShadowMethod = extractShadowInstructionsIntoNewMethod(extractedShadowMethodName, + shadowClassIsInterface?Modifier.PUBLIC:Modifier.PRIVATE, + munger.getSourceLocation(), parameterNames,shadowClassIsInterface); + + List argsToCallLocalAdviceMethodWith = new ArrayList(); + List proceedVarList = new ArrayList(); + int extraParamOffset = 0; + + // Create the extra parameters that are needed for passing to proceed + // This code is very similar to that found in makeCallToCallback and should + // be rationalized in the future + + if (thisVar != null) { + argsToCallLocalAdviceMethodWith.add(thisVar); + proceedVarList.add(new BcelVar(thisVar.getType(), extraParamOffset)); + extraParamOffset += thisVar.getType().getSize(); + } + + if (targetVar != null && targetVar != thisVar) { + argsToCallLocalAdviceMethodWith.add(targetVar); + proceedVarList.add(new BcelVar(targetVar.getType(), extraParamOffset)); + extraParamOffset += targetVar.getType().getSize(); + } + for (int i = 0, len = getArgCount(); i < len; i++) { + argsToCallLocalAdviceMethodWith.add(argVars[i]); + proceedVarList.add(new BcelVar(argVars[i].getType(), extraParamOffset)); + extraParamOffset += argVars[i].getType().getSize(); + } + if (thisJoinPointVar != null) { + argsToCallLocalAdviceMethodWith.add(thisJoinPointVar); + proceedVarList.add(new BcelVar(thisJoinPointVar.getType(), extraParamOffset)); + extraParamOffset += thisJoinPointVar.getType().getSize(); + } + + // We use the munger signature here because it allows for any parameterization of the mungers pointcut that + // may have occurred ie. if the pointcut is p(T t) in the super aspect and that has become p(Foo t) in the sub aspect + // then here the munger signature will have 'Foo' as an argument in it whilst the adviceMethod argument type will be + // 'Object' - since it represents the advice method in the superaspect which uses the erasure of the type variable p(Object + // t) - see pr174449. + + Type[] adviceParameterTypes = BcelWorld.makeBcelTypes(munger.getSignature().getParameterTypes()); + + // forces initialization ... dont like this but seems to be required for some tests to pass, I think that means there + // is a LazyMethodGen method that is not correctly setup to call initialize() when it is invoked - but I dont have + // time right now to discover which + adviceMethod.getArgumentTypes(); + + Type[] extractedMethodParameterTypes = extractedShadowMethod.getArgumentTypes(); + + Type[] parameterTypes = new Type[extractedMethodParameterTypes.length + adviceParameterTypes.length + 1]; + int parameterIndex = 0; + System.arraycopy(extractedMethodParameterTypes, 0, parameterTypes, parameterIndex, extractedMethodParameterTypes.length); + parameterIndex += extractedMethodParameterTypes.length; + parameterTypes[parameterIndex++] = BcelWorld.makeBcelType(adviceMethod.getEnclosingClass().getType()); + System.arraycopy(adviceParameterTypes, 0, parameterTypes, parameterIndex, adviceParameterTypes.length); + + // Extract the advice into a new method. This will go in the same type as the shadow + // name will be something like foo_aroundBody1$advice + String localAdviceMethodName = NameMangler.aroundAdviceMethodName(getSignature(), shadowClass.getNewGeneratedNameTag()); + int localAdviceMethodModifiers = Modifier.PRIVATE | (world.useFinal() & !shadowClassIsInterface ? Modifier.FINAL : 0) | Modifier.STATIC; + LazyMethodGen localAdviceMethod = new LazyMethodGen(localAdviceMethodModifiers, BcelWorld.makeBcelType(mungerSig.getReturnType()), localAdviceMethodName, parameterTypes, + NoDeclaredExceptions, shadowClass); + + // Doesnt work properly, so leave it out: + // String aspectFilename = adviceMethod.getEnclosingClass().getInternalFileName(); + // String shadowFilename = shadowClass.getInternalFileName(); + // if (!aspectFilename.equals(shadowFilename)) { + // localAdviceMethod.fromFilename = aspectFilename; + // shadowClass.addInlinedSourceFileInfo(aspectFilename, adviceMethod.highestLineNumber); + // } + + shadowClass.addMethodGen(localAdviceMethod); + + // create a map that will move all slots in advice method forward by extraParamOffset + // in order to make room for the new proceed-required arguments that are added at + // the beginning of the parameter list + int nVars = adviceMethod.getMaxLocals() + extraParamOffset; + IntMap varMap = IntMap.idMap(nVars); + for (int i = extraParamOffset; i < nVars; i++) { + varMap.put(i - extraParamOffset, i); + } + + final InstructionFactory fact = getFactory(); + + localAdviceMethod.getBody().insert( + BcelClassWeaver.genInlineInstructions(adviceMethod, localAdviceMethod, varMap, fact, true)); + + localAdviceMethod.setMaxLocals(nVars); + + // the shadow is now empty. First, create a correct call + // to the around advice. This includes both the call (which may involve + // value conversion of the advice arguments) and the return + // (which may involve value conversion of the return value). Right now + // we push a null for the unused closure. It's sad, but there it is. + + InstructionList advice = new InstructionList(); + // InstructionHandle adviceMethodInvocation; + { + for (Iterator i = argsToCallLocalAdviceMethodWith.iterator(); i.hasNext();) { + BcelVar var = i.next(); + var.appendLoad(advice, fact); + } + // ??? we don't actually need to push NULL for the closure if we take care + boolean isAnnoStyleConcreteAspect = munger.getConcreteAspect().isAnnotationStyleAspect(); + boolean isAnnoStyleDeclaringAspect = munger.getDeclaringAspect() != null ? munger.getDeclaringAspect().resolve(world) + .isAnnotationStyleAspect() : false; + + InstructionList iList = null; + if (isAnnoStyleConcreteAspect && isAnnoStyleDeclaringAspect) { + iList = this.loadThisJoinPoint(); + iList.append(Utility.createConversion(getFactory(), LazyClassGen.tjpType, LazyClassGen.proceedingTjpType)); + } else { + iList = new InstructionList(InstructionConstants.ACONST_NULL); + } + advice.append(munger.getAdviceArgSetup(this, null, iList)); + // adviceMethodInvocation = + advice.append(Utility.createInvoke(fact, localAdviceMethod)); // (fact, getWorld(), munger.getSignature())); + advice.append(Utility.createConversion(getFactory(), BcelWorld.makeBcelType(mungerSig.getReturnType()), + extractedShadowMethod.getReturnType(), world.isInJava5Mode())); + if (!isFallsThrough()) { + advice.append(InstructionFactory.createReturn(extractedShadowMethod.getReturnType())); + } + } + + // now, situate the call inside the possible dynamic tests, + // and actually add the whole mess to the shadow + if (!hasDynamicTest) { + range.append(advice); + } else { + InstructionList afterThingie = new InstructionList(InstructionConstants.NOP); + InstructionList callback = makeCallToCallback(extractedShadowMethod); + if (terminatesWithReturn()) { + callback.append(InstructionFactory.createReturn(extractedShadowMethod.getReturnType())); + } else { + // InstructionHandle endNop = range.insert(fact.NOP, Range.InsideAfter); + advice.append(InstructionFactory.createBranchInstruction(Constants.GOTO, afterThingie.getStart())); + } + range.append(munger.getTestInstructions(this, advice.getStart(), callback.getStart(), advice.getStart())); + range.append(advice); + range.append(callback); + range.append(afterThingie); + } + + // now search through the advice, looking for a call to PROCEED. + // Then we replace the call to proceed with some argument setup, and a + // call to the extracted method. + + // inlining support for code style aspects + if (!munger.getDeclaringType().isAnnotationStyleAspect()) { + String proceedName = NameMangler.proceedMethodName(munger.getSignature().getName()); + + InstructionHandle curr = localAdviceMethod.getBody().getStart(); + InstructionHandle end = localAdviceMethod.getBody().getEnd(); + ConstantPool cpg = localAdviceMethod.getEnclosingClass().getConstantPool(); + while (curr != end) { + InstructionHandle next = curr.getNext(); + Instruction inst = curr.getInstruction(); + if ((inst.opcode == Constants.INVOKESTATIC) && proceedName.equals(((InvokeInstruction) inst).getMethodName(cpg))) { + + localAdviceMethod.getBody().append(curr, + getRedoneProceedCall(fact, extractedShadowMethod, munger, localAdviceMethod, proceedVarList)); + Utility.deleteInstruction(curr, localAdviceMethod); + } + curr = next; + } + // and that's it. + } else { + // ATAJ inlining support for @AJ aspects + // [TODO document @AJ code rule: don't manipulate 2 jps proceed at the same time.. in an advice body] + InstructionHandle curr = localAdviceMethod.getBody().getStart(); + InstructionHandle end = localAdviceMethod.getBody().getEnd(); + ConstantPool cpg = localAdviceMethod.getEnclosingClass().getConstantPool(); + while (curr != end) { + InstructionHandle next = curr.getNext(); + Instruction inst = curr.getInstruction(); + if ((inst instanceof INVOKEINTERFACE) && "proceed".equals(((INVOKEINTERFACE) inst).getMethodName(cpg))) { + final boolean isProceedWithArgs; + if (((INVOKEINTERFACE) inst).getArgumentTypes(cpg).length == 1) { + // proceed with args as a boxed Object[] + isProceedWithArgs = true; + } else { + isProceedWithArgs = false; + } + InstructionList insteadProceedIl = getRedoneProceedCallForAnnotationStyle(fact, extractedShadowMethod, munger, + localAdviceMethod, proceedVarList, isProceedWithArgs); + localAdviceMethod.getBody().append(curr, insteadProceedIl); + Utility.deleteInstruction(curr, localAdviceMethod); + } + curr = next; + } + } + + // if (parameterNames.size() == 0) { + // On return we have inserted the advice body into the local advice method. We have remapped all the local variables + // that were referenced in the advice as we did the copy, and so the local variable table for localAdviceMethod is + // now lacking any information about all the initial variables. + InstructionHandle start = localAdviceMethod.getBody().getStart(); + InstructionHandle end = localAdviceMethod.getBody().getEnd(); + + // Find the real start and end + while (start.getInstruction().opcode == Constants.IMPDEP1) { + start = start.getNext(); + } + while (end.getInstruction().opcode == Constants.IMPDEP1) { + end = end.getPrev(); + } + Type[] args = localAdviceMethod.getArgumentTypes(); + int argNumber = 0; + for (int slot = 0; slot < extraParamOffset; argNumber++) { // slot will increase by the argument size each time + String argumentName = null; + if (argNumber >= args.length || parameterNames.size() == 0 || argNumber >= parameterNames.size()) { + // this should be unnecessary as I think all known joinpoints and helper methods + // propagate the parameter names around correctly - but just in case let us do this + // rather than fail. If a bug is raised reporting unknown as a local variable name + // then investigate the joinpoint giving rise to the ResolvedMember and why it has + // no parameter names specified + argumentName = new StringBuffer("unknown").append(argNumber).toString(); + } else { + argumentName = parameterNames.get(argNumber); + } + String argumentSignature = args[argNumber].getSignature(); + LocalVariableTag lvt = new LocalVariableTag(argumentSignature, argumentName, slot, 0); + start.addTargeter(lvt); + end.addTargeter(lvt); + slot += args[argNumber].getSize(); + } + } + + /** + * Check if the advice method passes a pjp parameter out via an invoke instruction - if so we can't risk inlining. + */ + private boolean isAnnotationStylePassingProceedingJoinPointOutOfAdvice(BcelAdvice munger, boolean hasDynamicTest, + LazyMethodGen adviceMethod) { + if (munger.getConcreteAspect().isAnnotationStyleAspect()) { + // if we can't find one proceed() we suspect that the call + // is happening in an inner class so we don't inline it. + // Note: for code style, this is done at Aspect compilation time. + boolean canSeeProceedPassedToOther = false; + InstructionHandle curr = adviceMethod.getBody().getStart(); + InstructionHandle end = adviceMethod.getBody().getEnd(); + ConstantPool cpg = adviceMethod.getEnclosingClass().getConstantPool(); + while (curr != end) { + InstructionHandle next = curr.getNext(); + Instruction inst = curr.getInstruction(); + if ((inst instanceof InvokeInstruction) + && ((InvokeInstruction) inst).getSignature(cpg).indexOf("Lorg/aspectj/lang/ProceedingJoinPoint;") > 0) { + // we may want to refine to exclude stuff returning jp ? + // does code style skip inline if i write dump(thisJoinPoint) ? + canSeeProceedPassedToOther = true;// we see one pjp passed around - dangerous + break; + } + curr = next; + } + if (canSeeProceedPassedToOther) { + // remember this decision to avoid re-analysis + adviceMethod.setCanInline(false); + weaveAroundClosure(munger, hasDynamicTest); + return true; + } + } + return false; + } + + private InstructionList getRedoneProceedCall(InstructionFactory fact, LazyMethodGen callbackMethod, BcelAdvice munger, + LazyMethodGen localAdviceMethod, List argVarList) { + InstructionList ret = new InstructionList(); + // we have on stack all the arguments for the ADVICE call. + // we have in frame somewhere all the arguments for the non-advice call. + + BcelVar[] adviceVars = munger.getExposedStateAsBcelVars(true); + IntMap proceedMap = makeProceedArgumentMap(adviceVars); + + // System.out.println(proceedMap + " for " + this); + // System.out.println(argVarList); + + ResolvedType[] proceedParamTypes = world.resolve(munger.getSignature().getParameterTypes()); + // remove this*JoinPoint* as arguments to proceed + if (munger.getBaseParameterCount() + 1 < proceedParamTypes.length) { + int len = munger.getBaseParameterCount() + 1; + ResolvedType[] newTypes = new ResolvedType[len]; + System.arraycopy(proceedParamTypes, 0, newTypes, 0, len); + proceedParamTypes = newTypes; + } + + // System.out.println("stateTypes: " + Arrays.asList(stateTypes)); + BcelVar[] proceedVars = Utility.pushAndReturnArrayOfVars(proceedParamTypes, ret, fact, localAdviceMethod); + + Type[] stateTypes = callbackMethod.getArgumentTypes(); + // System.out.println("stateTypes: " + Arrays.asList(stateTypes)); + + for (int i = 0, len = stateTypes.length; i < len; i++) { + Type stateType = stateTypes[i]; + ResolvedType stateTypeX = BcelWorld.fromBcel(stateType).resolve(world); + if (proceedMap.hasKey(i)) { + // throw new RuntimeException("unimplemented"); + proceedVars[proceedMap.get(i)].appendLoadAndConvert(ret, fact, stateTypeX); + } else { + argVarList.get(i).appendLoad(ret, fact); + } + } + + ret.append(Utility.createInvoke(fact, callbackMethod)); + ret.append(Utility.createConversion(fact, callbackMethod.getReturnType(), + BcelWorld.makeBcelType(munger.getSignature().getReturnType()), world.isInJava5Mode())); + return ret; + } + + // private static boolean bindsThisOrTarget(Pointcut pointcut) { + // ThisTargetFinder visitor = new ThisTargetFinder(); + // pointcut.accept(visitor, null); + // return visitor.bindsThisOrTarget; + // } + + // private static class ThisTargetFinder extends IdentityPointcutVisitor { + // boolean bindsThisOrTarget = false; + // + // public Object visit(ThisOrTargetPointcut node, Object data) { + // if (node.isBinding()) { + // bindsThisOrTarget = true; + // } + // return node; + // } + // + // public Object visit(AndPointcut node, Object data) { + // if (!bindsThisOrTarget) node.getLeft().accept(this, data); + // if (!bindsThisOrTarget) node.getRight().accept(this, data); + // return node; + // } + // + // public Object visit(NotPointcut node, Object data) { + // if (!bindsThisOrTarget) node.getNegatedPointcut().accept(this, data); + // return node; + // } + // + // public Object visit(OrPointcut node, Object data) { + // if (!bindsThisOrTarget) node.getLeft().accept(this, data); + // if (!bindsThisOrTarget) node.getRight().accept(this, data); + // return node; + // } + // } + + /** + * Annotation style handling for inlining. + * + * Note: The proceedingjoinpoint is already on the stack (since the user was calling pjp.proceed(...) + * + * The proceed map is ignored (in terms of argument repositioning) since we have a fixed expected format for annotation style. + * The aim here is to change the proceed() call into a call to the xxx_aroundBody0 method. + * + * + */ + private InstructionList getRedoneProceedCallForAnnotationStyle(InstructionFactory fact, LazyMethodGen callbackMethod, + BcelAdvice munger, LazyMethodGen localAdviceMethod, List argVarList, boolean isProceedWithArgs) { + InstructionList ret = new InstructionList(); + + // store the Object[] array on stack if proceed with args + if (isProceedWithArgs) { + + // STORE the Object[] into a local variable + Type objectArrayType = Type.OBJECT_ARRAY; + int theObjectArrayLocalNumber = localAdviceMethod.allocateLocal(objectArrayType); + ret.append(InstructionFactory.createStore(objectArrayType, theObjectArrayLocalNumber)); + + // STORE the ProceedingJoinPoint instance into a local variable + Type proceedingJpType = Type.getType("Lorg/aspectj/lang/ProceedingJoinPoint;"); + int pjpLocalNumber = localAdviceMethod.allocateLocal(proceedingJpType); + ret.append(InstructionFactory.createStore(proceedingJpType, pjpLocalNumber)); + + // Aim here initially is to determine whether the user will have provided a new + // this/target in the object array and consume them if they have, leaving us the rest of + // the arguments to process as regular arguments to the invocation at the original join point + + boolean pointcutBindsThis = bindsThis(munger); + boolean pointcutBindsTarget = bindsTarget(munger); + boolean targetIsSameAsThis = getKind().isTargetSameAsThis(); + + int nextArgumentToProvideForCallback = 0; + + if (hasThis()) { + if (!(pointcutBindsTarget && targetIsSameAsThis)) { + if (pointcutBindsThis) { + // they have supplied new this as first entry in object array, consume it + ret.append(InstructionFactory.createLoad(objectArrayType, theObjectArrayLocalNumber)); + ret.append(Utility.createConstant(fact, 0)); + ret.append(InstructionFactory.createArrayLoad(Type.OBJECT)); + ret.append(Utility.createConversion(fact, Type.OBJECT, callbackMethod.getArgumentTypes()[0])); + } else { + // use local variable 0 + ret.append(InstructionFactory.createALOAD(0)); + } + nextArgumentToProvideForCallback++; + } + } + + if (hasTarget()) { + if (pointcutBindsTarget) { + if (getKind().isTargetSameAsThis()) { + ret.append(InstructionFactory.createLoad(objectArrayType, theObjectArrayLocalNumber)); + ret.append(Utility.createConstant(fact, pointcutBindsThis ? 1 : 0)); + ret.append(InstructionFactory.createArrayLoad(Type.OBJECT)); + ret.append(Utility.createConversion(fact, Type.OBJECT, callbackMethod.getArgumentTypes()[0])); + } else { + int position = (hasThis() && pointcutBindsThis)? 1 : 0; + ret.append(InstructionFactory.createLoad(objectArrayType, theObjectArrayLocalNumber)); + ret.append(Utility.createConstant(fact, position)); + ret.append(InstructionFactory.createArrayLoad(Type.OBJECT)); + ret.append(Utility.createConversion(fact, Type.OBJECT, callbackMethod.getArgumentTypes()[nextArgumentToProvideForCallback])); + } + nextArgumentToProvideForCallback++; + } else { + if (getKind().isTargetSameAsThis()) { + // ret.append(new ALOAD(0)); + } else { + ret.append(InstructionFactory.createLoad(localAdviceMethod.getArgumentTypes()[0], hasThis() ? 1 : 0)); + nextArgumentToProvideForCallback++; + } + } + } + + // Where to start in the object array in order to pick up arguments + int indexIntoObjectArrayForArguments = (pointcutBindsThis ? 1 : 0) + (pointcutBindsTarget ? 1 : 0); + + int len = callbackMethod.getArgumentTypes().length; + for (int i = nextArgumentToProvideForCallback; i < len; i++) { + Type stateType = callbackMethod.getArgumentTypes()[i]; + BcelWorld.fromBcel(stateType).resolve(world); + if ("Lorg/aspectj/lang/JoinPoint;".equals(stateType.getSignature())) { + ret.append(new InstructionLV(Constants.ALOAD, pjpLocalNumber)); + } else { + ret.append(InstructionFactory.createLoad(objectArrayType, theObjectArrayLocalNumber)); + ret.append(Utility + .createConstant(fact, i - nextArgumentToProvideForCallback + indexIntoObjectArrayForArguments)); + ret.append(InstructionFactory.createArrayLoad(Type.OBJECT)); + ret.append(Utility.createConversion(fact, Type.OBJECT, stateType)); + } + } + + } else { + Type proceedingJpType = Type.getType("Lorg/aspectj/lang/ProceedingJoinPoint;"); + int localJp = localAdviceMethod.allocateLocal(proceedingJpType); + ret.append(InstructionFactory.createStore(proceedingJpType, localJp)); + + int idx = 0; + for (int i = 0, len = callbackMethod.getArgumentTypes().length; i < len; i++) { + Type stateType = callbackMethod.getArgumentTypes()[i]; + /* ResolvedType stateTypeX = */ + BcelWorld.fromBcel(stateType).resolve(world); + if ("Lorg/aspectj/lang/JoinPoint;".equals(stateType.getSignature())) { + ret.append(InstructionFactory.createALOAD(localJp));// from localAdvice signature + // } else if ("Lorg/aspectj/lang/ProceedingJoinPoint;".equals(stateType.getSignature())) { + // //FIXME ALEX? + // ret.append(new ALOAD(localJp));// from localAdvice signature + // // ret.append(fact.createCheckCast( + // // (ReferenceType) BcelWorld.makeBcelType(stateTypeX) + // // )); + // // cast ? + // + idx++; + } else { + ret.append(InstructionFactory.createLoad(stateType, idx)); + idx += stateType.getSize(); + } + } + } + + // do the callback invoke + ret.append(Utility.createInvoke(fact, callbackMethod)); + + // box it again. Handles cases where around advice does return something else than Object + if (!UnresolvedType.OBJECT.equals(munger.getSignature().getReturnType())) { + ret.append(Utility.createConversion(fact, callbackMethod.getReturnType(), Type.OBJECT)); + } + ret.append(Utility.createConversion(fact, callbackMethod.getReturnType(), + BcelWorld.makeBcelType(munger.getSignature().getReturnType()), world.isInJava5Mode())); + + return ret; + + // + // + // + // if (proceedMap.hasKey(i)) { + // ret.append(new ALOAD(i)); + // //throw new RuntimeException("unimplemented"); + // //proceedVars[proceedMap.get(i)].appendLoadAndConvert(ret, fact, stateTypeX); + // } else { + // //((BcelVar) argVarList.get(i)).appendLoad(ret, fact); + // //ret.append(new ALOAD(i)); + // if ("Lorg/aspectj/lang/JoinPoint;".equals(stateType.getSignature())) { + // ret.append(new ALOAD(i)); + // } else { + // ret.append(new ALOAD(i)); + // } + // } + // } + // + // ret.append(Utility.createInvoke(fact, callbackMethod)); + // ret.append(Utility.createConversion(fact, callbackMethod.getReturnType(), + // BcelWorld.makeBcelType(munger.getSignature().getReturnType()))); + // + // //ret.append(new ACONST_NULL());//will be POPed + // if (true) return ret; + // + // + // + // // we have on stack all the arguments for the ADVICE call. + // // we have in frame somewhere all the arguments for the non-advice call. + // + // BcelVar[] adviceVars = munger.getExposedStateAsBcelVars(); + // IntMap proceedMap = makeProceedArgumentMap(adviceVars); + // + // System.out.println(proceedMap + " for " + this); + // System.out.println(argVarList); + // + // ResolvedType[] proceedParamTypes = + // world.resolve(munger.getSignature().getParameterTypes()); + // // remove this*JoinPoint* as arguments to proceed + // if (munger.getBaseParameterCount()+1 < proceedParamTypes.length) { + // int len = munger.getBaseParameterCount()+1; + // ResolvedType[] newTypes = new ResolvedType[len]; + // System.arraycopy(proceedParamTypes, 0, newTypes, 0, len); + // proceedParamTypes = newTypes; + // } + // + // //System.out.println("stateTypes: " + Arrays.asList(stateTypes)); + // BcelVar[] proceedVars = + // Utility.pushAndReturnArrayOfVars(proceedParamTypes, ret, fact, localAdviceMethod); + // + // Type[] stateTypes = callbackMethod.getArgumentTypes(); + // // System.out.println("stateTypes: " + Arrays.asList(stateTypes)); + // + // for (int i=0, len=stateTypes.length; i < len; i++) { + // Type stateType = stateTypes[i]; + // ResolvedType stateTypeX = BcelWorld.fromBcel(stateType).resolve(world); + // if (proceedMap.hasKey(i)) { + // //throw new RuntimeException("unimplemented"); + // proceedVars[proceedMap.get(i)].appendLoadAndConvert(ret, fact, stateTypeX); + // } else { + // ((BcelVar) argVarList.get(i)).appendLoad(ret, fact); + // } + // } + // + // ret.append(Utility.createInvoke(fact, callbackMethod)); + // ret.append(Utility.createConversion(fact, callbackMethod.getReturnType(), + // BcelWorld.makeBcelType(munger.getSignature().getReturnType()))); + // return ret; + } + + private boolean bindsThis(BcelAdvice munger) { + UsesThisVisitor utv = new UsesThisVisitor(); + munger.getPointcut().accept(utv, null); + return utv.usesThis; + } + + private boolean bindsTarget(BcelAdvice munger) { + UsesTargetVisitor utv = new UsesTargetVisitor(); + munger.getPointcut().accept(utv, null); + return utv.usesTarget; + } + + private static class UsesThisVisitor extends AbstractPatternNodeVisitor { + boolean usesThis = false; + + @Override + public Object visit(ThisOrTargetPointcut node, Object data) { + if (node.isThis() && node.isBinding()) { + usesThis = true; + } + return node; + } + + @Override + public Object visit(AndPointcut node, Object data) { + if (!usesThis) { + node.getLeft().accept(this, data); + } + if (!usesThis) { + node.getRight().accept(this, data); + } + return node; + } + + @Override + public Object visit(NotPointcut node, Object data) { + if (!usesThis) { + node.getNegatedPointcut().accept(this, data); + } + return node; + } + + @Override + public Object visit(OrPointcut node, Object data) { + if (!usesThis) { + node.getLeft().accept(this, data); + } + if (!usesThis) { + node.getRight().accept(this, data); + } + return node; + } + } + + private static class UsesTargetVisitor extends AbstractPatternNodeVisitor { + boolean usesTarget = false; + + @Override + public Object visit(ThisOrTargetPointcut node, Object data) { + if (!node.isThis() && node.isBinding()) { + usesTarget = true; + } + return node; + } + + @Override + public Object visit(AndPointcut node, Object data) { + if (!usesTarget) { + node.getLeft().accept(this, data); + } + if (!usesTarget) { + node.getRight().accept(this, data); + } + return node; + } + + @Override + public Object visit(NotPointcut node, Object data) { + if (!usesTarget) { + node.getNegatedPointcut().accept(this, data); + } + return node; + } + + @Override + public Object visit(OrPointcut node, Object data) { + if (!usesTarget) { + node.getLeft().accept(this, data); + } + if (!usesTarget) { + node.getRight().accept(this, data); + } + return node; + } + } + + public void weaveAroundClosure(BcelAdvice munger, boolean hasDynamicTest) { + InstructionFactory fact = getFactory(); + + enclosingMethod.setCanInline(false); + + int linenumber = getSourceLine(); + // MOVE OUT ALL THE INSTRUCTIONS IN MY SHADOW INTO ANOTHER METHOD! + + // callbackMethod will be something like: "static final void m_aroundBody0(I)" + boolean shadowClassIsInterface = getEnclosingClass().isInterface(); + LazyMethodGen callbackMethod = extractShadowInstructionsIntoNewMethod( + NameMangler.aroundShadowMethodName(getSignature(), getEnclosingClass().getNewGeneratedNameTag()), shadowClassIsInterface?Modifier.PUBLIC:0, + munger.getSourceLocation(), new ArrayList(),shadowClassIsInterface); + + BcelVar[] adviceVars = munger.getExposedStateAsBcelVars(true); + + String closureClassName = NameMangler.makeClosureClassName(getEnclosingClass().getType(), getEnclosingClass() + .getNewGeneratedNameTag()); + + Member constructorSig = new MemberImpl(Member.CONSTRUCTOR, UnresolvedType.forName(closureClassName), 0, "", + "([Ljava/lang/Object;)V"); + + BcelVar closureHolder = null; + + // This is not being used currently since getKind() == preinitializaiton + // cannot happen in around advice + if (getKind() == PreInitialization) { + closureHolder = genTempVar(AjcMemberMaker.AROUND_CLOSURE_TYPE); + } + + InstructionList closureInstantiation = makeClosureInstantiation(constructorSig, closureHolder); + + /* LazyMethodGen constructor = */ + makeClosureClassAndReturnConstructor(closureClassName, callbackMethod, makeProceedArgumentMap(adviceVars)); + + InstructionList returnConversionCode; + if (getKind() == PreInitialization) { + returnConversionCode = new InstructionList(); + + BcelVar stateTempVar = genTempVar(UnresolvedType.OBJECTARRAY); + closureHolder.appendLoad(returnConversionCode, fact); + + returnConversionCode.append(Utility.createInvoke(fact, world, AjcMemberMaker.aroundClosurePreInitializationGetter())); + stateTempVar.appendStore(returnConversionCode, fact); + + Type[] stateTypes = getSuperConstructorParameterTypes(); + + returnConversionCode.append(InstructionConstants.ALOAD_0); // put "this" back on the stack + for (int i = 0, len = stateTypes.length; i < len; i++) { + UnresolvedType bcelTX = BcelWorld.fromBcel(stateTypes[i]); + ResolvedType stateRTX = world.resolve(bcelTX, true); + if (stateRTX.isMissing()) { + world.getLint().cantFindType.signal( + new String[] { WeaverMessages.format(WeaverMessages.CANT_FIND_TYPE_DURING_AROUND_WEAVE_PREINIT, + bcelTX.getClassName()) }, getSourceLocation(), + new ISourceLocation[] { munger.getSourceLocation() }); + // IMessage msg = new Message( + // WeaverMessages.format(WeaverMessages.CANT_FIND_TYPE_DURING_AROUND_WEAVE_PREINIT,bcelTX.getClassName()), + // "",IMessage.ERROR,getSourceLocation(),null, + // new ISourceLocation[]{ munger.getSourceLocation()}); + // world.getMessageHandler().handleMessage(msg); + } + stateTempVar.appendConvertableArrayLoad(returnConversionCode, fact, i, stateRTX); + } + } else { + // pr226201 + Member mungerSignature = munger.getSignature(); + if (munger.getSignature() instanceof ResolvedMember) { + if (((ResolvedMember) mungerSignature).hasBackingGenericMember()) { + mungerSignature = ((ResolvedMember) mungerSignature).getBackingGenericMember(); + } + } + UnresolvedType returnType = mungerSignature.getReturnType(); + returnConversionCode = Utility.createConversion(getFactory(), BcelWorld.makeBcelType(returnType), + callbackMethod.getReturnType(), world.isInJava5Mode()); + if (!isFallsThrough()) { + returnConversionCode.append(InstructionFactory.createReturn(callbackMethod.getReturnType())); + } + } + + // initialize the bit flags for this shadow + int bitflags = 0x000000; + if (getKind().isTargetSameAsThis()) { + bitflags |= 0x010000; + } + if (hasThis()) { + bitflags |= 0x001000; + } + if (bindsThis(munger)) { + bitflags |= 0x000100; + } + if (hasTarget()) { + bitflags |= 0x000010; + } + if (bindsTarget(munger)) { + bitflags |= 0x000001; + } + + // ATAJ for @AJ aspect we need to link the closure with the joinpoint instance + if (munger.getConcreteAspect() != null && munger.getConcreteAspect().isAnnotationStyleAspect() + && munger.getDeclaringAspect() != null && munger.getDeclaringAspect().resolve(world).isAnnotationStyleAspect()) { + // stick the bitflags on the stack and call the variant of linkClosureAndJoinPoint that takes an int + closureInstantiation.append(fact.createConstant(Integer.valueOf(bitflags))); + closureInstantiation.append(Utility.createInvoke(getFactory(), getWorld(), + new MemberImpl(Member.METHOD, UnresolvedType.forName("org.aspectj.runtime.internal.AroundClosure"), + Modifier.PUBLIC, "linkClosureAndJoinPoint", String.format("%s%s", "(I)", "Lorg/aspectj/lang/ProceedingJoinPoint;")))); + } + + InstructionList advice = new InstructionList(); + advice.append(munger.getAdviceArgSetup(this, null, closureInstantiation)); + + // invoke the advice + advice.append(munger.getNonTestAdviceInstructions(this)); + advice.append(returnConversionCode); + if (getKind() == Shadow.MethodExecution && linenumber > 0) { + advice.getStart().addTargeter(new LineNumberTag(linenumber)); + } + + if (!hasDynamicTest) { + range.append(advice); + } else { + InstructionList callback = makeCallToCallback(callbackMethod); + InstructionList postCallback = new InstructionList(); + if (terminatesWithReturn()) { + callback.append(InstructionFactory.createReturn(callbackMethod.getReturnType())); + } else { + advice.append(InstructionFactory.createBranchInstruction(Constants.GOTO, + postCallback.append(InstructionConstants.NOP))); + } + range.append(munger.getTestInstructions(this, advice.getStart(), callback.getStart(), advice.getStart())); + range.append(advice); + range.append(callback); + range.append(postCallback); + } + } + + // exposed for testing + InstructionList makeCallToCallback(LazyMethodGen callbackMethod) { + InstructionFactory fact = getFactory(); + InstructionList callback = new InstructionList(); + if (thisVar != null) { + callback.append(InstructionConstants.ALOAD_0); + } + if (targetVar != null && targetVar != thisVar) { + callback.append(BcelRenderer.renderExpr(fact, world, targetVar)); + } + callback.append(BcelRenderer.renderExprs(fact, world, argVars)); + // remember to render tjps + if (thisJoinPointVar != null) { + callback.append(BcelRenderer.renderExpr(fact, world, thisJoinPointVar)); + } + callback.append(Utility.createInvoke(fact, callbackMethod)); + return callback; + } + + /** side-effect-free */ + private InstructionList makeClosureInstantiation(Member constructor, BcelVar holder) { + + // LazyMethodGen constructor) { + InstructionFactory fact = getFactory(); + BcelVar arrayVar = genTempVar(UnresolvedType.OBJECTARRAY); + // final Type objectArrayType = new ArrayType(Type.OBJECT, 1); + final InstructionList il = new InstructionList(); + int alen = getArgCount() + (thisVar == null ? 0 : 1) + ((targetVar != null && targetVar != thisVar) ? 1 : 0) + + (thisJoinPointVar == null ? 0 : 1); + il.append(Utility.createConstant(fact, alen)); + il.append(fact.createNewArray(Type.OBJECT, (short) 1)); + arrayVar.appendStore(il, fact); + + int stateIndex = 0; + if (thisVar != null) { + arrayVar.appendConvertableArrayStore(il, fact, stateIndex, thisVar); + thisVar.setPositionInAroundState(stateIndex); + stateIndex++; + } + if (targetVar != null && targetVar != thisVar) { + arrayVar.appendConvertableArrayStore(il, fact, stateIndex, targetVar); + targetVar.setPositionInAroundState(stateIndex); + stateIndex++; + } + for (int i = 0, len = getArgCount(); i < len; i++) { + arrayVar.appendConvertableArrayStore(il, fact, stateIndex, argVars[i]); + argVars[i].setPositionInAroundState(stateIndex); + stateIndex++; + } + if (thisJoinPointVar != null) { + arrayVar.appendConvertableArrayStore(il, fact, stateIndex, thisJoinPointVar); + thisJoinPointVar.setPositionInAroundState(stateIndex); + stateIndex++; + } + il.append(fact.createNew(new ObjectType(constructor.getDeclaringType().getName()))); + il.append(InstructionConstants.DUP); + arrayVar.appendLoad(il, fact); + il.append(Utility.createInvoke(fact, world, constructor)); + if (getKind() == PreInitialization) { + il.append(InstructionConstants.DUP); + holder.appendStore(il, fact); + } + return il; + } + + private IntMap makeProceedArgumentMap(BcelVar[] adviceArgs) { + // System.err.println("coming in with " + Arrays.asList(adviceArgs)); + + IntMap ret = new IntMap(); + for (int i = 0, len = adviceArgs.length; i < len; i++) { + BcelVar v = adviceArgs[i]; + if (v == null) { + continue; // XXX we don't know why this is required + } + int pos = v.getPositionInAroundState(); + if (pos >= 0) { // need this test to avoid args bound via cflow + ret.put(pos, i); + } + } + // System.err.println("returning " + ret); + + return ret; + } + + /** + * + * @param callbackMethod the method we will call back to when our run method gets called. + * @param proceedMap A map from state position to proceed argument position. May be non covering on state position. + */ + private LazyMethodGen makeClosureClassAndReturnConstructor(String closureClassName, LazyMethodGen callbackMethod, + IntMap proceedMap) { + String superClassName = "org.aspectj.runtime.internal.AroundClosure"; + Type objectArrayType = new ArrayType(Type.OBJECT, 1); + + LazyClassGen closureClass = new LazyClassGen(closureClassName, superClassName, getEnclosingClass().getFileName(), + Modifier.PUBLIC, new String[] {}, getWorld()); + closureClass.setMajorMinor(getEnclosingClass().getMajor(), getEnclosingClass().getMinor()); + InstructionFactory fact = new InstructionFactory(closureClass.getConstantPool()); + + // constructor + LazyMethodGen constructor = new LazyMethodGen(Modifier.PUBLIC, Type.VOID, "", new Type[] { objectArrayType }, + new String[] {}, closureClass); + InstructionList cbody = constructor.getBody(); + cbody.append(InstructionFactory.createLoad(Type.OBJECT, 0)); + cbody.append(InstructionFactory.createLoad(objectArrayType, 1)); + cbody.append(fact + .createInvoke(superClassName, "", Type.VOID, new Type[] { objectArrayType }, Constants.INVOKESPECIAL)); + cbody.append(InstructionFactory.createReturn(Type.VOID)); + + closureClass.addMethodGen(constructor); + + // Create the 'Object run(Object[])' method + LazyMethodGen runMethod = new LazyMethodGen(Modifier.PUBLIC, Type.OBJECT, "run", new Type[] { objectArrayType }, + new String[] {}, closureClass); + InstructionList mbody = runMethod.getBody(); + BcelVar proceedVar = new BcelVar(UnresolvedType.OBJECTARRAY.resolve(world), 1); + // int proceedVarIndex = 1; + BcelVar stateVar = new BcelVar(UnresolvedType.OBJECTARRAY.resolve(world), runMethod.allocateLocal(1)); + // int stateVarIndex = runMethod.allocateLocal(1); + mbody.append(InstructionFactory.createThis()); + mbody.append(fact.createGetField(superClassName, "state", objectArrayType)); + mbody.append(stateVar.createStore(fact)); + // mbody.append(fact.createStore(objectArrayType, stateVarIndex)); + + Type[] stateTypes = callbackMethod.getArgumentTypes(); + + for (int i = 0, len = stateTypes.length; i < len; i++) { + ResolvedType resolvedStateType = BcelWorld.fromBcel(stateTypes[i]).resolve(world); + if (proceedMap.hasKey(i)) { + mbody.append(proceedVar.createConvertableArrayLoad(fact, proceedMap.get(i), resolvedStateType)); + } else { + mbody.append(stateVar.createConvertableArrayLoad(fact, i, resolvedStateType)); + } + } + + mbody.append(Utility.createInvoke(fact, callbackMethod)); + + if (getKind() == PreInitialization) { + mbody.append(Utility.createSet(fact, AjcMemberMaker.aroundClosurePreInitializationField())); + mbody.append(InstructionConstants.ACONST_NULL); + } else { + mbody.append(Utility.createConversion(fact, callbackMethod.getReturnType(), Type.OBJECT)); + } + mbody.append(InstructionFactory.createReturn(Type.OBJECT)); + + closureClass.addMethodGen(runMethod); + + // class + getEnclosingClass().addGeneratedInner(closureClass); + + return constructor; + } + + // ---- extraction methods + + /** + * Extract the instructions in the shadow to a new method. + * + * @param extractedMethodName name for the new method + * @param extractedMethodVisibilityModifier visibility modifiers for the new method + * @param adviceSourceLocation source location of the advice affecting the shadow + * @param beingPlacedInInterface is this new method going into an interface + */ + LazyMethodGen extractShadowInstructionsIntoNewMethod(String extractedMethodName, int extractedMethodVisibilityModifier, + ISourceLocation adviceSourceLocation, List parameterNames, boolean beingPlacedInInterface) { + // LazyMethodGen.assertGoodBody(range.getBody(), extractedMethodName); + if (!getKind().allowsExtraction()) { + throw new BCException("Attempt to extract method from a shadow kind (" + getKind() + + ") that does not support this operation"); + } + LazyMethodGen newMethod = createShadowMethodGen(extractedMethodName, extractedMethodVisibilityModifier, parameterNames, beingPlacedInInterface); + IntMap remapper = makeRemap(); + range.extractInstructionsInto(newMethod, remapper, (getKind() != PreInitialization) && isFallsThrough()); + if (getKind() == PreInitialization) { + addPreInitializationReturnCode(newMethod, getSuperConstructorParameterTypes()); + } + getEnclosingClass().addMethodGen(newMethod, adviceSourceLocation); + return newMethod; + } + + private void addPreInitializationReturnCode(LazyMethodGen extractedMethod, Type[] superConstructorTypes) { + InstructionList body = extractedMethod.getBody(); + final InstructionFactory fact = getFactory(); + + BcelVar arrayVar = new BcelVar(world.getCoreType(UnresolvedType.OBJECTARRAY), extractedMethod.allocateLocal(1)); + + int len = superConstructorTypes.length; + + body.append(Utility.createConstant(fact, len)); + + body.append(fact.createNewArray(Type.OBJECT, (short) 1)); + arrayVar.appendStore(body, fact); + + for (int i = len - 1; i >= 0; i++) { + // convert thing on top of stack to object + body.append(Utility.createConversion(fact, superConstructorTypes[i], Type.OBJECT)); + // push object array + arrayVar.appendLoad(body, fact); + // swap + body.append(InstructionConstants.SWAP); + // do object array store. + body.append(Utility.createConstant(fact, i)); + body.append(InstructionConstants.SWAP); + body.append(InstructionFactory.createArrayStore(Type.OBJECT)); + } + arrayVar.appendLoad(body, fact); + body.append(InstructionConstants.ARETURN); + } + + private Type[] getSuperConstructorParameterTypes() { + // assert getKind() == PreInitialization + InstructionHandle superCallHandle = getRange().getEnd().getNext(); + InvokeInstruction superCallInstruction = (InvokeInstruction) superCallHandle.getInstruction(); + return superCallInstruction.getArgumentTypes(getEnclosingClass().getConstantPool()); + } + + /** + * make a map from old frame location to new frame location. Any unkeyed frame location picks out a copied local + */ + private IntMap makeRemap() { + IntMap ret = new IntMap(5); + int reti = 0; + if (thisVar != null) { + ret.put(0, reti++); // thisVar guaranteed to be 0 + } + if (targetVar != null && targetVar != thisVar) { + ret.put(targetVar.getSlot(), reti++); + } + for (int i = 0, len = argVars.length; i < len; i++) { + ret.put(argVars[i].getSlot(), reti); + reti += argVars[i].getType().getSize(); + } + if (thisJoinPointVar != null) { + ret.put(thisJoinPointVar.getSlot(), reti++); + } + // we not only need to put the arguments, we also need to remap their + // aliases, which we so helpfully put into temps at the beginning of this join + // point. + if (!getKind().argsOnStack()) { + int oldi = 0; + int newi = 0; + // if we're passing in a this and we're not argsOnStack we're always + // passing in a target too + if (arg0HoldsThis()) { + ret.put(0, 0); + oldi++; + newi += 1; + } + // assert targetVar == thisVar + for (int i = 0; i < getArgCount(); i++) { + UnresolvedType type = getArgType(i); + ret.put(oldi, newi); + oldi += type.getSize(); + newi += type.getSize(); + } + } + + // System.err.println("making remap for : " + this); + // if (targetVar != null) System.err.println("target slot : " + targetVar.getSlot()); + // if (thisVar != null) System.err.println(" this slot : " + thisVar.getSlot()); + // System.err.println(ret); + + return ret; + } + + /** + * The new method always static. It may take some extra arguments: this, target. If it's argsOnStack, then it must take both + * this/target If it's argsOnFrame, it shares this and target. ??? rewrite this to do less array munging, please + */ + private LazyMethodGen createShadowMethodGen(String newMethodName, int visibilityModifier, List parameterNames, boolean beingPlacedInInterface) { + Type[] shadowParameterTypes = BcelWorld.makeBcelTypes(getArgTypes()); + int modifiers = (world.useFinal() && !beingPlacedInInterface ? Modifier.FINAL : 0) | Modifier.STATIC | visibilityModifier; + if (targetVar != null && targetVar != thisVar) { + UnresolvedType targetType = getTargetType(); + targetType = ensureTargetTypeIsCorrect(targetType); + // see pr109728,pr229910 - this fixes the case when the declaring class is sometype 'X' but the (gs)etfield + // in the bytecode refers to a subtype of 'X'. This makes sure we use the type originally + // mentioned in the fieldget instruction as the method parameter and *not* the type upon which the + // field is declared because when the instructions are extracted into the new around body, + // they will still refer to the subtype. + if ((getKind() == FieldGet || getKind() == FieldSet) && getActualTargetType() != null + && !getActualTargetType().equals(targetType.getName())) { + targetType = UnresolvedType.forName(getActualTargetType()).resolve(world); + } + ResolvedMember resolvedMember = getSignature().resolve(world); + + // pr230075, pr197719 + if (resolvedMember != null && Modifier.isProtected(resolvedMember.getModifiers()) + && !samePackage(resolvedMember.getDeclaringType().getPackageName(), getEnclosingType().getPackageName()) + && !resolvedMember.getName().equals("clone")) { + if (!hasThis()) { // pr197719 - static accessor has been created to handle the call + if (Modifier.isStatic(enclosingMethod.getAccessFlags()) && enclosingMethod.getName().startsWith("access$")) { + targetType = BcelWorld.fromBcel(enclosingMethod.getArgumentTypes()[0]); + } + } else { + if (!targetType.resolve(world).isAssignableFrom(getThisType().resolve(world))) { + throw new BCException("bad bytecode"); + } + targetType = getThisType(); + } + } + parameterNames.add("target"); + // There is a 'target' and it is not the same as 'this', so add it to the parameter list + shadowParameterTypes = addTypeToFront(BcelWorld.makeBcelType(targetType), shadowParameterTypes); + } + + if (thisVar != null) { + UnresolvedType thisType = getThisType(); + parameterNames.add(0, "ajc$this"); + shadowParameterTypes = addTypeToFront(BcelWorld.makeBcelType(thisType), shadowParameterTypes); + } + + if (this.getKind() == Shadow.FieldSet || this.getKind() == Shadow.FieldGet) { + parameterNames.add(getSignature().getName()); + } else { + String[] pnames = getSignature().getParameterNames(world); + if (pnames != null) { + for (int i = 0; i < pnames.length; i++) { + if (i == 0 && pnames[i].equals("this")) { + parameterNames.add("ajc$this"); + } else { + parameterNames.add(pnames[i]); + } + } + } + } + + // We always want to pass down thisJoinPoint in case we have already woven + // some advice in here. If we only have a single piece of around advice on a + // join point, it is unnecessary to accept (and pass) tjp. + if (thisJoinPointVar != null) { + parameterNames.add("thisJoinPoint"); + shadowParameterTypes = addTypeToEnd(LazyClassGen.tjpType, shadowParameterTypes); + } + + UnresolvedType returnType; + if (getKind() == PreInitialization) { + returnType = UnresolvedType.OBJECTARRAY; + } else { + if (getKind() == ConstructorCall) { + returnType = getSignature().getDeclaringType(); + } else if (getKind() == FieldSet) { + returnType = UnresolvedType.VOID; + } else { + returnType = getSignature().getReturnType().resolve(world); + // returnType = getReturnType(); // for this and above lines, see pr137496 + } + } + return new LazyMethodGen(modifiers, BcelWorld.makeBcelType(returnType), newMethodName, shadowParameterTypes, + NoDeclaredExceptions, getEnclosingClass()); + } + + private boolean samePackage(String p1, String p2) { + if (p1 == null) { + return p2 == null; + } + if (p2 == null) { + return false; + } + return p1.equals(p2); + } + + private Type[] addTypeToFront(Type type, Type[] types) { + int len = types.length; + Type[] ret = new Type[len + 1]; + ret[0] = type; + System.arraycopy(types, 0, ret, 1, len); + return ret; + } + + private Type[] addTypeToEnd(Type type, Type[] types) { + int len = types.length; + Type[] ret = new Type[len + 1]; + ret[len] = type; + System.arraycopy(types, 0, ret, 0, len); + return ret; + } + + public BcelVar genTempVar(UnresolvedType utype) { + ResolvedType rtype = utype.resolve(world); + return new BcelVar(rtype, genTempVarIndex(rtype.getSize())); + } + + // public static final boolean CREATE_TEMP_NAMES = true; + + public BcelVar genTempVar(UnresolvedType typeX, String localName) { + BcelVar tv = genTempVar(typeX); + + // if (CREATE_TEMP_NAMES) { + // for (InstructionHandle ih = range.getStart(); ih != range.getEnd(); ih = ih.getNext()) { + // if (Range.isRangeHandle(ih)) continue; + // ih.addTargeter(new LocalVariableTag(typeX, localName, tv.getSlot())); + // } + // } + return tv; + } + + // eh doesn't think we need to garbage collect these (64K is a big number...) + private int genTempVarIndex(int size) { + return enclosingMethod.allocateLocal(size); + } + + public InstructionFactory getFactory() { + return getEnclosingClass().getFactory(); + } + + @Override + public ISourceLocation getSourceLocation() { + int sourceLine = getSourceLine(); + if (sourceLine == 0 || sourceLine == -1) { + // Thread.currentThread().dumpStack(); + // System.err.println(this + ": " + range); + return getEnclosingClass().getType().getSourceLocation(); + } else { + // For staticinitialization, if we have a nice offset, don't build a new source loc + if (getKind() == Shadow.StaticInitialization && getEnclosingClass().getType().getSourceLocation().getOffset() != 0) { + return getEnclosingClass().getType().getSourceLocation(); + } else { + int offset = 0; + Kind kind = getKind(); + if ((kind == MethodExecution) || (kind == ConstructorExecution) || (kind == AdviceExecution) + || (kind == StaticInitialization) || (kind == PreInitialization) || (kind == Initialization)) { + if (getEnclosingMethod().hasDeclaredLineNumberInfo()) { + offset = getEnclosingMethod().getDeclarationOffset(); + } + } + return getEnclosingClass().getType().getSourceContext().makeSourceLocation(sourceLine, offset); + } + } + } + + public Shadow getEnclosingShadow() { + return enclosingShadow; + } + + public LazyMethodGen getEnclosingMethod() { + return enclosingMethod; + } + + public boolean isFallsThrough() { + return !terminatesWithReturn(); + } + + public void setActualTargetType(String className) { + this.actualInstructionTargetType = className; + } + + public String getActualTargetType() { + return actualInstructionTargetType; + } +} \ No newline at end of file diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/BcelTypeMunger.java b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelTypeMunger.java new file mode 100644 index 000000000..b92760fe9 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelTypeMunger.java @@ -0,0 +1,2116 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * Alexandre Vasseur @AspectJ ITDs + * ******************************************************************/ + +package org.aspectj.weaver.bcel; + +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.aspectj.apache.bcel.Constants; +import org.aspectj.apache.bcel.classfile.ClassFormatException; +import org.aspectj.apache.bcel.classfile.ConstantPool; +import org.aspectj.apache.bcel.classfile.Signature; +import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; +import org.aspectj.apache.bcel.generic.FieldGen; +import org.aspectj.apache.bcel.generic.InstructionBranch; +import org.aspectj.apache.bcel.generic.InstructionConstants; +import org.aspectj.apache.bcel.generic.InstructionFactory; +import org.aspectj.apache.bcel.generic.InstructionHandle; +import org.aspectj.apache.bcel.generic.InstructionList; +import org.aspectj.apache.bcel.generic.InvokeInstruction; +import org.aspectj.apache.bcel.generic.Type; +import org.aspectj.asm.AsmManager; +import org.aspectj.asm.IProgramElement; +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.Message; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.bridge.WeaveMessage; +import org.aspectj.bridge.context.CompilationAndWeavingContext; +import org.aspectj.bridge.context.ContextToken; +import org.aspectj.weaver.AjcMemberMaker; +import org.aspectj.weaver.AnnotationAJ; +import org.aspectj.weaver.AnnotationOnTypeMunger; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.ConcreteTypeMunger; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.MemberUtils; +import org.aspectj.weaver.MethodDelegateTypeMunger; +import org.aspectj.weaver.NameMangler; +import org.aspectj.weaver.NewConstructorTypeMunger; +import org.aspectj.weaver.NewFieldTypeMunger; +import org.aspectj.weaver.NewMemberClassTypeMunger; +import org.aspectj.weaver.NewMethodTypeMunger; +import org.aspectj.weaver.NewParentTypeMunger; +import org.aspectj.weaver.PerObjectInterfaceTypeMunger; +import org.aspectj.weaver.PrivilegedAccessMunger; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedMemberImpl; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.ResolvedTypeMunger; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.TypeVariableReference; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.WeaverStateInfo; +import org.aspectj.weaver.World; +import org.aspectj.weaver.model.AsmRelationshipProvider; +import org.aspectj.weaver.patterns.DeclareAnnotation; +import org.aspectj.weaver.patterns.Pointcut; + +public class BcelTypeMunger extends ConcreteTypeMunger { + + public BcelTypeMunger(ResolvedTypeMunger munger, ResolvedType aspectType) { + super(munger, aspectType); + } + + @Override + public String toString() { + return "(BcelTypeMunger " + getMunger() + ")"; + } + + @Override + public boolean shouldOverwrite() { + return false; + } + + public boolean munge(BcelClassWeaver weaver) { + ContextToken tok = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.MUNGING_WITH, this); + boolean changed = false; + boolean worthReporting = true; + + if (weaver.getWorld().isOverWeaving()) { + WeaverStateInfo typeWeaverState = weaver.getLazyClassGen().getType().getWeaverState(); + if (typeWeaverState != null && typeWeaverState.isAspectAlreadyApplied(getAspectType())) { + return false; + } + } + + if (munger.getKind() == ResolvedTypeMunger.Field) { + changed = mungeNewField(weaver, (NewFieldTypeMunger) munger); + } else if (munger.getKind() == ResolvedTypeMunger.Method) { + changed = mungeNewMethod(weaver, (NewMethodTypeMunger) munger); + } else if (munger.getKind() == ResolvedTypeMunger.InnerClass) { + changed = mungeNewMemberType(weaver, (NewMemberClassTypeMunger) munger); + } else if (munger.getKind() == ResolvedTypeMunger.MethodDelegate2) { + changed = mungeMethodDelegate(weaver, (MethodDelegateTypeMunger) munger); + } else if (munger.getKind() == ResolvedTypeMunger.FieldHost) { + changed = mungeFieldHost(weaver, (MethodDelegateTypeMunger.FieldHostTypeMunger) munger); + } else if (munger.getKind() == ResolvedTypeMunger.PerObjectInterface) { + changed = mungePerObjectInterface(weaver, (PerObjectInterfaceTypeMunger) munger); + worthReporting = false; + } else if (munger.getKind() == ResolvedTypeMunger.PerTypeWithinInterface) { + // PTWIMPL Transform the target type (add the aspect instance field) + changed = mungePerTypeWithinTransformer(weaver); + worthReporting = false; + } else if (munger.getKind() == ResolvedTypeMunger.PrivilegedAccess) { + changed = mungePrivilegedAccess(weaver, (PrivilegedAccessMunger) munger); + worthReporting = false; + } else if (munger.getKind() == ResolvedTypeMunger.Constructor) { + changed = mungeNewConstructor(weaver, (NewConstructorTypeMunger) munger); + } else if (munger.getKind() == ResolvedTypeMunger.Parent) { + changed = mungeNewParent(weaver, (NewParentTypeMunger) munger); + } else if (munger.getKind() == ResolvedTypeMunger.AnnotationOnType) { + changed = mungeNewAnnotationOnType(weaver, (AnnotationOnTypeMunger) munger); + worthReporting = false; + } else { + throw new RuntimeException("unimplemented"); + } + + if (changed && munger.changesPublicSignature()) { + WeaverStateInfo info = weaver.getLazyClassGen().getOrCreateWeaverStateInfo(weaver.getReweavableMode()); + info.addConcreteMunger(this); + } + + if (changed && worthReporting) { + ResolvedType declaringAspect = null; + AsmManager model = ((BcelWorld) getWorld()).getModelAsAsmManager(); + if (model != null) { + if (munger instanceof NewParentTypeMunger) { + NewParentTypeMunger nptMunger = (NewParentTypeMunger) munger; + declaringAspect = nptMunger.getDeclaringType(); + if (declaringAspect.isParameterizedOrGenericType()) { + declaringAspect = declaringAspect.getRawType(); + } + ResolvedType thisAspect = getAspectType(); + AsmRelationshipProvider.addRelationship(model, weaver.getLazyClassGen().getType(), munger, thisAspect); + + // Add a relationship on the actual declaring aspect too + if (!thisAspect.equals(declaringAspect)) { + // Might be the case the declaring aspect is generic and thisAspect is parameterizing it. In that case + // record the actual parameterizations + + ResolvedType target = weaver.getLazyClassGen().getType(); + ResolvedType newParent = nptMunger.getNewParent(); + IProgramElement thisAspectNode = model.getHierarchy().findElementForType(thisAspect.getPackageName(), + thisAspect.getClassName()); + Map> declareParentsMap = thisAspectNode.getDeclareParentsMap(); + if (declareParentsMap == null) { + declareParentsMap = new HashMap>(); + thisAspectNode.setDeclareParentsMap(declareParentsMap); + } + String tname = target.getName(); + String pname = newParent.getName(); + List newparents = declareParentsMap.get(tname); + if (newparents == null) { + newparents = new ArrayList(); + declareParentsMap.put(tname, newparents); + } + newparents.add(pname); + AsmRelationshipProvider.addRelationship(model, weaver.getLazyClassGen().getType(), munger, declaringAspect); + } + } else { + declaringAspect = getAspectType(); + AsmRelationshipProvider.addRelationship(model, weaver.getLazyClassGen().getType(), munger, declaringAspect); + } + } + } + + // TAG: WeavingMessage + if (changed && worthReporting && munger != null && !weaver.getWorld().getMessageHandler().isIgnoring(IMessage.WEAVEINFO)) { + String tName = weaver.getLazyClassGen().getType().getSourceLocation().getSourceFile().getName(); + if (tName.indexOf("no debug info available") != -1) { + tName = "no debug info available"; + } else { + tName = getShortname(weaver.getLazyClassGen().getType().getSourceLocation().getSourceFile().getPath()); + } + String fName = getShortname(getAspectType().getSourceLocation().getSourceFile().getPath()); + if (munger.getKind().equals(ResolvedTypeMunger.Parent)) { + // This message could come out of AjLookupEnvironment.addParent + // if doing parents munging at compile time only... + NewParentTypeMunger parentTM = (NewParentTypeMunger) munger; + if (parentTM.isMixin()) { + weaver.getWorld() + .getMessageHandler() + .handleMessage( + WeaveMessage.constructWeavingMessage(WeaveMessage.WEAVEMESSAGE_MIXIN, new String[] { + parentTM.getNewParent().getName(), fName, weaver.getLazyClassGen().getType().getName(), + tName }, weaver.getLazyClassGen().getClassName(), getAspectType().getName())); + } else { + if (parentTM.getNewParent().isInterface()) { + weaver.getWorld() + .getMessageHandler() + .handleMessage( + WeaveMessage.constructWeavingMessage(WeaveMessage.WEAVEMESSAGE_DECLAREPARENTSIMPLEMENTS, + new String[] { weaver.getLazyClassGen().getType().getName(), tName, + parentTM.getNewParent().getName(), fName }, weaver.getLazyClassGen() + .getClassName(), getAspectType().getName())); + } else { + weaver.getWorld() + .getMessageHandler() + .handleMessage( + WeaveMessage.constructWeavingMessage(WeaveMessage.WEAVEMESSAGE_DECLAREPARENTSEXTENDS, + new String[] { weaver.getLazyClassGen().getType().getName(), tName, + parentTM.getNewParent().getName(), fName })); + // TAG: WeavingMessage DECLARE PARENTS: EXTENDS + // reportDeclareParentsMessage(WeaveMessage. + // WEAVEMESSAGE_DECLAREPARENTSEXTENDS,sourceType,parent); + + } + } + } else if (munger.getKind().equals(ResolvedTypeMunger.FieldHost)) { + // hidden + } else { + ResolvedMember declaredSig = munger.getSignature(); + String fromString = fName + ":'" + declaredSig + "'"; + // if (declaredSig==null) declaredSig= munger.getSignature(); + String kindString = munger.getKind().toString().toLowerCase(); + if (kindString.equals("innerclass")) { + kindString = "member class"; + fromString = fName; + } + weaver.getWorld() + .getMessageHandler() + .handleMessage( + WeaveMessage.constructWeavingMessage(WeaveMessage.WEAVEMESSAGE_ITD, new String[] { + weaver.getLazyClassGen().getType().getName(), tName, kindString, getAspectType().getName(), + fromString }, weaver.getLazyClassGen().getClassName(), getAspectType().getName())); + } + } + + CompilationAndWeavingContext.leavingPhase(tok); + return changed; + } + + private String getShortname(String path) { + int takefrom = path.lastIndexOf('/'); + if (takefrom == -1) { + takefrom = path.lastIndexOf('\\'); + } + return path.substring(takefrom + 1); + } + + private boolean mungeNewAnnotationOnType(BcelClassWeaver weaver, AnnotationOnTypeMunger munger) { + // FIXME asc this has already been done up front, need to do it here too? + try { + BcelAnnotation anno = (BcelAnnotation) munger.getNewAnnotation(); + weaver.getLazyClassGen().addAnnotation(anno.getBcelAnnotation()); + } catch (ClassCastException cce) { + throw new IllegalStateException("DiagnosticsFor318237: The typemunger "+munger+" contains an annotation of type "+ + munger.getNewAnnotation().getClass().getName()+" when it should be a BcelAnnotation",cce); + } + return true; + } + + /** + * For a long time, AspectJ did not allow binary weaving of declare parents. This restriction is now lifted but could do with + * more testing! + */ + private boolean mungeNewParent(BcelClassWeaver weaver, NewParentTypeMunger typeTransformer) { + LazyClassGen newParentTarget = weaver.getLazyClassGen(); + ResolvedType newParent = typeTransformer.getNewParent(); + + boolean performChange = true; + performChange = enforceDecpRule1_abstractMethodsImplemented(weaver, typeTransformer.getSourceLocation(), newParentTarget, + newParent); + performChange = enforceDecpRule2_cantExtendFinalClass(weaver, typeTransformer.getSourceLocation(), newParentTarget, + newParent) && performChange; + + List methods = newParent.getMethodsWithoutIterator(false, true, false); + for (ResolvedMember method : methods) { + if (!method.getName().equals("")) { + LazyMethodGen subMethod = findMatchingMethod(newParentTarget, method); + // FIXME asc is this safe for all bridge methods? + if (subMethod != null && !subMethod.isBridgeMethod()) { + if (!(subMethod.isSynthetic() && method.isSynthetic())) { + if (!(subMethod.isStatic() && subMethod.getName().startsWith("access$"))) { + // ignore generated accessors + performChange = enforceDecpRule3_visibilityChanges(weaver, newParent, method, subMethod) + && performChange; + performChange = enforceDecpRule4_compatibleReturnTypes(weaver, method, subMethod) && performChange; + performChange = enforceDecpRule5_cantChangeFromStaticToNonstatic(weaver, + typeTransformer.getSourceLocation(), method, subMethod) + && performChange; + } + } + } + } + } + if (!performChange) { + // A rule was violated and an error message already reported + return false; + } + + if (newParent.isClass()) { + // Changing the supertype + if (!attemptToModifySuperCalls(weaver, newParentTarget, newParent)) { + return false; + } + newParentTarget.setSuperClass(newParent); + } else { + // Add a new interface + newParentTarget.addInterface(newParent, getSourceLocation()); + } + return true; + } + + /** + * Rule 1: For the declare parents to be allowed, the target type must override and implement inherited abstract methods (if the + * type is not declared abstract) + */ + private boolean enforceDecpRule1_abstractMethodsImplemented(BcelClassWeaver weaver, ISourceLocation mungerLoc, + LazyClassGen newParentTarget, ResolvedType newParent) { + // Ignore abstract classes or interfaces + if (newParentTarget.isAbstract() || newParentTarget.isInterface()) { + return true; + } + boolean ruleCheckingSucceeded = true; + List newParentMethods = newParent.getMethodsWithoutIterator(false, true, false); + for (ResolvedMember newParentMethod : newParentMethods) { + String newParentMethodName = newParentMethod.getName(); + // Ignore abstract ajc$interField prefixed methods + if (newParentMethod.isAbstract() && !newParentMethodName.startsWith("ajc$interField")) { + ResolvedMember discoveredImpl = null; + List targetMethods = newParentTarget.getType().getMethodsWithoutIterator(false, true, false); + for (ResolvedMember targetMethod : targetMethods) { + if (!targetMethod.isAbstract() && targetMethod.getName().equals(newParentMethodName)) { + String newParentMethodSig = newParentMethod.getParameterSignature(); // ([TT;) + String targetMethodSignature = targetMethod.getParameterSignature(); // ([Ljava/lang/Object;) + // could be a match + if (targetMethodSignature.equals(newParentMethodSig)) { + discoveredImpl = targetMethod; + } else { + // Does the erasure match? In which case a bridge method will be created later to + // satisfy the abstract method + if (targetMethod.hasBackingGenericMember() + && targetMethod.getBackingGenericMember().getParameterSignature().equals(newParentMethodSig)) { + discoveredImpl = targetMethod; + } else if (newParentMethod.hasBackingGenericMember()) { + if (newParentMethod.getBackingGenericMember().getParameterSignature().equals(targetMethodSignature)) { // newParentMethod.getBackingGenericMember().getParameterSignature gives: (Pjava/util/List;) targetMethodSignature= (Ljava/util/List;) + discoveredImpl = targetMethod; + } else if (targetMethod instanceof BcelMethod) { + // BcelMethod does not have backing generic member set (need to investigate why). For now, special case here: + UnresolvedType[] targetMethodGenericParameterTypes = targetMethod.getGenericParameterTypes(); + if (targetMethodGenericParameterTypes !=null) { + StringBuilder b = new StringBuilder("("); + for (UnresolvedType p: targetMethodGenericParameterTypes) { + b.append(p.getSignature()); + } + b.append(')'); + if (b.toString().equals(newParentMethodSig)) { + discoveredImpl = targetMethod; + } + } + } + } + } + if (discoveredImpl != null) { + break; + } + } + } + if (discoveredImpl == null) { + // didnt find a valid implementation, lets check the + // ITDs on this type to see if they satisfy it + boolean satisfiedByITD = false; + for (ConcreteTypeMunger m : newParentTarget.getType().getInterTypeMungersIncludingSupers()) { + if (m.getMunger() != null && m.getMunger().getKind() == ResolvedTypeMunger.Method) { + ResolvedMember sig = m.getSignature(); + if (!Modifier.isAbstract(sig.getModifiers())) { + // If the ITD shares a type variable with some target type, we need to tailor it + // for that type + if (m.isTargetTypeParameterized()) { + ResolvedType genericOnType = getWorld().resolve(sig.getDeclaringType()).getGenericType(); + ResolvedType actualOccurrence = newParent.discoverActualOccurrenceOfTypeInHierarchy(genericOnType); + if (actualOccurrence == null) { + // Handle the case where the ITD is onto the type targeted by the declare parents (PR478003) + actualOccurrence = newParentTarget.getType().discoverActualOccurrenceOfTypeInHierarchy(genericOnType); + } + m = m.parameterizedFor(actualOccurrence); + // possible sig change when type parameters filled in + sig = m.getSignature(); + } + if (ResolvedType.matches( + AjcMemberMaker.interMethod(sig, m.getAspectType(), + sig.getDeclaringType().resolve(weaver.getWorld()).isInterface()), newParentMethod)) { + satisfiedByITD = true; + } + } + } else if (m.getMunger() != null && m.getMunger().getKind() == ResolvedTypeMunger.MethodDelegate2) { + // AV - that should be enough, no need to check more + satisfiedByITD = true; + } + } + if (!satisfiedByITD) { + error(weaver, + "The type " + newParentTarget.getName() + " must implement the inherited abstract method " + + newParentMethod.getDeclaringType() + "." + newParentMethodName + + newParentMethod.getParameterSignature(), newParentTarget.getType().getSourceLocation(), + new ISourceLocation[] { newParentMethod.getSourceLocation(), mungerLoc }); + ruleCheckingSucceeded = false; + } + } + } + } + return ruleCheckingSucceeded; + } + + /** + * Rule 2. Can't extend final types + */ + private boolean enforceDecpRule2_cantExtendFinalClass(BcelClassWeaver weaver, ISourceLocation transformerLoc, + LazyClassGen targetType, ResolvedType newParent) { + if (newParent.isFinal()) { + error(weaver, "Cannot make type " + targetType.getName() + " extend final class " + newParent.getName(), targetType + .getType().getSourceLocation(), new ISourceLocation[] { transformerLoc }); + return false; + } + return true; + } + + /** + * Rule 3. Can't narrow visibility of methods when overriding + */ + private boolean enforceDecpRule3_visibilityChanges(BcelClassWeaver weaver, ResolvedType newParent, ResolvedMember superMethod, + LazyMethodGen subMethod) { + boolean cont = true; + if (Modifier.isPublic(superMethod.getModifiers())) { + if (subMethod.isProtected() || subMethod.isDefault() || subMethod.isPrivate()) { + weaver.getWorld() + .getMessageHandler() + .handleMessage( + MessageUtil.error("Cannot reduce the visibility of the inherited method '" + superMethod + + "' from " + newParent.getName(), superMethod.getSourceLocation())); + cont = false; + } + } else if (Modifier.isProtected(superMethod.getModifiers())) { + if (subMethod.isDefault() || subMethod.isPrivate()) { + weaver.getWorld() + .getMessageHandler() + .handleMessage( + MessageUtil.error("Cannot reduce the visibility of the inherited method '" + superMethod + + "' from " + newParent.getName(), superMethod.getSourceLocation())); + cont = false; + } + } else if (superMethod.isDefault()) { + if (subMethod.isPrivate()) { + weaver.getWorld() + .getMessageHandler() + .handleMessage( + MessageUtil.error("Cannot reduce the visibility of the inherited method '" + superMethod + + "' from " + newParent.getName(), superMethod.getSourceLocation())); + cont = false; + } + } + return cont; + } + + /** + * Rule 4. Can't have incompatible return types + */ + private boolean enforceDecpRule4_compatibleReturnTypes(BcelClassWeaver weaver, ResolvedMember superMethod, + LazyMethodGen subMethod) { + boolean cont = true; + String superReturnTypeSig = superMethod.getGenericReturnType().getSignature(); // eg. Pjava/util/Collection + String subReturnTypeSig = subMethod.getGenericReturnTypeSignature(); + superReturnTypeSig = superReturnTypeSig.replace('.', '/'); + subReturnTypeSig = subReturnTypeSig.replace('.', '/'); + if (!superReturnTypeSig.equals(subReturnTypeSig)) { + // Check for covariance + ResolvedType subType = weaver.getWorld().resolve(subMethod.getReturnType()); + ResolvedType superType = weaver.getWorld().resolve(superMethod.getReturnType()); + if (!superType.isAssignableFrom(subType)) { + weaver.getWorld() + .getMessageHandler() + .handleMessage( + MessageUtil.error("The return type is incompatible with " + superMethod.getDeclaringType() + "." + + superMethod.getName() + superMethod.getParameterSignature(), + subMethod.getSourceLocation())); + // this just might be a better error message... + // "The return type '"+subReturnTypeSig+ + // "' is incompatible with the overridden method " + // +superMethod.getDeclaringType()+"."+ + // superMethod.getName()+superMethod.getParameterSignature()+ + // " which returns '"+superReturnTypeSig+"'", + cont = false; + } + } + return cont; + } + + /** + * Rule5. Method overrides can't change the staticality (word?) - you can't override and make an instance method static or + * override and make a static method an instance method. + */ + private boolean enforceDecpRule5_cantChangeFromStaticToNonstatic(BcelClassWeaver weaver, ISourceLocation mungerLoc, + ResolvedMember superMethod, LazyMethodGen subMethod) { + boolean superMethodStatic = Modifier.isStatic(superMethod.getModifiers()); + if (superMethodStatic && !subMethod.isStatic()) { + error(weaver, "This instance method " + subMethod.getName() + subMethod.getParameterSignature() + + " cannot override the static method from " + superMethod.getDeclaringType().getName(), + subMethod.getSourceLocation(), new ISourceLocation[] { mungerLoc }); + return false; + } else if (!superMethodStatic && subMethod.isStatic()) { + error(weaver, "The static method " + subMethod.getName() + subMethod.getParameterSignature() + + " cannot hide the instance method from " + superMethod.getDeclaringType().getName(), + subMethod.getSourceLocation(), new ISourceLocation[] { mungerLoc }); + return false; + } + return true; + } + + public void error(BcelClassWeaver weaver, String text, ISourceLocation primaryLoc, ISourceLocation[] extraLocs) { + IMessage msg = new Message(text, primaryLoc, true, extraLocs); + weaver.getWorld().getMessageHandler().handleMessage(msg); + } + + /** + * Search the specified type for a particular method - do not use the return value in the comparison as it is not considered for + * overriding. + */ + private LazyMethodGen findMatchingMethod(LazyClassGen type, ResolvedMember searchMethod) { + String searchName = searchMethod.getName(); + String searchSig = searchMethod.getParameterSignature(); + for (LazyMethodGen method : type.getMethodGens()) { + if (method.getName().equals(searchName) && method.getParameterSignature().equals(searchSig)) { + return method; + } + } + return null; + } + + /** + * The main part of implementing declare parents extends. Modify super ctor calls to target the new type. + */ + public boolean attemptToModifySuperCalls(BcelClassWeaver weaver, LazyClassGen newParentTarget, ResolvedType newParent) { + ResolvedType currentParentType = newParentTarget.getSuperClass(); + if (currentParentType.getGenericType() != null) { + currentParentType = currentParentType.getGenericType(); + } + String currentParent = currentParentType.getName(); + if (newParent.getGenericType() != null) { + newParent = newParent.getGenericType(); // target new super calls at + } + // the generic type if its raw or parameterized + List mgs = newParentTarget.getMethodGens(); + + // Look for ctors to modify + for (LazyMethodGen aMethod : mgs) { + if (LazyMethodGen.isConstructor(aMethod)) { + InstructionList insList = aMethod.getBody(); + InstructionHandle handle = insList.getStart(); + while (handle != null) { + if (handle.getInstruction().opcode == Constants.INVOKESPECIAL) { + ConstantPool cpg = newParentTarget.getConstantPool(); + InvokeInstruction invokeSpecial = (InvokeInstruction) handle.getInstruction(); + if (invokeSpecial.getClassName(cpg).equals(currentParent) + && invokeSpecial.getMethodName(cpg).equals("")) { + // System.err.println("Transforming super call '" + invokeSpecial.getSignature(cpg) + "'"); + + // 1. Check there is a ctor in the new parent with + // the same signature + ResolvedMember newCtor = getConstructorWithSignature(newParent, invokeSpecial.getSignature(cpg)); + + if (newCtor == null) { + + // 2. Check ITDCs to see if the necessary ctor is provided that way + boolean satisfiedByITDC = false; + for (Iterator ii = newParentTarget.getType() + .getInterTypeMungersIncludingSupers().iterator(); ii.hasNext() && !satisfiedByITDC;) { + ConcreteTypeMunger m = ii.next(); + if (m.getMunger() instanceof NewConstructorTypeMunger) { + if (m.getSignature().getSignature().equals(invokeSpecial.getSignature(cpg))) { + satisfiedByITDC = true; + } + } + } + + if (!satisfiedByITDC) { + String csig = createReadableCtorSig(newParent, cpg, invokeSpecial); + weaver.getWorld() + .getMessageHandler() + .handleMessage( + MessageUtil.error( + "Unable to modify hierarchy for " + newParentTarget.getClassName() + + " - the constructor " + csig + " is missing", + this.getSourceLocation())); + return false; + } + } + + int idx = cpg.addMethodref(newParent.getName(), invokeSpecial.getMethodName(cpg), + invokeSpecial.getSignature(cpg)); + invokeSpecial.setIndex(idx); + } + } + handle = handle.getNext(); + } + } + } + return true; + } + + /** + * Creates a nice signature for the ctor, something like "(int,Integer,String)" + */ + private String createReadableCtorSig(ResolvedType newParent, ConstantPool cpg, InvokeInstruction invokeSpecial) { + StringBuffer sb = new StringBuffer(); + Type[] ctorArgs = invokeSpecial.getArgumentTypes(cpg); + sb.append(newParent.getClassName()); + sb.append("("); + for (int i = 0; i < ctorArgs.length; i++) { + String argtype = ctorArgs[i].toString(); + if (argtype.lastIndexOf(".") != -1) { + sb.append(argtype.substring(argtype.lastIndexOf(".") + 1)); + } else { + sb.append(argtype); + } + if (i + 1 < ctorArgs.length) { + sb.append(","); + } + } + sb.append(")"); + return sb.toString(); + } + + private ResolvedMember getConstructorWithSignature(ResolvedType type, String searchSig) { + for (ResolvedMember method : type.getDeclaredJavaMethods()) { + if (MemberUtils.isConstructor(method)) { + if (method.getSignature().equals(searchSig)) { + return method; + } + } + } + return null; + } + + private boolean mungePrivilegedAccess(BcelClassWeaver weaver, PrivilegedAccessMunger munger) { + LazyClassGen gen = weaver.getLazyClassGen(); + ResolvedMember member = munger.getMember(); + + ResolvedType onType = weaver.getWorld().resolve(member.getDeclaringType(), munger.getSourceLocation()); + if (onType.isRawType()) { + onType = onType.getGenericType(); + } + + // System.out.println("munging: " + gen + " with " + member); + if (onType.equals(gen.getType())) { + if (member.getKind() == Member.FIELD) { + // System.out.println("matched: " + gen); + addFieldGetter(gen, member, + AjcMemberMaker.privilegedAccessMethodForFieldGet(aspectType, member, munger.shortSyntax)); + addFieldSetter(gen, member, + AjcMemberMaker.privilegedAccessMethodForFieldSet(aspectType, member, munger.shortSyntax)); + return true; + } else if (member.getKind() == Member.METHOD) { + addMethodDispatch(gen, member, AjcMemberMaker.privilegedAccessMethodForMethod(aspectType, member)); + return true; + } else if (member.getKind() == Member.CONSTRUCTOR) { + for (Iterator i = gen.getMethodGens().iterator(); i.hasNext();) { + LazyMethodGen m = i.next(); + if (m.getMemberView() != null && m.getMemberView().getKind() == Member.CONSTRUCTOR) { + // m.getMemberView().equals(member)) { + m.forcePublic(); + // return true; + } + } + return true; + // throw new BCException("no match for " + member + " in " + + // gen); + } else if (member.getKind() == Member.STATIC_INITIALIZATION) { + gen.forcePublic(); + return true; + } else { + throw new RuntimeException("unimplemented"); + } + } + return false; + } + + private void addFieldGetter(LazyClassGen gen, ResolvedMember field, ResolvedMember accessMethod) { + LazyMethodGen mg = makeMethodGen(gen, accessMethod); + InstructionList il = new InstructionList(); + InstructionFactory fact = gen.getFactory(); + if (Modifier.isStatic(field.getModifiers())) { + il.append(fact.createFieldAccess(gen.getClassName(), field.getName(), BcelWorld.makeBcelType(field.getType()), + Constants.GETSTATIC)); + } else { + il.append(InstructionConstants.ALOAD_0); + il.append(fact.createFieldAccess(gen.getClassName(), field.getName(), BcelWorld.makeBcelType(field.getType()), + Constants.GETFIELD)); + } + il.append(InstructionFactory.createReturn(BcelWorld.makeBcelType(field.getType()))); + mg.getBody().insert(il); + + gen.addMethodGen(mg, getSignature().getSourceLocation()); + } + + private void addFieldSetter(LazyClassGen gen, ResolvedMember field, ResolvedMember accessMethod) { + LazyMethodGen mg = makeMethodGen(gen, accessMethod); + InstructionList il = new InstructionList(); + InstructionFactory fact = gen.getFactory(); + Type fieldType = BcelWorld.makeBcelType(field.getType()); + + if (Modifier.isStatic(field.getModifiers())) { + il.append(InstructionFactory.createLoad(fieldType, 0)); + il.append(fact.createFieldAccess(gen.getClassName(), field.getName(), fieldType, Constants.PUTSTATIC)); + } else { + il.append(InstructionConstants.ALOAD_0); + il.append(InstructionFactory.createLoad(fieldType, 1)); + il.append(fact.createFieldAccess(gen.getClassName(), field.getName(), fieldType, Constants.PUTFIELD)); + } + il.append(InstructionFactory.createReturn(Type.VOID)); + mg.getBody().insert(il); + + gen.addMethodGen(mg, getSignature().getSourceLocation()); + } + + private void addMethodDispatch(LazyClassGen gen, ResolvedMember method, ResolvedMember accessMethod) { + LazyMethodGen mg = makeMethodGen(gen, accessMethod); + InstructionList il = new InstructionList(); + InstructionFactory fact = gen.getFactory(); + Type[] paramTypes = BcelWorld.makeBcelTypes(method.getParameterTypes()); + + int pos = 0; + + if (!Modifier.isStatic(method.getModifiers())) { + il.append(InstructionConstants.ALOAD_0); + pos++; + } + for (int i = 0, len = paramTypes.length; i < len; i++) { + Type paramType = paramTypes[i]; + il.append(InstructionFactory.createLoad(paramType, pos)); + pos += paramType.getSize(); + } + il.append(Utility.createInvoke(fact, (BcelWorld) aspectType.getWorld(), method)); + il.append(InstructionFactory.createReturn(BcelWorld.makeBcelType(method.getReturnType()))); + + mg.getBody().insert(il); + + gen.addMethodGen(mg); + } + + protected LazyMethodGen makeMethodGen(LazyClassGen gen, ResolvedMember member) { + try { + Type returnType = BcelWorld.makeBcelType(member.getReturnType()); + Type[] parameterTypes = BcelWorld.makeBcelTypes(member.getParameterTypes()); + LazyMethodGen ret = new LazyMethodGen(member.getModifiers(), returnType, + member.getName(), parameterTypes, UnresolvedType.getNames(member + .getExceptions()), gen); + + // 43972 : Static crosscutting makes interfaces unusable for javac + // ret.makeSynthetic(); + return ret; + } catch (ClassFormatException cfe) { + throw new RuntimeException("Problem with makeMethodGen for method "+member.getName()+" in type "+gen.getName()+" ret="+member.getReturnType(),cfe); + } + } + + protected FieldGen makeFieldGen(LazyClassGen gen, ResolvedMember member) { + return new FieldGen(member.getModifiers(), BcelWorld.makeBcelType(member.getReturnType()), member.getName(), + gen.getConstantPool()); + } + + private boolean mungePerObjectInterface(BcelClassWeaver weaver, PerObjectInterfaceTypeMunger munger) { + // System.err.println("Munging perobject ["+munger+"] onto "+weaver. + // getLazyClassGen().getClassName()); + LazyClassGen gen = weaver.getLazyClassGen(); + + if (couldMatch(gen.getBcelObjectType(), munger.getTestPointcut())) { + FieldGen fg = makeFieldGen(gen, AjcMemberMaker.perObjectField(gen.getType(), aspectType)); + + gen.addField(fg, getSourceLocation()); + + Type fieldType = BcelWorld.makeBcelType(aspectType); + LazyMethodGen mg = new LazyMethodGen(Modifier.PUBLIC, fieldType, NameMangler.perObjectInterfaceGet(aspectType), + new Type[0], new String[0], gen); + InstructionList il = new InstructionList(); + InstructionFactory fact = gen.getFactory(); + il.append(InstructionConstants.ALOAD_0); + il.append(fact.createFieldAccess(gen.getClassName(), fg.getName(), fieldType, Constants.GETFIELD)); + il.append(InstructionFactory.createReturn(fieldType)); + mg.getBody().insert(il); + + gen.addMethodGen(mg); + + LazyMethodGen mg1 = new LazyMethodGen(Modifier.PUBLIC, Type.VOID, NameMangler.perObjectInterfaceSet(aspectType), + + new Type[] { fieldType, }, new String[0], gen); + InstructionList il1 = new InstructionList(); + il1.append(InstructionConstants.ALOAD_0); + il1.append(InstructionFactory.createLoad(fieldType, 1)); + il1.append(fact.createFieldAccess(gen.getClassName(), fg.getName(), fieldType, Constants.PUTFIELD)); + il1.append(InstructionFactory.createReturn(Type.VOID)); + mg1.getBody().insert(il1); + + gen.addMethodGen(mg1); + + gen.addInterface(munger.getInterfaceType().resolve(weaver.getWorld()), getSourceLocation()); + + return true; + } else { + return false; + } + } + + // PTWIMPL Add field to hold aspect instance and an accessor + private boolean mungePerTypeWithinTransformer(BcelClassWeaver weaver) { + LazyClassGen gen = weaver.getLazyClassGen(); + + // if (couldMatch(gen.getBcelObjectType(), munger.getTestPointcut())) { + + // Add (to the target type) the field that will hold the aspect instance + // e.g ajc$com_blah_SecurityAspect$ptwAspectInstance + FieldGen fg = makeFieldGen(gen, AjcMemberMaker.perTypeWithinField(gen.getType(), aspectType)); + gen.addField(fg, getSourceLocation()); + if (!gen.getType().canBeSeenBy(aspectType) && aspectType.isPrivilegedAspect()) { + gen.forcePublic(); + } + // Add an accessor for this new field, the + // ajc$$localAspectOf() method + // e.g. + // "public com_blah_SecurityAspect ajc$com_blah_SecurityAspect$localAspectOf()" + Type fieldType = BcelWorld.makeBcelType(aspectType); + LazyMethodGen mg = new LazyMethodGen(Modifier.PUBLIC | Modifier.STATIC, fieldType, + NameMangler.perTypeWithinLocalAspectOf(aspectType), new Type[0], new String[0], gen); + InstructionList il = new InstructionList(); + // PTWIMPL ?? Should check if it is null and throw + // NoAspectBoundException + InstructionFactory fact = gen.getFactory(); + il.append(fact.createFieldAccess(gen.getClassName(), fg.getName(), fieldType, Constants.GETSTATIC)); + il.append(InstructionFactory.createReturn(fieldType)); + mg.getBody().insert(il); + gen.addMethodGen(mg); + return true; + // } else { + // return false; + // } + } + + // ??? Why do we have this method? I thought by now we would know if it + // matched or not + private boolean couldMatch(BcelObjectType bcelObjectType, Pointcut pointcut) { + return !bcelObjectType.isInterface(); + } + + private boolean mungeNewMemberType(BcelClassWeaver classWeaver, NewMemberClassTypeMunger munger) { + World world = classWeaver.getWorld(); + ResolvedType onType = world.resolve(munger.getTargetType()); + if (onType.isRawType()) { + onType = onType.getGenericType(); + } + return onType.equals(classWeaver.getLazyClassGen().getType()); + } + + private boolean mungeNewMethod(BcelClassWeaver classWeaver, NewMethodTypeMunger munger) { + World world = classWeaver.getWorld(); + + // Resolving it will sort out the tvars + ResolvedMember unMangledInterMethod = munger.getSignature().resolve(world); + + // do matching on the unMangled one, but actually add them to the mangled method + ResolvedMember interMethodBody = munger.getDeclaredInterMethodBody(aspectType, world); + ResolvedMember interMethodDispatcher = munger.getDeclaredInterMethodDispatcher(aspectType, world); + ResolvedMember memberHoldingAnyAnnotations = interMethodDispatcher; + LazyClassGen classGen = classWeaver.getLazyClassGen(); + + ResolvedType onType = world.resolve(unMangledInterMethod.getDeclaringType(), munger.getSourceLocation()); + if (onType.isRawType()) { + onType = onType.getGenericType(); + } + + // Simple checks, can't ITD on annotations or enums + if (onType.isAnnotation()) { + signalError(WeaverMessages.ITDM_ON_ANNOTATION_NOT_ALLOWED, classWeaver, onType); + return false; + } + + if (onType.isEnum()) { + signalError(WeaverMessages.ITDM_ON_ENUM_NOT_ALLOWED, classWeaver, onType); + return false; + } + + boolean mungingInterface = classGen.isInterface(); + boolean onInterface = onType.isInterface(); + + if (onInterface + && classGen.getLazyMethodGen(unMangledInterMethod.getName(), unMangledInterMethod.getSignature(), true) != null) { + // this is ok, we could be providing the default implementation of a + // method + // that the target has already declared + return false; + } + + // If we are processing the intended ITD target type (might be an interface) + if (onType.equals(classGen.getType())) { + ResolvedMember mangledInterMethod = AjcMemberMaker.interMethod(unMangledInterMethod, aspectType, onInterface); + + LazyMethodGen newMethod = makeMethodGen(classGen, mangledInterMethod); + if (mungingInterface) { + // we want the modifiers of the ITD to be used for all *implementors* of the + // interface, but the method itself we add to the interface must be public abstract + newMethod.setAccessFlags(Modifier.PUBLIC | Modifier.ABSTRACT); + } + + // pr98901 + // For copying the annotations across, we have to discover the real + // member in the aspect which is holding them. + if (classWeaver.getWorld().isInJava5Mode()) { + AnnotationAJ annotationsOnRealMember[] = null; + ResolvedType toLookOn = aspectType; + if (aspectType.isRawType()) { + toLookOn = aspectType.getGenericType(); + } + ResolvedMember realMember = getRealMemberForITDFromAspect(toLookOn, memberHoldingAnyAnnotations, false); + // 266602 - consider it missing to mean that the corresponding aspect had errors + if (realMember == null) { + // signalWarning("Unable to apply any annotations attached to " + munger.getSignature(), weaver); + // throw new BCException("Couldn't find ITD init member '" + interMethodBody + "' on aspect " + aspectType); + } else { + annotationsOnRealMember = realMember.getAnnotations(); + } + Set addedAnnotations = new HashSet(); + if (annotationsOnRealMember != null) { + for (AnnotationAJ anno : annotationsOnRealMember) { + AnnotationGen a = ((BcelAnnotation) anno).getBcelAnnotation(); + AnnotationGen ag = new AnnotationGen(a, classGen.getConstantPool(), true); + newMethod.addAnnotation(new BcelAnnotation(ag, classWeaver.getWorld())); + addedAnnotations.add(anno.getType()); + } + } + if (realMember != null) { + copyOverParameterAnnotations(newMethod, realMember); + } + // the code below was originally added to cope with the case where an aspect declares an annotation on an ITD + // declared within itself (an unusual situation). However, it also addresses the case where we may not find the + // annotation on the real representation of the ITD. This can happen in a load-time weaving situation where + // we couldn't add the annotation in time - and so here we recheck the declare annotations. Not quite ideal but + // works. pr288635 + List allDecams = world.getDeclareAnnotationOnMethods(); + for (DeclareAnnotation declareAnnotationMC : allDecams) { + if (declareAnnotationMC.matches(unMangledInterMethod, world)) { + // && newMethod.getEnclosingClass().getType() == aspectType) { + AnnotationAJ annotation = declareAnnotationMC.getAnnotation(); + if (!addedAnnotations.contains(annotation.getType())) { + newMethod.addAnnotation(annotation); + } + } + } + } + + // If it doesn't target an interface and there is a body (i.e. it isnt abstract) + if (!onInterface && !Modifier.isAbstract(mangledInterMethod.getModifiers())) { + InstructionList body = newMethod.getBody(); + InstructionFactory fact = classGen.getFactory(); + int pos = 0; + + if (!Modifier.isStatic(unMangledInterMethod.getModifiers())) { + body.append(InstructionFactory.createThis()); + pos++; + } + Type[] paramTypes = BcelWorld.makeBcelTypes(mangledInterMethod.getParameterTypes()); + for (int i = 0, len = paramTypes.length; i < len; i++) { + Type paramType = paramTypes[i]; + body.append(InstructionFactory.createLoad(paramType, pos)); + pos += paramType.getSize(); + } + body.append(Utility.createInvoke(fact, classWeaver.getWorld(), interMethodBody)); + body.append(InstructionFactory.createReturn(BcelWorld.makeBcelType(mangledInterMethod.getReturnType()))); + + if (classWeaver.getWorld().isInJava5Mode()) { // Don't need bridge + // methods if not in + // 1.5 mode. + createAnyBridgeMethodsForCovariance(classWeaver, munger, unMangledInterMethod, onType, classGen, paramTypes); + } + + } else { + // ??? this is okay + // if (!(mg.getBody() == null)) throw new + // RuntimeException("bas"); + } + + if (world.isInJava5Mode()) { + String basicSignature = mangledInterMethod.getSignature(); + String genericSignature = ((ResolvedMemberImpl) mangledInterMethod).getSignatureForAttribute(); + if (!basicSignature.equals(genericSignature)) { + // Add a signature attribute to it + newMethod.addAttribute(createSignatureAttribute(classGen.getConstantPool(), genericSignature)); + } + } + // XXX make sure to check that we set exceptions properly on this + // guy. + classWeaver.addLazyMethodGen(newMethod); + classWeaver.getLazyClassGen().warnOnAddedMethod(newMethod.getMethod(), getSignature().getSourceLocation()); + + addNeededSuperCallMethods(classWeaver, onType, munger.getSuperMethodsCalled()); + + return true; + + } else if (onInterface && !Modifier.isAbstract(unMangledInterMethod.getModifiers())) { + + // This means the 'gen' should be the top most implementor + // - if it is *not* then something went wrong after we worked + // out that it was the top most implementor (see pr49657) + if (!classGen.getType().isTopmostImplementor(onType)) { + ResolvedType rtx = classGen.getType().getTopmostImplementor(onType); + if (rtx == null) { + // pr302460 + // null means there is something wrong with what we are looking at + ResolvedType rt = classGen.getType(); + if (rt.isInterface()) { + ISourceLocation sloc = munger.getSourceLocation(); + classWeaver + .getWorld() + .getMessageHandler() + .handleMessage( + MessageUtil.error( + "ITD target " + + rt.getName() + + " is an interface but has been incorrectly determined to be the topmost implementor of " + + onType.getName() + ". ITD is " + this.getSignature(), sloc)); + } + if (!onType.isAssignableFrom(rt)) { + ISourceLocation sloc = munger.getSourceLocation(); + classWeaver + .getWorld() + .getMessageHandler() + .handleMessage( + MessageUtil.error( + "ITD target " + rt.getName() + " doesn't appear to implement " + onType.getName() + + " why did we consider it the top most implementor? ITD is " + + this.getSignature(), sloc)); + } + } else if (!rtx.isExposedToWeaver()) { + ISourceLocation sLoc = munger.getSourceLocation(); + classWeaver + .getWorld() + .getMessageHandler() + .handleMessage( + MessageUtil.error(WeaverMessages.format(WeaverMessages.ITD_NON_EXPOSED_IMPLEMENTOR, rtx, + getAspectType().getName()), (sLoc == null ? getAspectType().getSourceLocation() : sLoc))); + } else { + // XXX what does this state mean? + // We have incorrectly identified what is the top most + // implementor and its not because + // a type wasn't exposed to the weaver + } + return false; + } else { + + ResolvedMember mangledInterMethod = AjcMemberMaker.interMethod(unMangledInterMethod, aspectType, false); + + LazyMethodGen mg = makeMethodGen(classGen, mangledInterMethod); + + // From 98901#29 - need to copy annotations across + if (classWeaver.getWorld().isInJava5Mode()) { + AnnotationAJ annotationsOnRealMember[] = null; + ResolvedType toLookOn = aspectType; + if (aspectType.isRawType()) { + toLookOn = aspectType.getGenericType(); + } + ResolvedMember realMember = getRealMemberForITDFromAspect(toLookOn, memberHoldingAnyAnnotations, false); + if (realMember == null) { + throw new BCException("Couldn't find ITD holder member '" + memberHoldingAnyAnnotations + "' on aspect " + + aspectType); + } + annotationsOnRealMember = realMember.getAnnotations(); + + if (annotationsOnRealMember != null) { + for (AnnotationAJ annotationX : annotationsOnRealMember) { + AnnotationGen a = ((BcelAnnotation) annotationX).getBcelAnnotation(); + AnnotationGen ag = new AnnotationGen(a, classWeaver.getLazyClassGen().getConstantPool(), true); + mg.addAnnotation(new BcelAnnotation(ag, classWeaver.getWorld())); + } + } + + copyOverParameterAnnotations(mg, realMember); + } + + if (mungingInterface) { + // we want the modifiers of the ITD to be used for all + // *implementors* of the + // interface, but the method itself we add to the interface + // must be public abstract + mg.setAccessFlags(Modifier.PUBLIC | Modifier.ABSTRACT); + } + + Type[] paramTypes = BcelWorld.makeBcelTypes(mangledInterMethod.getParameterTypes()); + Type returnType = BcelWorld.makeBcelType(mangledInterMethod.getReturnType()); + + InstructionList body = mg.getBody(); + InstructionFactory fact = classGen.getFactory(); + int pos = 0; + + if (!Modifier.isStatic(mangledInterMethod.getModifiers())) { + body.append(InstructionFactory.createThis()); + pos++; + } + for (int i = 0, len = paramTypes.length; i < len; i++) { + Type paramType = paramTypes[i]; + body.append(InstructionFactory.createLoad(paramType, pos)); + pos += paramType.getSize(); + } + + body.append(Utility.createInvoke(fact, classWeaver.getWorld(), interMethodBody)); + Type t = BcelWorld.makeBcelType(interMethodBody.getReturnType()); + if (!t.equals(returnType)) { + body.append(fact.createCast(t, returnType)); + } + body.append(InstructionFactory.createReturn(returnType)); + mg.definingType = onType; + + if (world.isInJava5Mode()) { + String basicSignature = mangledInterMethod.getSignature(); + String genericSignature = ((ResolvedMemberImpl) mangledInterMethod).getSignatureForAttribute(); + if (!basicSignature.equals(genericSignature)) { + // Add a signature attribute to it + mg.addAttribute(createSignatureAttribute(classGen.getConstantPool(), genericSignature)); + } + } + + classWeaver.addOrReplaceLazyMethodGen(mg); + + addNeededSuperCallMethods(classWeaver, onType, munger.getSuperMethodsCalled()); + + // Work out if we need a bridge method for the new method added to the topmostimplementor. + + // Check if the munger being processed is a parameterized form of the original munger + createBridgeIfNecessary(classWeaver, munger, unMangledInterMethod, classGen); + + return true; + } + } else { + return false; + } + } + + private void createBridgeIfNecessary(BcelClassWeaver classWeaver, NewMethodTypeMunger munger, + ResolvedMember unMangledInterMethod, LazyClassGen classGen) { + if (munger.getDeclaredSignature() != null) { + boolean needsbridging = false; + ResolvedMember mungerSignature = munger.getSignature(); + ResolvedMember toBridgeTo = munger.getDeclaredSignature().parameterizedWith(null, + mungerSignature.getDeclaringType().resolve(getWorld()), false, munger.getTypeVariableAliases()); + if (!toBridgeTo.getReturnType().getErasureSignature().equals(mungerSignature.getReturnType().getErasureSignature())) { + needsbridging = true; + } + UnresolvedType[] originalParams = toBridgeTo.getParameterTypes(); + UnresolvedType[] newParams = mungerSignature.getParameterTypes(); + for (int ii = 0; ii < originalParams.length; ii++) { + if (!originalParams[ii].getErasureSignature().equals(newParams[ii].getErasureSignature())) { + needsbridging = true; + } + } + if (needsbridging) { + createBridge(classWeaver, unMangledInterMethod, classGen, toBridgeTo); + } + } + } + + private void copyOverParameterAnnotations(LazyMethodGen receiverMethod, ResolvedMember donorMethod) { + AnnotationAJ[][] pAnnos = donorMethod.getParameterAnnotations(); + if (pAnnos != null) { + int offset = receiverMethod.isStatic() ? 0 : 1; + int param = 0; + for (int i = offset; i < pAnnos.length; i++) { + AnnotationAJ[] annosOnParam = pAnnos[i]; + if (annosOnParam != null) { + for (AnnotationAJ anno : annosOnParam) { + receiverMethod.addParameterAnnotation(param, anno); + } + } + param++; + } + } + } + + private void createBridge(BcelClassWeaver weaver, ResolvedMember unMangledInterMethod, LazyClassGen classGen, + ResolvedMember toBridgeTo) { + Type[] paramTypes; + Type returnType; + InstructionList body; + InstructionFactory fact; + int pos; + ResolvedMember bridgerMethod = AjcMemberMaker.bridgerToInterMethod(unMangledInterMethod, classGen.getType()); + ResolvedMember bridgingSetter = AjcMemberMaker.interMethodBridger(toBridgeTo, aspectType, false); // pr250493 + + LazyMethodGen bridgeMethod = makeMethodGen(classGen, bridgingSetter); + paramTypes = BcelWorld.makeBcelTypes(bridgingSetter.getParameterTypes()); + Type[] bridgingToParms = BcelWorld.makeBcelTypes(unMangledInterMethod.getParameterTypes()); + returnType = BcelWorld.makeBcelType(bridgingSetter.getReturnType()); + body = bridgeMethod.getBody(); + fact = classGen.getFactory(); + pos = 0; + if (!Modifier.isStatic(bridgingSetter.getModifiers())) { + body.append(InstructionFactory.createThis()); + pos++; + } + for (int i = 0, len = paramTypes.length; i < len; i++) { + Type paramType = paramTypes[i]; + body.append(InstructionFactory.createLoad(paramType, pos)); + if (!bridgingSetter.getParameterTypes()[i].getErasureSignature().equals( + unMangledInterMethod.getParameterTypes()[i].getErasureSignature())) { + // System.err.println("Putting in cast from "+ + // paramType+" to "+bridgingToParms[i]); + body.append(fact.createCast(paramType, bridgingToParms[i])); + } + pos += paramType.getSize(); + } + + body.append(Utility.createInvoke(fact, weaver.getWorld(), bridgerMethod)); + body.append(InstructionFactory.createReturn(returnType)); + classGen.addMethodGen(bridgeMethod); + // mg.definingType = onType; + } + + /** + * Helper method to create a signature attribute based on a string signature: e.g. "Ljava/lang/Object;LI;" + */ + private Signature createSignatureAttribute(ConstantPool cp, String signature) { + int nameIndex = cp.addUtf8("Signature"); + int sigIndex = cp.addUtf8(signature); + return new Signature(nameIndex, 2, sigIndex, cp); + } + + /** + * Create any bridge method required because of covariant returns being used. This method is used in the case where an ITD is + * applied to some type and it may be in an override relationship with a method from the supertype - but due to covariance there + * is a mismatch in return values. Example of when required: Super defines: Object m(String s) Sub defines: String m(String s) + * then we need a bridge method in Sub called 'Object m(String s)' that forwards to 'String m(String s)' + */ + private void createAnyBridgeMethodsForCovariance(BcelClassWeaver weaver, NewMethodTypeMunger munger, + ResolvedMember unMangledInterMethod, ResolvedType onType, LazyClassGen gen, Type[] paramTypes) { + // PERFORMANCE BOTTLENECK? Might need investigating, method analysis + // between types in a hierarchy just seems expensive... + // COVARIANCE BRIDGING + // Algorithm: Step1. Check in this type - has someone already created + // the bridge method? + // Step2. Look above us - do we 'override' a method and yet differ in + // return type (i.e. covariance) + // Step3. Create a forwarding bridge method + // ResolvedType superclass = onType.getSuperclass(); + boolean quitRightNow = false; + + String localMethodName = unMangledInterMethod.getName(); + String erasedSig = unMangledInterMethod.getSignatureErased(); // will be something like (LSuperB;)LFoo; + String localParameterSig = erasedSig.substring(0,erasedSig.lastIndexOf(')')+1);//unMangledInterMethod.getParameterSignature(); + // getParameterSignatureErased() does not include parens, which we do need. + String localReturnTypeESig = unMangledInterMethod.getReturnType().getErasureSignature(); + + // Step1 + boolean alreadyDone = false; // Compiler might have done it + ResolvedMember[] localMethods = onType.getDeclaredMethods(); + for (int i = 0; i < localMethods.length; i++) { + ResolvedMember member = localMethods[i]; + if (member.getName().equals(localMethodName)) { + // Check the params + if (member.getParameterSignature().equals(localParameterSig)) { + alreadyDone = true; + } + } + } + + // Step2 + if (!alreadyDone) { + // Use the iterator form of 'getMethods()' so we do as little work as necessary + ResolvedType supertype = onType.getSuperclass(); + if (supertype != null) { + for (Iterator iter = supertype.getMethods(true, true); iter.hasNext() && !quitRightNow;) { + ResolvedMember aMethod = iter.next(); + if (aMethod.getName().equals(localMethodName) && aMethod.getParameterSignature().equals(localParameterSig)) { + // check the return types, if they are different we need a + // bridging method. + if (!aMethod.getReturnType().getErasureSignature().equals(localReturnTypeESig) + && !Modifier.isPrivate(aMethod.getModifiers())) { + // Step3 + createBridgeMethod(weaver.getWorld(), munger, unMangledInterMethod, gen, paramTypes, aMethod); + quitRightNow = true; + } + } + } + } + } + } + + /** + * Create a bridge method for a particular munger. + * + * @param world + * @param munger + * @param unMangledInterMethod the method to bridge 'to' that we have already created in the 'subtype' + * @param clazz the class in which to put the bridge method + * @param paramTypes Parameter types for the bridge method, passed in as an optimization since the caller is likely to have + * already created them. + * @param theBridgeMethod + */ + private void createBridgeMethod(BcelWorld world, NewMethodTypeMunger munger, ResolvedMember unMangledInterMethod, + LazyClassGen clazz, Type[] paramTypes, ResolvedMember theBridgeMethod) { + InstructionList body; + InstructionFactory fact; + int pos = 0; + + // The bridge method in this type will have the same signature as the one in the supertype + LazyMethodGen bridgeMethod = makeMethodGen(clazz, theBridgeMethod); + bridgeMethod.setAccessFlags(bridgeMethod.getAccessFlags() | 0x00000040 /* BRIDGE = 0x00000040 */); + // UnresolvedType[] newParams = munger.getSignature().getParameterTypes(); + Type returnType = BcelWorld.makeBcelType(theBridgeMethod.getReturnType()); + body = bridgeMethod.getBody(); + fact = clazz.getFactory(); + + if (!Modifier.isStatic(unMangledInterMethod.getModifiers())) { + body.append(InstructionFactory.createThis()); + pos++; + } + for (int i = 0, len = paramTypes.length; i < len; i++) { + Type paramType = paramTypes[i]; + body.append(InstructionFactory.createLoad(paramType, pos)); + // if (!bridgingSetter.getParameterTypes()[i].getErasureSignature(). + // equals + // (unMangledInterMethod.getParameterTypes()[i].getErasureSignature + // ())) { + // System.err.println("Putting in cast from "+paramType+" to "+ + // bridgingToParms[i]); + // body.append(fact.createCast(paramType,bridgingToParms[i])); + // } + pos += paramType.getSize(); + } + + body.append(Utility.createInvoke(fact, world, unMangledInterMethod)); + body.append(InstructionFactory.createReturn(returnType)); + clazz.addMethodGen(bridgeMethod); + } + + // Unlike toString() on a member, this does not include the declaring type + private String stringifyMember(ResolvedMember member) { + StringBuffer buf = new StringBuffer(); + buf.append(member.getReturnType().getName()); + buf.append(' '); + buf.append(member.getName()); + if (member.getKind() != Member.FIELD) { + buf.append("("); + UnresolvedType[] params = member.getParameterTypes(); + if (params.length != 0) { + buf.append(params[0]); + for (int i = 1, len = params.length; i < len; i++) { + buf.append(", "); + buf.append(params[i].getName()); + } + } + buf.append(")"); + } + return buf.toString(); + } + + private boolean mungeMethodDelegate(BcelClassWeaver weaver, MethodDelegateTypeMunger munger) { + World world = weaver.getWorld(); + + LazyClassGen gen = weaver.getLazyClassGen(); + if (gen.getType().isAnnotation() || gen.getType().isEnum()) { + // don't signal error as it could be a consequence of a wild type pattern + return false; + } + + ResolvedMember introduced = munger.getSignature(); + + ResolvedType fromType = world.resolve(introduced.getDeclaringType(), munger.getSourceLocation()); + if (fromType.isRawType()) { + fromType = fromType.getGenericType(); + } + + boolean shouldApply = munger.matches(weaver.getLazyClassGen().getType(), aspectType); + + if (shouldApply) { + Type bcelReturnType = BcelWorld.makeBcelType(introduced.getReturnType()); + + // If no implementation class was specified, the intention was that + // the types matching the pattern + // already implemented the interface, let's check that now! + if (munger.getImplClassName() == null && !munger.specifiesDelegateFactoryMethod()) { + boolean isOK = false; + List existingMethods = gen.getMethodGens(); + for (LazyMethodGen m : existingMethods) { + if (m.getName().equals(introduced.getName()) + && m.getParameterSignature().equals(introduced.getParameterSignature()) + && m.getReturnType().equals(bcelReturnType)) { + isOK = true; + } + } + if (!isOK) { + // the class does not implement this method, they needed to + // supply a default impl class + IMessage msg = new Message("@DeclareParents: No defaultImpl was specified but the type '" + gen.getName() + + "' does not implement the method '" + stringifyMember(introduced) + "' defined on the interface '" + + introduced.getDeclaringType() + "'", weaver.getLazyClassGen().getType().getSourceLocation(), true, + new ISourceLocation[] { munger.getSourceLocation() }); + weaver.getWorld().getMessageHandler().handleMessage(msg); + return false; + } + + return true; + } + + LazyMethodGen mg = new LazyMethodGen(introduced.getModifiers() - Modifier.ABSTRACT, bcelReturnType, + introduced.getName(), BcelWorld.makeBcelTypes(introduced.getParameterTypes()), + BcelWorld.makeBcelTypesAsClassNames(introduced.getExceptions()), gen); + + // annotation copy from annotation on ITD interface + if (weaver.getWorld().isInJava5Mode()) { + AnnotationAJ annotationsOnRealMember[] = null; + ResolvedType toLookOn = weaver.getWorld().lookupOrCreateName(introduced.getDeclaringType()); + if (fromType.isRawType()) { + toLookOn = fromType.getGenericType(); + } + // lookup the method + ResolvedMember[] ms = toLookOn.getDeclaredJavaMethods(); + for (ResolvedMember m : ms) { + if (introduced.getName().equals(m.getName()) && introduced.getSignature().equals(m.getSignature())) { + annotationsOnRealMember = m.getAnnotations(); + break; + } + } + if (annotationsOnRealMember != null) { + for (AnnotationAJ anno : annotationsOnRealMember) { + AnnotationGen a = ((BcelAnnotation) anno).getBcelAnnotation(); + AnnotationGen ag = new AnnotationGen(a, weaver.getLazyClassGen().getConstantPool(), true); + mg.addAnnotation(new BcelAnnotation(ag, weaver.getWorld())); + } + } + } + + InstructionList body = new InstructionList(); + InstructionFactory fact = gen.getFactory(); + + // getfield + body.append(InstructionConstants.ALOAD_0); + body.append(Utility.createGet(fact, munger.getDelegate(weaver.getLazyClassGen().getType()))); + InstructionBranch ifNonNull = InstructionFactory.createBranchInstruction(Constants.IFNONNULL, null); + body.append(ifNonNull); + + // Create and store a new instance + body.append(InstructionConstants.ALOAD_0); // 'this' is where we'll store the field value + + // TODO for non-static case, call aspectOf() then call the factory method on the retval + // TODO decide whether the value can really be cached + + // locate the aspect and call the static method in it + if (munger.specifiesDelegateFactoryMethod()) { + ResolvedMember rm = munger.getDelegateFactoryMethod(weaver.getWorld()); + + // Check the method parameter is compatible with the type of the instance to be passed + if (rm.getArity() != 0) { + ResolvedType parameterType = rm.getParameterTypes()[0].resolve(weaver.getWorld()); + if (!parameterType.isAssignableFrom(weaver.getLazyClassGen().getType())) { + signalError("For mixin factory method '" + rm + "': Instance type '" + weaver.getLazyClassGen().getType() + + "' is not compatible with factory parameter type '" + parameterType + "'", weaver); + return false; + } + } + if (Modifier.isStatic(rm.getModifiers())) { + if (rm.getArity() != 0) { + body.append(InstructionConstants.ALOAD_0); + } + body.append(fact.createInvoke(rm.getDeclaringType().getName(), rm.getName(), rm.getSignature(), + Constants.INVOKESTATIC)); + body.append(Utility.createSet(fact, munger.getDelegate(weaver.getLazyClassGen().getType()))); + } else { + // Need to call aspectOf() to obtain the aspect instance then call the factory method upon that + UnresolvedType theAspect = munger.getAspect(); + body.append(fact.createInvoke(theAspect.getName(), "aspectOf", "()" + theAspect.getSignature(), + Constants.INVOKESTATIC)); + if (rm.getArity() != 0) { + body.append(InstructionConstants.ALOAD_0); + } + body.append(fact.createInvoke(rm.getDeclaringType().getName(), rm.getName(), rm.getSignature(), + Constants.INVOKEVIRTUAL)); + body.append(Utility.createSet(fact, munger.getDelegate(weaver.getLazyClassGen().getType()))); + } + } else { + body.append(fact.createNew(munger.getImplClassName())); + body.append(InstructionConstants.DUP); + body.append(fact.createInvoke(munger.getImplClassName(), "", Type.VOID, Type.NO_ARGS, Constants.INVOKESPECIAL)); + body.append(Utility.createSet(fact, munger.getDelegate(weaver.getLazyClassGen().getType()))); + } + + // if not null use the instance we've got + InstructionHandle ifNonNullElse = body.append(InstructionConstants.ALOAD_0); + ifNonNull.setTarget(ifNonNullElse); + body.append(Utility.createGet(fact, munger.getDelegate(weaver.getLazyClassGen().getType()))); + + // args + int pos = 0; + if (!Modifier.isStatic(introduced.getModifiers())) { // skip 'this' (?? can this really + // happen) + // body.append(InstructionFactory.createThis()); + pos++; + } + Type[] paramTypes = BcelWorld.makeBcelTypes(introduced.getParameterTypes()); + for (int i = 0, len = paramTypes.length; i < len; i++) { + Type paramType = paramTypes[i]; + body.append(InstructionFactory.createLoad(paramType, pos)); + pos += paramType.getSize(); + } + body.append(Utility.createInvoke(fact, Constants.INVOKEINTERFACE, introduced)); + body.append(InstructionFactory.createReturn(bcelReturnType)); + + mg.getBody().append(body); + weaver.addLazyMethodGen(mg); + weaver.getLazyClassGen().warnOnAddedMethod(mg.getMethod(), getSignature().getSourceLocation()); + return true; + } + return false; + } + + private boolean mungeFieldHost(BcelClassWeaver weaver, MethodDelegateTypeMunger.FieldHostTypeMunger munger) { + LazyClassGen gen = weaver.getLazyClassGen(); + if (gen.getType().isAnnotation() || gen.getType().isEnum()) { + // don't signal error as it could be a consequence of a wild type + // pattern + return false; + } + // boolean shouldApply = + munger.matches(weaver.getLazyClassGen().getType(), aspectType); // why + // do + // this? + ResolvedMember host = AjcMemberMaker.itdAtDeclareParentsField(weaver.getLazyClassGen().getType(), munger.getSignature() + .getType(), aspectType); + FieldGen field = makeFieldGen(weaver.getLazyClassGen(), host); + field.setModifiers(field.getModifiers() | BcelField.AccSynthetic); + weaver.getLazyClassGen().addField(field, null); + return true; + } + + private ResolvedMember getRealMemberForITDFromAspect(ResolvedType aspectType, ResolvedMember lookingFor, boolean isCtorRelated) { + World world = aspectType.getWorld(); + boolean debug = false; + if (debug) { + System.err.println("Searching for a member on type: " + aspectType); + System.err.println("Member we are looking for: " + lookingFor); + } + + ResolvedMember aspectMethods[] = aspectType.getDeclaredMethods(); + UnresolvedType[] lookingForParams = lookingFor.getParameterTypes(); + + ResolvedMember realMember = null; + for (int i = 0; realMember == null && i < aspectMethods.length; i++) { + ResolvedMember member = aspectMethods[i]; + if (member.getName().equals(lookingFor.getName())) { + UnresolvedType[] memberParams = member.getGenericParameterTypes(); + if (memberParams.length == lookingForParams.length) { + if (debug) { + System.err.println("Reviewing potential candidates: " + member); + } + boolean matchOK = true; + // If not related to a ctor ITD then the name is enough to + // confirm we have the + // right one. If it is ctor related we need to check the + // params all match, although + // only the erasure. + if (isCtorRelated) { + for (int j = 0; j < memberParams.length && matchOK; j++) { + ResolvedType pMember = memberParams[j].resolve(world); + ResolvedType pLookingFor = lookingForParams[j].resolve(world); + + if (pMember.isTypeVariableReference()) { + pMember = ((TypeVariableReference) pMember).getTypeVariable().getFirstBound().resolve(world); + } + if (pMember.isParameterizedType() || pMember.isGenericType()) { + pMember = pMember.getRawType().resolve(aspectType.getWorld()); + } + + if (pLookingFor.isTypeVariableReference()) { + pLookingFor = ((TypeVariableReference) pLookingFor).getTypeVariable().getFirstBound() + .resolve(world); + } + if (pLookingFor.isParameterizedType() || pLookingFor.isGenericType()) { + pLookingFor = pLookingFor.getRawType().resolve(world); + } + + if (debug) { + System.err.println("Comparing parameter " + j + " member=" + pMember + " lookingFor=" + + pLookingFor); + } + if (!pMember.equals(pLookingFor)) { + matchOK = false; + } + } + } + if (matchOK) { + realMember = member; + } + } + } + } + if (debug && realMember == null) { + System.err.println("Didn't find a match"); + } + return realMember; + } + + private void addNeededSuperCallMethods(BcelClassWeaver weaver, ResolvedType onType, Set neededSuperCalls) { + LazyClassGen gen = weaver.getLazyClassGen(); + for (ResolvedMember superMethod: neededSuperCalls) { + if (weaver.addDispatchTarget(superMethod)) { + // System.err.println("super type: " + superMethod.getDeclaringType() + ", " + gen.getType()); + boolean isSuper = !superMethod.getDeclaringType().equals(gen.getType()); + String dispatchName; + if (isSuper) { + dispatchName = NameMangler.superDispatchMethod(onType, superMethod.getName()); + } else { + dispatchName = NameMangler.protectedDispatchMethod(onType, superMethod.getName()); + } + superMethod = superMethod.resolve(weaver.getWorld()); + LazyMethodGen dispatcher = makeDispatcher(gen, dispatchName, superMethod, weaver.getWorld(), isSuper); + weaver.addLazyMethodGen(dispatcher); + } + } + } + + private void signalError(String msgid, BcelClassWeaver weaver, UnresolvedType onType) { + IMessage msg = MessageUtil.error(WeaverMessages.format(msgid, onType.getName()), getSourceLocation()); + weaver.getWorld().getMessageHandler().handleMessage(msg); + } + + // private void signalWarning(String msgString, BcelClassWeaver weaver) { + // IMessage msg = MessageUtil.warn(msgString, getSourceLocation()); + // weaver.getWorld().getMessageHandler().handleMessage(msg); + // } + + private void signalError(String msgString, BcelClassWeaver weaver) { + IMessage msg = MessageUtil.error(msgString, getSourceLocation()); + weaver.getWorld().getMessageHandler().handleMessage(msg); + } + + private boolean mungeNewConstructor(BcelClassWeaver weaver, NewConstructorTypeMunger newConstructorTypeMunger) { + + final LazyClassGen currentClass = weaver.getLazyClassGen(); + final InstructionFactory fact = currentClass.getFactory(); + + ResolvedMember newConstructorMember = newConstructorTypeMunger.getSyntheticConstructor(); + ResolvedType onType = newConstructorMember.getDeclaringType().resolve(weaver.getWorld()); + if (onType.isRawType()) { + onType = onType.getGenericType(); + } + + if (onType.isAnnotation()) { + signalError(WeaverMessages.ITDC_ON_ANNOTATION_NOT_ALLOWED, weaver, onType); + return false; + } + + if (onType.isEnum()) { + signalError(WeaverMessages.ITDC_ON_ENUM_NOT_ALLOWED, weaver, onType); + return false; + } + + if (!onType.equals(currentClass.getType())) { + return false; + } + + ResolvedMember explicitConstructor = newConstructorTypeMunger.getExplicitConstructor(); + // int declaredParameterCount = + // newConstructorTypeMunger.getDeclaredParameterCount(); + LazyMethodGen mg = makeMethodGen(currentClass, newConstructorMember); + mg.setEffectiveSignature(newConstructorTypeMunger.getSignature(), Shadow.ConstructorExecution, true); + + // pr98901 + // For copying the annotations across, we have to discover the real + // member in the aspect + // which is holding them. + if (weaver.getWorld().isInJava5Mode()) { + + ResolvedMember interMethodDispatcher = AjcMemberMaker.postIntroducedConstructor(aspectType, onType, + newConstructorTypeMunger.getSignature().getParameterTypes()); + AnnotationAJ annotationsOnRealMember[] = null; + ResolvedMember realMember = getRealMemberForITDFromAspect(aspectType, interMethodDispatcher, true); + // 266602 - consider it missing to mean that the corresponding aspect had errors + if (realMember == null) { + // signalWarning("Unable to apply any annotations attached to " + munger.getSignature(), weaver); + // throw new BCException("Couldn't find ITD init member '" + interMethodBody + "' on aspect " + aspectType); + } else { + annotationsOnRealMember = realMember.getAnnotations(); + } + if (annotationsOnRealMember != null) { + for (int i = 0; i < annotationsOnRealMember.length; i++) { + AnnotationAJ annotationX = annotationsOnRealMember[i]; + AnnotationGen a = ((BcelAnnotation) annotationX).getBcelAnnotation(); + AnnotationGen ag = new AnnotationGen(a, weaver.getLazyClassGen().getConstantPool(), true); + mg.addAnnotation(new BcelAnnotation(ag, weaver.getWorld())); + } + } + // the below loop fixes the very special (and very stupid) + // case where an aspect declares an annotation + // on an ITD it declared on itself. + List allDecams = weaver.getWorld().getDeclareAnnotationOnMethods(); + for (Iterator i = allDecams.iterator(); i.hasNext();) { + DeclareAnnotation decaMC = i.next(); + if (decaMC.matches(explicitConstructor, weaver.getWorld()) && mg.getEnclosingClass().getType() == aspectType) { + mg.addAnnotation(decaMC.getAnnotation()); + } + } + } + + // Might have to remove the default constructor - b275032 + // TODO could have tagged the type munger when the fact we needed to do this was detected earlier + if (mg.getArgumentTypes().length == 0) { + LazyMethodGen toRemove = null; + for (LazyMethodGen object : currentClass.getMethodGens()) { + if (object.getName().equals("") && object.getArgumentTypes().length == 0) { + toRemove = object; + } + } + if (toRemove != null) { + currentClass.removeMethodGen(toRemove); + } + } + + currentClass.addMethodGen(mg); + // weaver.addLazyMethodGen(freshConstructor); + + InstructionList body = mg.getBody(); + + // add to body: push arts for call to pre, from actual args starting at + // 1 (skipping this), going to + // declared argcount + 1 + UnresolvedType[] declaredParams = newConstructorTypeMunger.getSignature().getParameterTypes(); + Type[] paramTypes = mg.getArgumentTypes(); + int frameIndex = 1; + for (int i = 0, len = declaredParams.length; i < len; i++) { + body.append(InstructionFactory.createLoad(paramTypes[i], frameIndex)); + frameIndex += paramTypes[i].getSize(); + } + // do call to pre + Member preMethod = AjcMemberMaker.preIntroducedConstructor(aspectType, onType, declaredParams); + body.append(Utility.createInvoke(fact, null, preMethod)); + + // create a local, and store return pre stuff into it. + int arraySlot = mg.allocateLocal(1); + body.append(InstructionFactory.createStore(Type.OBJECT, arraySlot)); + + // put this on the stack + body.append(InstructionConstants.ALOAD_0); + + // unpack pre args onto stack + UnresolvedType[] superParamTypes = explicitConstructor.getParameterTypes(); + + for (int i = 0, len = superParamTypes.length; i < len; i++) { + body.append(InstructionFactory.createLoad(Type.OBJECT, arraySlot)); + body.append(Utility.createConstant(fact, i)); + body.append(InstructionFactory.createArrayLoad(Type.OBJECT)); + body.append(Utility.createConversion(fact, Type.OBJECT, BcelWorld.makeBcelType(superParamTypes[i]))); + } + + // call super/this + + body.append(Utility.createInvoke(fact, null, explicitConstructor)); + + // put this back on the stack + + body.append(InstructionConstants.ALOAD_0); + + // unpack params onto stack + Member postMethod = AjcMemberMaker.postIntroducedConstructor(aspectType, onType, declaredParams); + UnresolvedType[] postParamTypes = postMethod.getParameterTypes(); + + for (int i = 1, len = postParamTypes.length; i < len; i++) { + body.append(InstructionFactory.createLoad(Type.OBJECT, arraySlot)); + body.append(Utility.createConstant(fact, superParamTypes.length + i - 1)); + body.append(InstructionFactory.createArrayLoad(Type.OBJECT)); + body.append(Utility.createConversion(fact, Type.OBJECT, BcelWorld.makeBcelType(postParamTypes[i]))); + } + + // call post + body.append(Utility.createInvoke(fact, null, postMethod)); + + // don't forget to return!! + body.append(InstructionConstants.RETURN); + + addNeededSuperCallMethods(weaver, onType, munger.getSuperMethodsCalled()); + + return true; + } + + private static LazyMethodGen makeDispatcher(LazyClassGen onGen, String dispatchName, ResolvedMember superMethod, + BcelWorld world, boolean isSuper) { + Type[] paramTypes = BcelWorld.makeBcelTypes(superMethod.getParameterTypes()); + Type returnType = BcelWorld.makeBcelType(superMethod.getReturnType()); + + int modifiers = Modifier.PUBLIC; + if (onGen.isInterface()) { + modifiers |= Modifier.ABSTRACT; + } + + LazyMethodGen mg = new LazyMethodGen(modifiers, returnType, dispatchName, paramTypes, UnresolvedType.getNames(superMethod + .getExceptions()), onGen); + InstructionList body = mg.getBody(); + + if (onGen.isInterface()) { + return mg; + } + + // assert (!superMethod.isStatic()) + InstructionFactory fact = onGen.getFactory(); + int pos = 0; + + body.append(InstructionFactory.createThis()); + pos++; + for (int i = 0, len = paramTypes.length; i < len; i++) { + Type paramType = paramTypes[i]; + body.append(InstructionFactory.createLoad(paramType, pos)); + pos += paramType.getSize(); + } + if (isSuper) { + body.append(Utility.createSuperInvoke(fact, world, superMethod)); + } else { + body.append(Utility.createInvoke(fact, world, superMethod)); + } + body.append(InstructionFactory.createReturn(returnType)); + + return mg; + } + + private boolean mungeNewField(BcelClassWeaver weaver, NewFieldTypeMunger munger) { + /* ResolvedMember initMethod = */munger.getInitMethod(aspectType); + LazyClassGen gen = weaver.getLazyClassGen(); + ResolvedMember field = munger.getSignature(); + + ResolvedType onType = weaver.getWorld().resolve(field.getDeclaringType(), munger.getSourceLocation()); + if (onType.isRawType()) { + onType = onType.getGenericType(); + } + + boolean onInterface = onType.isInterface(); + + if (onType.isAnnotation()) { + signalError(WeaverMessages.ITDF_ON_ANNOTATION_NOT_ALLOWED, weaver, onType); + return false; + } + + if (onType.isEnum()) { + signalError(WeaverMessages.ITDF_ON_ENUM_NOT_ALLOWED, weaver, onType); + return false; + } + + ResolvedMember interMethodBody = munger.getInitMethod(aspectType); + + AnnotationAJ annotationsOnRealMember[] = null; + // pr98901 + // For copying the annotations across, we have to discover the real + // member in the aspect + // which is holding them. + if (weaver.getWorld().isInJava5Mode()) { + // the below line just gets the method with the same name in + // aspectType.getDeclaredMethods(); + ResolvedType toLookOn = aspectType; + if (aspectType.isRawType()) { + toLookOn = aspectType.getGenericType(); + } + ResolvedMember realMember = getRealMemberForITDFromAspect(toLookOn, interMethodBody, false); + if (realMember == null) { + // signalWarning("Unable to apply any annotations attached to " + munger.getSignature(), weaver); + // throw new BCException("Couldn't find ITD init member '" + interMethodBody + "' on aspect " + aspectType); + } else { + annotationsOnRealMember = realMember.getAnnotations(); + } + } + + if (onType.equals(gen.getType())) { + if (onInterface) { + ResolvedMember itdfieldGetter = AjcMemberMaker.interFieldInterfaceGetter(field, onType, aspectType); + LazyMethodGen mg = makeMethodGen(gen, itdfieldGetter); + gen.addMethodGen(mg); + + LazyMethodGen mg1 = makeMethodGen(gen, AjcMemberMaker.interFieldInterfaceSetter(field, onType, aspectType)); + gen.addMethodGen(mg1); + } else { + weaver.addInitializer(this); + ResolvedMember newField = AjcMemberMaker.interFieldClassField(field, aspectType, + munger.version == NewFieldTypeMunger.VersionTwo); + FieldGen fg = makeFieldGen(gen, newField); + + if (annotationsOnRealMember != null) { + for (int i = 0; i < annotationsOnRealMember.length; i++) { + AnnotationAJ annotationX = annotationsOnRealMember[i]; + AnnotationGen a = ((BcelAnnotation) annotationX).getBcelAnnotation(); + AnnotationGen ag = new AnnotationGen(a, weaver.getLazyClassGen().getConstantPool(), true); + fg.addAnnotation(ag); + } + } + + if (weaver.getWorld().isInJava5Mode()) { + String basicSignature = field.getSignature(); + String genericSignature = field.getReturnType().resolve(weaver.getWorld()).getSignatureForAttribute(); + // String genericSignature = + // ((ResolvedMemberImpl)field).getSignatureForAttribute(); + if (!basicSignature.equals(genericSignature)) { + // Add a signature attribute to it + fg.addAttribute(createSignatureAttribute(gen.getConstantPool(), genericSignature)); + } + } + gen.addField(fg, getSourceLocation()); + + } + return true; + } else if (onInterface && gen.getType().isTopmostImplementor(onType)) { + // we know that we can't be static since we don't allow statics on interfaces + if (Modifier.isStatic(field.getModifiers())) { + throw new RuntimeException("unimplemented"); + } + + boolean alreadyExists = false; + // only need to check for version 2 style mungers + if (munger.version==NewFieldTypeMunger.VersionTwo) { + for (BcelField fieldgen: gen.getFieldGens()) { + if (fieldgen.getName().equals(field.getName())) { + alreadyExists=true; + break; + } + } + } + + // FieldGen fg = makeFieldGen(gen, AjcMemberMaker.interFieldInterfaceField(field, onType, aspectType)); + ResolvedMember newField = AjcMemberMaker.interFieldInterfaceField(field, onType, aspectType, munger.version == NewFieldTypeMunger.VersionTwo); + String fieldName = newField.getName(); + + Type fieldType = BcelWorld.makeBcelType(field.getType()); + if (!alreadyExists) { + weaver.addInitializer(this); + FieldGen fg = makeFieldGen(gen,newField); + if (annotationsOnRealMember != null) { + for (int i = 0; i < annotationsOnRealMember.length; i++) { + AnnotationAJ annotationX = annotationsOnRealMember[i]; + AnnotationGen a = ((BcelAnnotation) annotationX).getBcelAnnotation(); + AnnotationGen ag = new AnnotationGen(a, weaver.getLazyClassGen().getConstantPool(), true); + fg.addAnnotation(ag); + } + } + + if (weaver.getWorld().isInJava5Mode()) { + String basicSignature = field.getSignature(); + String genericSignature = field.getReturnType().resolve(weaver.getWorld()).getSignatureForAttribute(); + // String genericSignature = + // ((ResolvedMemberImpl)field).getSignatureForAttribute(); + if (!basicSignature.equals(genericSignature)) { + // Add a signature attribute to it + fg.addAttribute(createSignatureAttribute(gen.getConstantPool(), genericSignature)); + } + } + gen.addField(fg, getSourceLocation()); + } + // this uses a shadow munger to add init method to constructors + // weaver.getShadowMungers().add(makeInitCallShadowMunger(initMethod) + // ); + + ResolvedMember itdfieldGetter = AjcMemberMaker.interFieldInterfaceGetter(field, gen.getType()/* onType */, aspectType); + LazyMethodGen mg = makeMethodGen(gen, itdfieldGetter); + InstructionList il = new InstructionList(); + InstructionFactory fact = gen.getFactory(); + if (Modifier.isStatic(field.getModifiers())) { + il.append(fact.createFieldAccess(gen.getClassName(), fieldName, fieldType, Constants.GETSTATIC)); + } else { + il.append(InstructionConstants.ALOAD_0); + il.append(fact.createFieldAccess(gen.getClassName(), fieldName, fieldType, Constants.GETFIELD)); + } + il.append(InstructionFactory.createReturn(fieldType)); + mg.getBody().insert(il); + + gen.addMethodGen(mg); + + // Check if we need bridge methods for the field getter and setter + if (munger.getDeclaredSignature() != null) { // is this munger a + // parameterized + // form of some + // original munger? + ResolvedMember toBridgeTo = munger.getDeclaredSignature().parameterizedWith(null, + munger.getSignature().getDeclaringType().resolve(getWorld()), false, munger.getTypeVariableAliases()); + boolean needsbridging = false; + if (!toBridgeTo.getReturnType().getErasureSignature() + .equals(munger.getSignature().getReturnType().getErasureSignature())) { + needsbridging = true; + } + if (needsbridging) { + ResolvedMember bridgingGetter = AjcMemberMaker.interFieldInterfaceGetter(toBridgeTo, gen.getType(), aspectType); + createBridgeMethodForITDF(weaver, gen, itdfieldGetter, bridgingGetter); + } + } + + ResolvedMember itdfieldSetter = AjcMemberMaker.interFieldInterfaceSetter(field, gen.getType(), aspectType); + LazyMethodGen mg1 = makeMethodGen(gen, itdfieldSetter); + InstructionList il1 = new InstructionList(); + if (Modifier.isStatic(field.getModifiers())) { + il1.append(InstructionFactory.createLoad(fieldType, 0)); + il1.append(fact.createFieldAccess(gen.getClassName(), fieldName, fieldType, Constants.PUTSTATIC)); + } else { + il1.append(InstructionConstants.ALOAD_0); + il1.append(InstructionFactory.createLoad(fieldType, 1)); + il1.append(fact.createFieldAccess(gen.getClassName(), fieldName, fieldType, Constants.PUTFIELD)); + } + il1.append(InstructionFactory.createReturn(Type.VOID)); + mg1.getBody().insert(il1); + + gen.addMethodGen(mg1); + + if (munger.getDeclaredSignature() != null) { + ResolvedMember toBridgeTo = munger.getDeclaredSignature().parameterizedWith(null, + munger.getSignature().getDeclaringType().resolve(getWorld()), false, munger.getTypeVariableAliases()); + boolean needsbridging = false; + if (!toBridgeTo.getReturnType().getErasureSignature() + .equals(munger.getSignature().getReturnType().getErasureSignature())) { + needsbridging = true; + } + if (needsbridging) { + ResolvedMember bridgingSetter = AjcMemberMaker.interFieldInterfaceSetter(toBridgeTo, gen.getType(), aspectType); + createBridgeMethodForITDF(weaver, gen, itdfieldSetter, bridgingSetter); + } + } + + return true; + } else { + return false; + } + } + + // FIXME asc combine with other createBridge.. method in this class, avoid + // the duplication... + private void createBridgeMethodForITDF(BcelClassWeaver weaver, LazyClassGen gen, ResolvedMember itdfieldSetter, + ResolvedMember bridgingSetter) { + InstructionFactory fact; + LazyMethodGen bridgeMethod = makeMethodGen(gen, bridgingSetter); + bridgeMethod.setAccessFlags(bridgeMethod.getAccessFlags() | 0x00000040); // BRIDGE = 0x00000040 + Type[] paramTypes = BcelWorld.makeBcelTypes(bridgingSetter.getParameterTypes()); + Type[] bridgingToParms = BcelWorld.makeBcelTypes(itdfieldSetter.getParameterTypes()); + Type returnType = BcelWorld.makeBcelType(bridgingSetter.getReturnType()); + InstructionList body = bridgeMethod.getBody(); + fact = gen.getFactory(); + int pos = 0; + + if (!Modifier.isStatic(bridgingSetter.getModifiers())) { + body.append(InstructionFactory.createThis()); + pos++; + } + for (int i = 0, len = paramTypes.length; i < len; i++) { + Type paramType = paramTypes[i]; + body.append(InstructionFactory.createLoad(paramType, pos)); + if (!bridgingSetter.getParameterTypes()[i].getErasureSignature().equals( + itdfieldSetter.getParameterTypes()[i].getErasureSignature())) { + body.append(fact.createCast(paramType, bridgingToParms[i])); + } + pos += paramType.getSize(); + } + + body.append(Utility.createInvoke(fact, weaver.getWorld(), itdfieldSetter)); + body.append(InstructionFactory.createReturn(returnType)); + gen.addMethodGen(bridgeMethod); + } + + @Override + public ConcreteTypeMunger parameterizedFor(ResolvedType target) { + return new BcelTypeMunger(munger.parameterizedFor(target), aspectType); + } + + @Override + public ConcreteTypeMunger parameterizeWith(Map m, World w) { + return new BcelTypeMunger(munger.parameterizeWith(m, w), aspectType); + } + + /** + * Returns a list of type variable aliases used in this munger. For example, if the ITD is 'int I.m(List las,List + * lbs) {}' then this returns a list containing the strings "A" and "B". + */ + public List getTypeVariableAliases() { + return munger.getTypeVariableAliases(); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof BcelTypeMunger)) { + return false; + } + BcelTypeMunger o = (BcelTypeMunger) other; + return ((o.getMunger() == null) ? (getMunger() == null) : o.getMunger().equals(getMunger())) + && ((o.getAspectType() == null) ? (getAspectType() == null) : o.getAspectType().equals(getAspectType())); + // && (AsmManager.getDefault().getHandleProvider().dependsOnLocation() ? ((o.getSourceLocation() == null) ? + // (getSourceLocation() == null) + // : o.getSourceLocation().equals(getSourceLocation())) + // : true); // pr134471 - remove when handles are improved + // to be independent of location + + } + + private volatile int hashCode = 0; + + @Override + public int hashCode() { + if (hashCode == 0) { + int result = 17; + result = 37 * result + ((getMunger() == null) ? 0 : getMunger().hashCode()); + result = 37 * result + ((getAspectType() == null) ? 0 : getAspectType().hashCode()); + hashCode = result; + } + return hashCode; + } +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/BcelVar.java b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelVar.java new file mode 100644 index 000000000..ce45fdf12 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelVar.java @@ -0,0 +1,117 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.bcel; + +import org.aspectj.apache.bcel.generic.Instruction; +import org.aspectj.apache.bcel.generic.InstructionFactory; +import org.aspectj.apache.bcel.generic.InstructionList; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.ast.Var; + +public class BcelVar extends Var { + + private int positionInAroundState = -1; + + private int slot; + + public BcelVar(ResolvedType type, int slot) { + super(type); + this.slot = slot; + } + + public String toString() { + return "BcelVar(" + getType() + " " + slot + ((positionInAroundState != -1) ? (" " + positionInAroundState) : "") + + + ")"; + } + + public int getSlot() { + return slot; + } + + // fact is used in the subtypes + public Instruction createLoad(InstructionFactory fact) { + return InstructionFactory.createLoad(BcelWorld.makeBcelType(getType()), slot); + } + + public Instruction createStore(InstructionFactory fact) { + return InstructionFactory.createStore(BcelWorld.makeBcelType(getType()), slot); + } + + public void appendStore(InstructionList il, InstructionFactory fact) { + il.append(createStore(fact)); + } + + public void appendLoad(InstructionList il, InstructionFactory fact) { + il.append(createLoad(fact)); + } + + public void appendLoadAndConvert(InstructionList il, InstructionFactory fact, ResolvedType toType) { + il.append(createLoad(fact)); + Utility.appendConversion(il, fact, getType(), toType); + } + + public void insertLoad(InstructionList il, InstructionFactory fact) { + il.insert(createLoad(fact)); + } + + public InstructionList createCopyFrom(InstructionFactory fact, int oldSlot) { + InstructionList il = new InstructionList(); + il.append(InstructionFactory.createLoad(BcelWorld.makeBcelType(getType()), oldSlot)); + il.append(createStore(fact)); + return il; + } + + // this is an array var + void appendConvertableArrayLoad(InstructionList il, InstructionFactory fact, int index, ResolvedType convertTo) { + ResolvedType convertFromType = getType().getResolvedComponentType(); + appendLoad(il, fact); + il.append(Utility.createConstant(fact, index)); + il.append(InstructionFactory.createArrayLoad(BcelWorld.makeBcelType(convertFromType))); + Utility.appendConversion(il, fact, convertFromType, convertTo); + } + + void appendConvertableArrayStore(InstructionList il, InstructionFactory fact, int index, BcelVar storee) { + ResolvedType convertToType = getType().getResolvedComponentType(); + appendLoad(il, fact); + il.append(Utility.createConstant(fact, index)); + storee.appendLoad(il, fact); + Utility.appendConversion(il, fact, storee.getType(), convertToType); + il.append(InstructionFactory.createArrayStore(BcelWorld.makeBcelType(convertToType))); + } + + InstructionList createConvertableArrayStore(InstructionFactory fact, int index, BcelVar storee) { + InstructionList il = new InstructionList(); + appendConvertableArrayStore(il, fact, index, storee); + return il; + } + + InstructionList createConvertableArrayLoad(InstructionFactory fact, int index, ResolvedType convertTo) { + InstructionList il = new InstructionList(); + appendConvertableArrayLoad(il, fact, index, convertTo); + return il; + } + + public int getPositionInAroundState() { + return positionInAroundState; + } + + public void setPositionInAroundState(int positionInAroundState) { + this.positionInAroundState = positionInAroundState; + } + + // random useful fields + + public static final BcelVar[] NONE = new BcelVar[] {}; + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/BcelWeakClassLoaderReference.java b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelWeakClassLoaderReference.java new file mode 100644 index 000000000..aac294fa4 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelWeakClassLoaderReference.java @@ -0,0 +1,49 @@ +/* ******************************************************************* + * Copyright (c) 2008 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement initial implementation + * ******************************************************************/ +package org.aspectj.weaver.bcel; + +import org.aspectj.apache.bcel.util.ClassLoaderReference; +import org.aspectj.weaver.WeakClassLoaderReference; + +/** + * Wraps a reference to a classloader inside a WeakReference. This should be used where we do not want the existence of a + * classloader reference to prevent garbage collection of that classloader (and possibly an associated weaver instance in the case + * of load time weaving). + *

+ * In more detail:
+ * When load time weaving, the class Aj maintains a WeakHashMap from the classloader instance to a weaver instance. The aim is that + * the weaver is around as long as the classloader is and should the classloader be dereferenced then the weaver can also be garbage + * collected. The problem is that if there are many references to the classloader from within the weaver, these are considered hard + * references and cause the classloader to be long lived - even if the user of the classloader has dereferenced it in their code. + * The solution is that the weaver should use instances of WeakClassLoaderReference objects - so that when the users hard reference + * to the classloader goes, nothing in the weaver will cause it to hang around. There is a big assertion here that the + * WeakClassLoaderReference instances will not 'lose' their ClassLoader references until the top level ClassLoader reference is + * null'd. This means there is no need to check for the null case on get() in this WeakReference logic below, because we shouldn't + * be using this weaver if its associated ClassLoader has been collected. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=210470 + * + * + * @author Andy Clement + */ +public class BcelWeakClassLoaderReference extends WeakClassLoaderReference implements ClassLoaderReference { + + public BcelWeakClassLoaderReference(ClassLoader loader) { + super(loader); + } + + public boolean equals(Object obj) { + if (!(obj instanceof BcelWeakClassLoaderReference)) + return false; + BcelWeakClassLoaderReference other = (BcelWeakClassLoaderReference) obj; + return (other.hashcode == hashcode); + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/BcelWeaver.java b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelWeaver.java new file mode 100644 index 000000000..f83a79379 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelWeaver.java @@ -0,0 +1,2043 @@ +/* ******************************************************************* + * Copyright (c) 2002-2010 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * ******************************************************************/ +package org.aspectj.weaver.bcel; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileFilter; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.jar.Attributes; +import java.util.jar.Attributes.Name; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + +import org.aspectj.apache.bcel.classfile.ClassParser; +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.asm.AsmManager; +import org.aspectj.asm.IProgramElement; +import org.aspectj.asm.internal.AspectJElementHierarchy; +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.Message; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.bridge.SourceLocation; +import org.aspectj.bridge.WeaveMessage; +import org.aspectj.bridge.context.CompilationAndWeavingContext; +import org.aspectj.bridge.context.ContextToken; +import org.aspectj.util.FileUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.Advice; +import org.aspectj.weaver.AdviceKind; +import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; +import org.aspectj.weaver.AnnotationAJ; +import org.aspectj.weaver.AnnotationOnTypeMunger; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ConcreteTypeMunger; +import org.aspectj.weaver.CrosscuttingMembersSet; +import org.aspectj.weaver.CustomMungerFactory; +import org.aspectj.weaver.IClassFileProvider; +import org.aspectj.weaver.IUnwovenClassFile; +import org.aspectj.weaver.IWeaveRequestor; +import org.aspectj.weaver.NewParentTypeMunger; +import org.aspectj.weaver.ReferenceType; +import org.aspectj.weaver.ReferenceTypeDelegate; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.ResolvedTypeMunger; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.ShadowMunger; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.WeaverStateInfo; +import org.aspectj.weaver.World; +import org.aspectj.weaver.model.AsmRelationshipProvider; +import org.aspectj.weaver.patterns.AndPointcut; +import org.aspectj.weaver.patterns.BindingPattern; +import org.aspectj.weaver.patterns.BindingTypePattern; +import org.aspectj.weaver.patterns.ConcreteCflowPointcut; +import org.aspectj.weaver.patterns.DeclareAnnotation; +import org.aspectj.weaver.patterns.DeclareParents; +import org.aspectj.weaver.patterns.DeclareTypeErrorOrWarning; +import org.aspectj.weaver.patterns.FastMatchInfo; +import org.aspectj.weaver.patterns.IfPointcut; +import org.aspectj.weaver.patterns.KindedPointcut; +import org.aspectj.weaver.patterns.NameBindingPointcut; +import org.aspectj.weaver.patterns.NotPointcut; +import org.aspectj.weaver.patterns.OrPointcut; +import org.aspectj.weaver.patterns.Pointcut; +import org.aspectj.weaver.patterns.PointcutRewriter; +import org.aspectj.weaver.patterns.WithinPointcut; +import org.aspectj.weaver.tools.Trace; +import org.aspectj.weaver.tools.TraceFactory; + +/** + * + * @author PARC + * @author Andy Clement + * @author Alexandre Vasseur + */ +public class BcelWeaver { + + public static final String CLOSURE_CLASS_PREFIX = "$Ajc"; + public static final String SYNTHETIC_CLASS_POSTFIX = "$ajc"; + + private static Trace trace = TraceFactory.getTraceFactory().getTrace(BcelWeaver.class); + + private transient final BcelWorld world; + private final CrosscuttingMembersSet xcutSet; + + private boolean inReweavableMode = false; + + private transient List addedClasses = new ArrayList(); + private transient List deletedTypenames = new ArrayList(); + + // These four are setup by prepareForWeave + private transient List shadowMungerList = null; + private transient List typeMungerList = null; + private transient List lateTypeMungerList = null; + private transient List declareParentsList = null; + + private Manifest manifest = null; + private boolean needToReweaveWorld = false; + + private boolean isBatchWeave = true; + + private ZipOutputStream zipOutputStream; + private CustomMungerFactory customMungerFactory; + + public BcelWeaver(BcelWorld world) { + super(); + if (trace.isTraceEnabled()) { + trace.enter("", this, world); + } + this.world = world; + this.xcutSet = world.getCrosscuttingMembersSet(); + if (trace.isTraceEnabled()) { + trace.exit(""); + } + } + + /** + * Add the given aspect to the weaver. The type is resolved to support DOT for static inner classes as well as DOLLAR + * + * @param aspectName + * @return aspect + */ + public ResolvedType addLibraryAspect(String aspectName) { + if (trace.isTraceEnabled()) { + trace.enter("addLibraryAspect", this, aspectName); + } + + // 1 - resolve as is + UnresolvedType unresolvedT = UnresolvedType.forName(aspectName); + unresolvedT.setNeedsModifiableDelegate(true); + ResolvedType type = world.resolve(unresolvedT, true); + if (type.isMissing()) { + // fallback on inner class lookup mechanism + String fixedName = aspectName; + int hasDot = fixedName.lastIndexOf('.'); + while (hasDot > 0) { + // System.out.println("BcelWeaver.addLibraryAspect " + fixedName); + char[] fixedNameChars = fixedName.toCharArray(); + fixedNameChars[hasDot] = '$'; + fixedName = new String(fixedNameChars); + hasDot = fixedName.lastIndexOf('.'); + UnresolvedType ut = UnresolvedType.forName(fixedName); + ut.setNeedsModifiableDelegate(true); + type = world.resolve(ut, true); + if (!type.isMissing()) { + break; + } + } + } + + // System.out.println("type: " + type + " for " + aspectName); + if (type.isAspect()) { + // Bug 119657 ensure we use the unwoven aspect + WeaverStateInfo wsi = type.getWeaverState(); + if (wsi != null && wsi.isReweavable()) { + BcelObjectType classType = getClassType(type.getName()); + JavaClass wovenJavaClass = classType.getJavaClass(); + byte[] bytes = wsi.getUnwovenClassFileData(wovenJavaClass.getBytes()); + JavaClass unwovenJavaClass = Utility.makeJavaClass(wovenJavaClass.getFileName(), bytes); + world.storeClass(unwovenJavaClass); + classType.setJavaClass(unwovenJavaClass, true); + // classType.setJavaClass(Utility.makeJavaClass(classType. + // getJavaClass().getFileName(), + // wsi.getUnwovenClassFileData(classType.getJavaClass().getBytes( + // )))); + } + + // TODO AV - happens to reach that a lot of time: for each type + // flagged reweavable X for each aspect in the weaverstate + // => mainly for nothing for LTW - pbly for something in incremental + // build... + xcutSet.addOrReplaceAspect(type); + if (trace.isTraceEnabled()) { + trace.exit("addLibraryAspect", type); + } + if (type.getSuperclass().isAspect()) { + // If the supertype includes ITDs and the user has not included + // that aspect in the aop.xml, they will + // not get picked up, which can give unusual behaviour! See bug + // 223094 + // This change causes us to pick up the super aspect regardless + // of what was said in the aop.xml - giving + // predictable behaviour. If the user also supplied it, there + // will be no problem other than the second + // addition overriding the first + addLibraryAspect(type.getSuperclass().getName()); + } + return type; + } else { + if (type.isMissing()) { + // May not be found if not visible to the classloader that can see the aop.xml during LTW + IMessage message = new Message("The specified aspect '"+aspectName+"' cannot be found", null, true); + world.getMessageHandler().handleMessage(message); + } else { + IMessage message = new Message("Cannot register '"+aspectName+"' because the type found with that name is not an aspect", null, true); + world.getMessageHandler().handleMessage(message); + } + return null; + } + } + + /** + * + * @param inFile directory containing classes or zip/jar class archive + */ + public void addLibraryJarFile(File inFile) throws IOException { + List addedAspects = null; + if (inFile.isDirectory()) { + addedAspects = addAspectsFromDirectory(inFile); + } else { + addedAspects = addAspectsFromJarFile(inFile); + } + for (ResolvedType addedAspect : addedAspects) { + xcutSet.addOrReplaceAspect(addedAspect); + } + } + + private List addAspectsFromJarFile(File inFile) throws FileNotFoundException, IOException { + ZipInputStream inStream = new ZipInputStream(new FileInputStream(inFile)); // ??? buffered + List addedAspects = new ArrayList(); + try { + while (true) { + ZipEntry entry = inStream.getNextEntry(); + if (entry == null) { + break; + } + + if (entry.isDirectory() || !entry.getName().endsWith(".class")) { + continue; + } + + // FIXME ASC performance? of this alternative soln. + int size = (int) entry.getSize(); + ClassParser parser = new ClassParser(new ByteArrayInputStream(FileUtil.readAsByteArray(inStream)), entry.getName()); + JavaClass jc = parser.parse(); + inStream.closeEntry(); + + ResolvedType type = world.addSourceObjectType(jc, false).getResolvedTypeX(); + type.setBinaryPath(inFile.getAbsolutePath()); + if (type.isAspect()) { + addedAspects.add(type); + } else { + world.demote(type); + } + + } + } finally { + inStream.close(); + } + return addedAspects; + } + + /** + * Look for .class files that represent aspects in the supplied directory - return the list of accumulated aspects. + * + * @param directory the directory in which to look for Aspect .class files + * @return the list of discovered aspects + * @throws FileNotFoundException + * @throws IOException + */ + private List addAspectsFromDirectory(File directory) throws FileNotFoundException, IOException { + List addedAspects = new ArrayList(); + File[] classFiles = FileUtil.listFiles(directory, new FileFilter() { + public boolean accept(File pathname) { + return pathname.getName().endsWith(".class"); + } + }); + for (File classFile : classFiles) { + FileInputStream fis = new FileInputStream(classFile); + byte[] classBytes = FileUtil.readAsByteArray(fis); + ResolvedType aspectType = isAspect(classBytes, classFile.getAbsolutePath(), directory); + if (aspectType != null) { + addedAspects.add(aspectType); + } + fis.close(); + } + return addedAspects; + } + + /** + * Determine if the supplied bytes represent an aspect, if they do then create a ResolvedType instance for the aspect and return + * it, otherwise return null + * + * @param classbytes the classbytes that might represent an aspect + * @param name the name of the class + * @param directory directory which contained the class file + * @return a ResolvedType if the classbytes represent an aspect, otherwise null + */ + private ResolvedType isAspect(byte[] classbytes, String name, File dir) throws IOException { + ClassParser parser = new ClassParser(new ByteArrayInputStream(classbytes), name); + JavaClass jc = parser.parse(); + ResolvedType type = world.addSourceObjectType(jc, false).getResolvedTypeX(); + String typeName = type.getName().replace('.', File.separatorChar); + int end = name.lastIndexOf(typeName + ".class"); + String binaryPath = null; + // if end is -1 then something weird happened, the class file is not in + // the correct place, something like + // bin/A.class when the declaration for A specifies it is in a package. + if (end == -1) { + binaryPath = dir.getAbsolutePath(); + } else { + binaryPath = name.substring(0, end - 1); + } + type.setBinaryPath(binaryPath); + if (type.isAspect()) { + return type; + } else { + // immediately demote the type we just added since it will have + // have been stuffed into the permanent map (assumed to be + // an aspect) + world.demote(type); + return null; + } + } + + // // The ANT copy task should be used to copy resources across. + // private final static boolean + // CopyResourcesFromInpathDirectoriesToOutput=false; + + /** + * Add any .class files in the directory to the outdir. Anything other than .class files in the directory (or its + * subdirectories) are considered resources and are also copied. + * + */ + public List addDirectoryContents(File inFile, File outDir) throws IOException { + List addedClassFiles = new ArrayList(); + + // Get a list of all files (i.e. everything that isnt a directory) + File[] files = FileUtil.listFiles(inFile, new FileFilter() { + public boolean accept(File f) { + boolean accept = !f.isDirectory(); + return accept; + } + }); + + // For each file, add it either as a real .class file or as a resource + for (int i = 0; i < files.length; i++) { + addedClassFiles.add(addClassFile(files[i], inFile, outDir)); + } + + return addedClassFiles; + } + + /** + * Adds all class files in the jar + */ + public List addJarFile(File inFile, File outDir, boolean canBeDirectory) { + // System.err.println("? addJarFile(" + inFile + ", " + outDir + ")"); + List addedClassFiles = new ArrayList(); + needToReweaveWorld = true; + JarFile inJar = null; + + try { + // Is this a directory we are looking at? + if (inFile.isDirectory() && canBeDirectory) { + addedClassFiles.addAll(addDirectoryContents(inFile, outDir)); + } else { + + inJar = new JarFile(inFile); + try { + addManifest(inJar.getManifest()); + Enumeration entries = inJar.entries(); + + while (entries.hasMoreElements()) { + JarEntry entry = (JarEntry) entries.nextElement(); + InputStream inStream = inJar.getInputStream(entry); + + byte[] bytes = FileUtil.readAsByteArray(inStream); + String filename = entry.getName(); + // System.out.println("? addJarFile() filename='" + filename + // + "'"); + UnwovenClassFile classFile = new UnwovenClassFile(new File(outDir, filename).getAbsolutePath(), bytes); + + if (filename.endsWith(".class")) { + ReferenceType type = this.addClassFile(classFile, false); + StringBuffer sb = new StringBuffer(); + sb.append(inFile.getAbsolutePath()); + sb.append("!"); + sb.append(entry.getName()); + type.setBinaryPath(sb.toString()); + addedClassFiles.add(classFile); + } + // else if (!entry.isDirectory()) { + // + // /* bug-44190 Copy meta-data */ + // addResource(filename,classFile); + // } + + inStream.close(); + } + } finally { + inJar.close(); + } + inJar.close(); + } + } catch (FileNotFoundException ex) { + IMessage message = new Message("Could not find input jar file " + inFile.getPath() + ", ignoring", new SourceLocation( + inFile, 0), false); + world.getMessageHandler().handleMessage(message); + } catch (IOException ex) { + IMessage message = new Message("Could not read input jar file " + inFile.getPath() + "(" + ex.getMessage() + ")", + new SourceLocation(inFile, 0), true); + world.getMessageHandler().handleMessage(message); + } finally { + if (inJar != null) { + try { + inJar.close(); + } catch (IOException ex) { + IMessage message = new Message("Could not close input jar file " + inFile.getPath() + "(" + ex.getMessage() + + ")", new SourceLocation(inFile, 0), true); + world.getMessageHandler().handleMessage(message); + } + } + } + + return addedClassFiles; + } + + public boolean needToReweaveWorld() { + return needToReweaveWorld; + } + + /** + * Should be addOrReplace + */ + public ReferenceType addClassFile(UnwovenClassFile classFile, boolean fromInpath) { + addedClasses.add(classFile); + ReferenceType type = world.addSourceObjectType(classFile.getJavaClass(), false).getResolvedTypeX(); + if (fromInpath) { + type.setBinaryPath(classFile.getFilename()); + } + return type; + } + + public UnwovenClassFile addClassFile(File classFile, File inPathDir, File outDir) throws IOException { + FileInputStream fis = new FileInputStream(classFile); + byte[] bytes = FileUtil.readAsByteArray(fis); + // String relativePath = files[i].getPath(); + + // ASSERT: + // files[i].getAbsolutePath().startsWith(inFile.getAbsolutePath() + // or we are in trouble... + String filename = classFile.getAbsolutePath().substring(inPathDir.getAbsolutePath().length() + 1); + UnwovenClassFile ucf = new UnwovenClassFile(new File(outDir, filename).getAbsolutePath(), bytes); + if (filename.endsWith(".class")) { + // System.err.println( + // "BCELWeaver: processing class from input directory "+classFile); + StringBuffer sb = new StringBuffer(); + sb.append(inPathDir.getAbsolutePath()); + sb.append("!"); + sb.append(filename); + ReferenceType type = this.addClassFile(ucf, false); + type.setBinaryPath(sb.toString()); + } + fis.close(); + return ucf; + } + + public void deleteClassFile(String typename) { + deletedTypenames.add(typename); + world.deleteSourceObjectType(UnresolvedType.forName(typename)); + } + + // ---- weave preparation + + public void setIsBatchWeave(boolean b) { + isBatchWeave = b; + } + + public void prepareForWeave() { + if (trace.isTraceEnabled()) { + trace.enter("prepareForWeave", this); + } + needToReweaveWorld = xcutSet.hasChangedSinceLastReset(); + + // update mungers + for (Iterator i = addedClasses.iterator(); i.hasNext();) { + UnwovenClassFile jc = i.next(); + String name = jc.getClassName(); + ResolvedType type = world.resolve(name); + // No overweaving guard. If you have one then when overweaving is on the + // addOrReplaceAspect will not be called when the aspect delegate changes from + // EclipseSourceType to BcelObjectType. This will mean the mungers + // are not picked up. + if (type.isAspect()) { + needToReweaveWorld |= xcutSet.addOrReplaceAspect(type); + } + } + + for (Iterator i = deletedTypenames.iterator(); i.hasNext();) { + String name = i.next(); + if (xcutSet.deleteAspect(UnresolvedType.forName(name))) { + needToReweaveWorld = true; + } + } + + shadowMungerList = xcutSet.getShadowMungers(); + // world.debug("shadow mungers=" + shadowMungerList); + rewritePointcuts(shadowMungerList); + // Sometimes an error occurs during rewriting pointcuts (for example, if + // ambiguous bindings + // are detected) - we ought to fail the prepare when this happens + // because continuing with + // inconsistent pointcuts could lead to problems + typeMungerList = xcutSet.getTypeMungers(); + lateTypeMungerList = xcutSet.getLateTypeMungers(); + declareParentsList = xcutSet.getDeclareParents(); + + addCustomMungers(); + + // The ordering here used to be based on a string compare on toString() + // for the two mungers - + // that breaks for the @AJ style where advice names aren't + // programmatically generated. So we + // have changed the sorting to be based on source location in the file - + // this is reliable, in + // the case of source locations missing, we assume they are 'sorted' - + // i.e. the order in + // which they were added to the collection is correct, this enables the + // @AJ stuff to work properly. + + // When @AJ processing starts filling in source locations for mungers, + // this code may need + // a bit of alteration... + + Collections.sort(shadowMungerList, new Comparator() { + public int compare(ShadowMunger sm1, ShadowMunger sm2) { + if (sm1.getSourceLocation() == null) { + return (sm2.getSourceLocation() == null ? 0 : 1); + } + if (sm2.getSourceLocation() == null) { + return -1; + } + + return (sm2.getSourceLocation().getOffset() - sm1.getSourceLocation().getOffset()); + } + }); + + if (inReweavableMode) { + world.showMessage(IMessage.INFO, WeaverMessages.format(WeaverMessages.REWEAVABLE_MODE), null, null); + } + + if (trace.isTraceEnabled()) { + trace.exit("prepareForWeave"); + } + } + + private void addCustomMungers() { + if (customMungerFactory != null) { + for (Iterator i = addedClasses.iterator(); i.hasNext();) { + UnwovenClassFile jc = i.next(); + String name = jc.getClassName(); + ResolvedType type = world.resolve(name); + if (type.isAspect()) { + Collection shadowMungers = customMungerFactory.createCustomShadowMungers(type); + if (shadowMungers != null) { + shadowMungerList.addAll(shadowMungers); + } + Collection typeMungers = customMungerFactory.createCustomTypeMungers(type); + if (typeMungers != null) { + typeMungerList.addAll(typeMungers); + } + } + } + } + } + + public void setCustomMungerFactory(CustomMungerFactory factory) { + customMungerFactory = factory; + } + + /* + * Rewrite all of the pointcuts in the world into their most efficient form for subsequent matching. Also ensure that if + * pc1.equals(pc2) then pc1 == pc2 (for non-binding pcds) by making references all point to the same instance. Since pointcuts + * remember their match decision on the last shadow, this makes matching faster when many pointcuts share common elements, or + * even when one single pointcut has one common element (which can be a side-effect of DNF rewriting). + */ + private void rewritePointcuts(List shadowMungers) { + PointcutRewriter rewriter = new PointcutRewriter(); + for (ShadowMunger munger : shadowMungers) { + Pointcut p = munger.getPointcut(); + Pointcut newP = rewriter.rewrite(p); + // validateBindings now whilst we still have around the pointcut + // that resembles what the user actually wrote in their program + // text. + if (munger instanceof Advice) { + Advice advice = (Advice) munger; + if (advice.getSignature() != null) { + final int numFormals; + final String names[]; + // If the advice is being concretized in a @AJ aspect *and* + // the advice was declared in + // an @AJ aspect (it could have been inherited from a code + // style aspect) then + // evaluate the alternative set of formals. pr125699 + if ((advice.getConcreteAspect().isAnnotationStyleAspect() && advice.getDeclaringAspect() != null && advice + .getDeclaringAspect().resolve(world).isAnnotationStyleAspect()) + || advice.isAnnotationStyle()) { + numFormals = advice.getBaseParameterCount(); + int numArgs = advice.getSignature().getParameterTypes().length; + if (numFormals > 0) { + names = advice.getSignature().getParameterNames(world); + validateBindings(newP, p, numArgs, names); + } + } else { + numFormals = advice.getBaseParameterCount(); + if (numFormals > 0) { + names = advice.getBaseParameterNames(world); + validateBindings(newP, p, numFormals, names); + } + } + } + } + newP.m_ignoreUnboundBindingForNames = p.m_ignoreUnboundBindingForNames; + munger.setPointcut(newP); + } + // now that we have optimized individual pointcuts, optimize + // across the set of pointcuts.... + // Use a map from key based on pc equality, to value based on + // pc identity. + Map pcMap = new HashMap(); + for (ShadowMunger munger: shadowMungers) { + Pointcut p = munger.getPointcut(); + Pointcut newP = shareEntriesFromMap(p, pcMap); + newP.m_ignoreUnboundBindingForNames = p.m_ignoreUnboundBindingForNames; + munger.setPointcut(newP); + } + } + + private Pointcut shareEntriesFromMap(Pointcut p, Map pcMap) { + // some things cant be shared... + if (p instanceof NameBindingPointcut) { + return p; + } + if (p instanceof IfPointcut) { + return p; + } + if (p instanceof ConcreteCflowPointcut) { + return p; + } + if (p instanceof AndPointcut) { + AndPointcut apc = (AndPointcut) p; + Pointcut left = shareEntriesFromMap(apc.getLeft(), pcMap); + Pointcut right = shareEntriesFromMap(apc.getRight(), pcMap); + return new AndPointcut(left, right); + } else if (p instanceof OrPointcut) { + OrPointcut opc = (OrPointcut) p; + Pointcut left = shareEntriesFromMap(opc.getLeft(), pcMap); + Pointcut right = shareEntriesFromMap(opc.getRight(), pcMap); + return new OrPointcut(left, right); + } else if (p instanceof NotPointcut) { + NotPointcut npc = (NotPointcut) p; + Pointcut not = shareEntriesFromMap(npc.getNegatedPointcut(), pcMap); + return new NotPointcut(not); + } else { + // primitive pcd + if (pcMap.containsKey(p)) { // based on equality + return pcMap.get(p); // same instance (identity) + } else { + pcMap.put(p, p); + return p; + } + } + } + + // userPointcut is the pointcut that the user wrote in the program text. + // dnfPointcut is the same pointcut rewritten in DNF + // numFormals is the number of formal parameters in the pointcut + // if numFormals > 0 then every branch of a disjunction must bind each + // formal once and only once. + // in addition, the left and right branches of a disjunction must hold on + // join point kinds in common. + private void validateBindings(Pointcut dnfPointcut, Pointcut userPointcut, int numFormals, String[] names) { + if (numFormals == 0) { + return; // nothing to check + } + if (dnfPointcut.couldMatchKinds() == Shadow.NO_SHADOW_KINDS_BITS) { + return; // cant have problems if you dont match! + } + if (dnfPointcut instanceof OrPointcut) { + OrPointcut orBasedDNFPointcut = (OrPointcut) dnfPointcut; + Pointcut[] leftBindings = new Pointcut[numFormals]; + Pointcut[] rightBindings = new Pointcut[numFormals]; + validateOrBranch(orBasedDNFPointcut, userPointcut, numFormals, names, leftBindings, rightBindings); + } else { + Pointcut[] bindings = new Pointcut[numFormals]; + validateSingleBranch(dnfPointcut, userPointcut, numFormals, names, bindings); + } + } + + private void validateOrBranch(OrPointcut pc, Pointcut userPointcut, int numFormals, String[] names, Pointcut[] leftBindings, + Pointcut[] rightBindings) { + Pointcut left = pc.getLeft(); + Pointcut right = pc.getRight(); + if (left instanceof OrPointcut) { + Pointcut[] newRightBindings = new Pointcut[numFormals]; + validateOrBranch((OrPointcut) left, userPointcut, numFormals, names, leftBindings, newRightBindings); + } else { + if (left.couldMatchKinds() != Shadow.NO_SHADOW_KINDS_BITS) { + validateSingleBranch(left, userPointcut, numFormals, names, leftBindings); + } + } + if (right instanceof OrPointcut) { + Pointcut[] newLeftBindings = new Pointcut[numFormals]; + validateOrBranch((OrPointcut) right, userPointcut, numFormals, names, newLeftBindings, rightBindings); + } else { + if (right.couldMatchKinds() != Shadow.NO_SHADOW_KINDS_BITS) { + validateSingleBranch(right, userPointcut, numFormals, names, rightBindings); + } + } + int kindsInCommon = left.couldMatchKinds() & right.couldMatchKinds(); + if (kindsInCommon != Shadow.NO_SHADOW_KINDS_BITS && couldEverMatchSameJoinPoints(left, right)) { + // we know that every branch binds every formal, so there is no + // ambiguity if each branch binds it in exactly the same way... + List ambiguousNames = new ArrayList(); + for (int i = 0; i < numFormals; i++) { + if (leftBindings[i] == null) { + if (rightBindings[i] != null) { + ambiguousNames.add(names[i]); + } + } else if (!leftBindings[i].equals(rightBindings[i])) { + ambiguousNames.add(names[i]); + } + } + if (!ambiguousNames.isEmpty()) { + raiseAmbiguityInDisjunctionError(userPointcut, ambiguousNames); + } + } + } + + // pc is a pointcut that does not contain any disjunctions + // check that every formal is bound (negation doesn't count). + // we know that numFormals > 0 or else we would not be called + private void validateSingleBranch(Pointcut pc, Pointcut userPointcut, int numFormals, String[] names, Pointcut[] bindings) { + boolean[] foundFormals = new boolean[numFormals]; + for (int i = 0; i < foundFormals.length; i++) { + foundFormals[i] = false; + } + validateSingleBranchRecursion(pc, userPointcut, foundFormals, names, bindings); + for (int i = 0; i < foundFormals.length; i++) { + if (!foundFormals[i]) { + boolean ignore = false; + // ATAJ soften the unbound error for implicit bindings like + // JoinPoint in @AJ style + for (int j = 0; j < userPointcut.m_ignoreUnboundBindingForNames.length; j++) { + if (names[i] != null && names[i].equals(userPointcut.m_ignoreUnboundBindingForNames[j])) { + ignore = true; + break; + } + } + if (!ignore) { + raiseUnboundFormalError(names[i], userPointcut); + } + } + } + } + + // each formal must appear exactly once + private void validateSingleBranchRecursion(Pointcut pc, Pointcut userPointcut, boolean[] foundFormals, String[] names, + Pointcut[] bindings) { + if (pc instanceof NotPointcut) { + // nots can only appear at leaves in DNF + NotPointcut not = (NotPointcut) pc; + if (not.getNegatedPointcut() instanceof NameBindingPointcut) { + NameBindingPointcut nnbp = (NameBindingPointcut) not.getNegatedPointcut(); + if (!nnbp.getBindingAnnotationTypePatterns().isEmpty() && !nnbp.getBindingTypePatterns().isEmpty()) { + raiseNegationBindingError(userPointcut); + } + } + } else if (pc instanceof AndPointcut) { + AndPointcut and = (AndPointcut) pc; + validateSingleBranchRecursion(and.getLeft(), userPointcut, foundFormals, names, bindings); + validateSingleBranchRecursion(and.getRight(), userPointcut, foundFormals, names, bindings); + } else if (pc instanceof NameBindingPointcut) { + List bindingTypePatterns = ((NameBindingPointcut) pc).getBindingTypePatterns(); + for (BindingTypePattern bindingTypePattern: bindingTypePatterns) { + int index = bindingTypePattern.getFormalIndex(); + bindings[index] = pc; + if (foundFormals[index]) { + raiseAmbiguousBindingError(names[index], userPointcut); + } else { + foundFormals[index] = true; + } + } + List bindingAnnotationTypePatterns = ((NameBindingPointcut) pc).getBindingAnnotationTypePatterns(); + for (BindingPattern bindingAnnotationTypePattern: bindingAnnotationTypePatterns) { + int index = bindingAnnotationTypePattern.getFormalIndex(); + bindings[index] = pc; + if (foundFormals[index]) { + raiseAmbiguousBindingError(names[index], userPointcut); + } else { + foundFormals[index] = true; + } + } + } else if (pc instanceof ConcreteCflowPointcut) { + ConcreteCflowPointcut cfp = (ConcreteCflowPointcut) pc; + int[] slots = cfp.getUsedFormalSlots(); + for (int i = 0; i < slots.length; i++) { + bindings[slots[i]] = cfp; + if (foundFormals[slots[i]]) { + raiseAmbiguousBindingError(names[slots[i]], userPointcut); + } else { + foundFormals[slots[i]] = true; + } + } + } + } + + // By returning false from this method, we are allowing binding of the same + // variable on either side of an or. + // Be conservative :- have to consider overriding, varargs, autoboxing, + // the effects of itds (on within for example), interfaces, the fact that + // join points can have multiple signatures and so on. + private boolean couldEverMatchSameJoinPoints(Pointcut left, Pointcut right) { + + if (left instanceof OrPointcut) { + OrPointcut leftOrPointcut = (OrPointcut) left; + if (couldEverMatchSameJoinPoints(leftOrPointcut.getLeft(), right)) { + return true; + } + if (couldEverMatchSameJoinPoints(leftOrPointcut.getRight(), right)) { + return true; + } + return false; + } + + if (right instanceof OrPointcut) { + OrPointcut rightOrPointcut = (OrPointcut) right; + if (couldEverMatchSameJoinPoints(left, rightOrPointcut.getLeft())) { + return true; + } + if (couldEverMatchSameJoinPoints(left, rightOrPointcut.getRight())) { + return true; + } + return false; + } + + // look for withins + WithinPointcut leftWithin = (WithinPointcut) findFirstPointcutIn(left, WithinPointcut.class); + WithinPointcut rightWithin = (WithinPointcut) findFirstPointcutIn(right, WithinPointcut.class); + if ((leftWithin != null) && (rightWithin != null)) { + if (!leftWithin.couldEverMatchSameJoinPointsAs(rightWithin)) { + return false; + } + } + // look for kinded + KindedPointcut leftKind = (KindedPointcut) findFirstPointcutIn(left, KindedPointcut.class); + KindedPointcut rightKind = (KindedPointcut) findFirstPointcutIn(right, KindedPointcut.class); + if ((leftKind != null) && (rightKind != null)) { + if (!leftKind.couldEverMatchSameJoinPointsAs(rightKind)) { + return false; + } + } + return true; + } + + private Pointcut findFirstPointcutIn(Pointcut toSearch, Class toLookFor) { + if (toSearch instanceof NotPointcut) { + return null; + } + if (toLookFor.isInstance(toSearch)) { + return toSearch; + } + if (toSearch instanceof AndPointcut) { + AndPointcut apc = (AndPointcut) toSearch; + Pointcut left = findFirstPointcutIn(apc.getLeft(), toLookFor); + if (left != null) { + return left; + } + return findFirstPointcutIn(apc.getRight(), toLookFor); + } + return null; + } + + /** + * @param userPointcut + */ + private void raiseNegationBindingError(Pointcut userPointcut) { + world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.NEGATION_DOESNT_ALLOW_BINDING), userPointcut + .getSourceContext().makeSourceLocation(userPointcut), null); + } + + private void raiseAmbiguousBindingError(String name, Pointcut pointcut) { + world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.AMBIGUOUS_BINDING, name), pointcut + .getSourceContext().makeSourceLocation(pointcut), null); + } + + /** + * @param userPointcut + */ + private void raiseAmbiguityInDisjunctionError(Pointcut userPointcut, List names) { + StringBuffer formalNames = new StringBuffer(names.get(0).toString()); + for (int i = 1; i < names.size(); i++) { + formalNames.append(", "); + formalNames.append(names.get(i)); + } + world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.AMBIGUOUS_BINDING_IN_OR, formalNames), userPointcut + .getSourceContext().makeSourceLocation(userPointcut), null); + } + + /** + * @param name + * @param userPointcut + */ + private void raiseUnboundFormalError(String name, Pointcut userPointcut) { + world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.UNBOUND_FORMAL, name), + userPointcut.getSourceLocation(), null); + } + + public void addManifest(Manifest newManifest) { + // System.out.println("? addManifest() newManifest=" + newManifest); + if (manifest == null) { + manifest = newManifest; + } + } + + public Manifest getManifest(boolean shouldCreate) { + if (manifest == null && shouldCreate) { + String WEAVER_MANIFEST_VERSION = "1.0"; + Attributes.Name CREATED_BY = new Name("Created-By"); + String WEAVER_CREATED_BY = "AspectJ Compiler"; + + manifest = new Manifest(); + + Attributes attributes = manifest.getMainAttributes(); + attributes.put(Name.MANIFEST_VERSION, WEAVER_MANIFEST_VERSION); + attributes.put(CREATED_BY, WEAVER_CREATED_BY); + } + + return manifest; + } + + // ---- weaving + + // FOR TESTING + public Collection weave(File file) throws IOException { + OutputStream os = FileUtil.makeOutputStream(file); + this.zipOutputStream = new ZipOutputStream(os); + prepareForWeave(); + Collection c = weave(new IClassFileProvider() { + + public boolean isApplyAtAspectJMungersOnly() { + return false; + } + + public Iterator getClassFileIterator() { + return addedClasses.iterator(); + } + + public IWeaveRequestor getRequestor() { + return new IWeaveRequestor() { + public void acceptResult(IUnwovenClassFile result) { + try { + writeZipEntry(result.getFilename(), result.getBytes()); + } catch (IOException ex) { + } + } + + public void processingReweavableState() { + } + + public void addingTypeMungers() { + } + + public void weavingAspects() { + } + + public void weavingClasses() { + } + + public void weaveCompleted() { + } + }; + } + }); + // /* BUG 40943*/ + // dumpResourcesToOutJar(); + zipOutputStream.close(); // this flushes and closes the acutal file + return c; + } + + private Set candidatesForRemoval = null; + + // variation of "weave" that sources class files from an external source. + public Collection weave(IClassFileProvider input) throws IOException { + if (trace.isTraceEnabled()) { + trace.enter("weave", this, input); + } + ContextToken weaveToken = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.WEAVING, ""); + Collection wovenClassNames = new ArrayList(); + IWeaveRequestor requestor = input.getRequestor(); + + if (world.getModel() != null && world.isMinimalModel()) { + candidatesForRemoval = new HashSet(); + } + if (world.getModel() != null && !isBatchWeave) { + AsmManager manager = world.getModelAsAsmManager(); + for (Iterator i = input.getClassFileIterator(); i.hasNext();) { + UnwovenClassFile classFile = i.next(); + // remove all relationships where this file being woven is + // the target of the relationship + manager.removeRelationshipsTargettingThisType(classFile.getClassName()); + } + } + + // Go through the types and ensure any 'damaged' during compile time are + // repaired prior to weaving + for (Iterator i = input.getClassFileIterator(); i.hasNext();) { + UnwovenClassFile classFile = i.next(); + if (classFile.shouldBeWoven()) { + String className = classFile.getClassName(); + ResolvedType theType = world.resolve(className); + if (theType != null) { + theType.ensureConsistent(); + } + } + } + + // special case for AtAspectJMungerOnly - see #113587 + if (input.isApplyAtAspectJMungersOnly()) { + ContextToken atAspectJMungersOnly = CompilationAndWeavingContext.enteringPhase( + CompilationAndWeavingContext.PROCESSING_ATASPECTJTYPE_MUNGERS_ONLY, ""); + requestor.weavingAspects(); + // ContextToken aspectToken = + CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.WEAVING_ASPECTS, ""); + for (Iterator i = input.getClassFileIterator(); i.hasNext();) { + UnwovenClassFile classFile = i.next(); + if (classFile.shouldBeWoven()) { + String className = classFile.getClassName(); + ResolvedType theType = world.resolve(className); + if (theType.isAnnotationStyleAspect()) { + BcelObjectType classType = BcelWorld.getBcelObjectType(theType); + if (classType == null) { + throw new BCException("Can't find bcel delegate for " + className + " type=" + theType.getClass()); + } + LazyClassGen clazz = classType.getLazyClassGen(); + BcelPerClauseAspectAdder selfMunger = new BcelPerClauseAspectAdder(theType, theType.getPerClause().getKind()); + selfMunger.forceMunge(clazz, true); + classType.finishedWith(); + UnwovenClassFile[] newClasses = getClassFilesFor(clazz); + for (int news = 0; news < newClasses.length; news++) { + requestor.acceptResult(newClasses[news]); + } + wovenClassNames.add(classFile.getClassName()); + } + } + } + requestor.weaveCompleted(); + CompilationAndWeavingContext.leavingPhase(atAspectJMungersOnly); + return wovenClassNames; + } + + requestor.processingReweavableState(); + ContextToken reweaveToken = CompilationAndWeavingContext.enteringPhase( + CompilationAndWeavingContext.PROCESSING_REWEAVABLE_STATE, ""); + prepareToProcessReweavableState(); + // clear all state from files we'll be reweaving + for (Iterator i = input.getClassFileIterator(); i.hasNext();) { + UnwovenClassFile classFile = i.next(); + if (classFile.shouldBeWoven()) { + String className = classFile.getClassName(); + BcelObjectType classType = getClassType(className); + + // null return from getClassType() means the delegate is an eclipse + // source type - so + // there *cant* be any reweavable state... (he bravely claimed...) + if (classType != null) { + ContextToken tok = CompilationAndWeavingContext.enteringPhase( + CompilationAndWeavingContext.PROCESSING_REWEAVABLE_STATE, className); + processReweavableStateIfPresent(className, classType); + CompilationAndWeavingContext.leavingPhase(tok); + } + } + } + + CompilationAndWeavingContext.leavingPhase(reweaveToken); + + ContextToken typeMungingToken = CompilationAndWeavingContext.enteringPhase( + CompilationAndWeavingContext.PROCESSING_TYPE_MUNGERS, ""); + requestor.addingTypeMungers(); + + // We process type mungers in two groups, first mungers that change the + // type + // hierarchy, then 'normal' ITD type mungers. + + // Process the types in a predictable order (rather than the order + // encountered). + // For class A, the order is superclasses of A then superinterfaces of A + // (and this mechanism is applied recursively) + List typesToProcess = new ArrayList(); + for (Iterator iter = input.getClassFileIterator(); iter.hasNext();) { + UnwovenClassFile clf = iter.next(); + if (clf.shouldBeWoven()) { + typesToProcess.add(clf.getClassName()); + } + } + while (typesToProcess.size() > 0) { + weaveParentsFor(typesToProcess, typesToProcess.get(0), null); + } + + for (Iterator i = input.getClassFileIterator(); i.hasNext();) { + UnwovenClassFile classFile = i.next(); + if (classFile.shouldBeWoven()) { + String className = classFile.getClassName(); + addNormalTypeMungers(className); + } + } + + CompilationAndWeavingContext.leavingPhase(typeMungingToken); + + requestor.weavingAspects(); + ContextToken aspectToken = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.WEAVING_ASPECTS, ""); + // first weave into aspects + for (Iterator i = input.getClassFileIterator(); i.hasNext();) { + UnwovenClassFile classFile = i.next(); + if (classFile.shouldBeWoven()) { + String className = classFile.getClassName(); + ResolvedType theType = world.resolve(className); + if (theType.isAspect()) { + BcelObjectType classType = BcelWorld.getBcelObjectType(theType); + if (classType == null) { + + // Sometimes.. if the Bcel Delegate couldn't be found then a + // problem occurred at compile time - on + // a previous compiler run. In this case I assert the + // delegate will still be an EclipseSourceType + // and we can ignore the problem here (the original compile + // error will be reported again from + // the eclipse source type) - pr113531 + ReferenceTypeDelegate theDelegate = ((ReferenceType) theType).getDelegate(); + if (theDelegate.getClass().getName().endsWith("EclipseSourceType")) { + continue; + } + + throw new BCException("Can't find bcel delegate for " + className + " type=" + theType.getClass()); + } + weaveAndNotify(classFile, classType, requestor); + wovenClassNames.add(className); + } + } + } + + CompilationAndWeavingContext.leavingPhase(aspectToken); + + requestor.weavingClasses(); + ContextToken classToken = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.WEAVING_CLASSES, ""); + // then weave into non-aspects + for (Iterator i = input.getClassFileIterator(); i.hasNext();) { + UnwovenClassFile classFile = i.next(); + if (classFile.shouldBeWoven()) { + String className = classFile.getClassName(); + ResolvedType theType = world.resolve(className); + if (!theType.isAspect()) { + BcelObjectType classType = BcelWorld.getBcelObjectType(theType); + if (classType == null) { + + // bug 119882 - see above comment for bug 113531 + ReferenceTypeDelegate theDelegate = ((ReferenceType) theType).getDelegate(); + + // TODO urgh - put a method on the interface to check this, + // string compare is hideous + if (theDelegate.getClass().getName().endsWith("EclipseSourceType")) { + continue; + } + + throw new BCException("Can't find bcel delegate for " + className + " type=" + theType.getClass()); + } + weaveAndNotify(classFile, classType, requestor); + wovenClassNames.add(className); + } + } + } + CompilationAndWeavingContext.leavingPhase(classToken); + + addedClasses.clear(); + deletedTypenames.clear(); + + requestor.weaveCompleted(); + CompilationAndWeavingContext.leavingPhase(weaveToken); + if (trace.isTraceEnabled()) { + trace.exit("weave", wovenClassNames); + } + if (world.getModel() != null && world.isMinimalModel()) { + candidatesForRemoval.clear(); + } + return wovenClassNames; + } + + public void allWeavingComplete() { + warnOnUnmatchedAdvice(); + } + + /** + * In 1.5 mode and with XLint:adviceDidNotMatch enabled, put out messages for any mungers that did not match anything. + */ + private void warnOnUnmatchedAdvice() { + + class AdviceLocation { + private final int lineNo; + private final UnresolvedType inAspect; + + public AdviceLocation(BcelAdvice advice) { + this.lineNo = advice.getSourceLocation().getLine(); + this.inAspect = advice.getDeclaringAspect(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof AdviceLocation)) { + return false; + } + AdviceLocation other = (AdviceLocation) obj; + if (this.lineNo != other.lineNo) { + return false; + } + if (!this.inAspect.equals(other.inAspect)) { + return false; + } + return true; + } + + @Override + public int hashCode() { + return 37 + 17 * lineNo + 17 * inAspect.hashCode(); + } + } + + // FIXME asc Should be factored out into Xlint code and done + // automatically for all xlint messages, ideally. + // if a piece of advice hasn't matched anywhere and we are in -1.5 mode, + // put out a warning + if (world.isInJava5Mode() && world.getLint().adviceDidNotMatch.isEnabled()) { + List l = world.getCrosscuttingMembersSet().getShadowMungers(); + Set alreadyWarnedLocations = new HashSet(); + + for (Iterator iter = l.iterator(); iter.hasNext();) { + ShadowMunger element = (ShadowMunger) iter.next(); + // This will stop us incorrectly reporting deow checkers: + if (element instanceof BcelAdvice) { + BcelAdvice ba = (BcelAdvice) element; + if (ba.getKind() == AdviceKind.CflowEntry || ba.getKind() == AdviceKind.CflowBelowEntry) { + continue; + } + if (!ba.hasMatchedSomething()) { + // Because we implement some features of AJ itself by + // creating our own kind of mungers, you sometimes + // find that ba.getSignature() is not a BcelMethod - for + // example it might be a cflow entry munger. + if (ba.getSignature() != null) { + // check we haven't already warned on this advice and line + // (cflow creates multiple mungers for the same advice) + AdviceLocation loc = new AdviceLocation(ba); + if (alreadyWarnedLocations.contains(loc)) { + continue; + } else { + alreadyWarnedLocations.add(loc); + } + + if (!(ba.getSignature() instanceof BcelMethod) + || !Utility.isSuppressing(ba.getSignature(), "adviceDidNotMatch")) { + world.getLint().adviceDidNotMatch.signal(ba.getDeclaringAspect().toString(), new SourceLocation( + element.getSourceLocation().getSourceFile(), element.getSourceLocation().getLine())); + } + } + } + } + } + } + } + + /** + * 'typeToWeave' is one from the 'typesForWeaving' list. This routine ensures we process supertypes (classes/interfaces) of + * 'typeToWeave' that are in the 'typesForWeaving' list before 'typeToWeave' itself. 'typesToWeave' is then removed from the + * 'typesForWeaving' list. + * + * Note: Future gotcha in here ... when supplying partial hierarchies, this algorithm may break down. If you have a hierarchy + * A>B>C and only give A and C to the weaver, it may choose to weave them in either order - but you'll probably have other + * problems if you are supplying partial hierarchies like that ! + */ + private void weaveParentsFor(List typesForWeaving, String typeToWeave, ResolvedType resolvedTypeToWeave) { + if (resolvedTypeToWeave == null) { + // resolve it if the caller could not pass in the resolved type + resolvedTypeToWeave = world.resolve(typeToWeave); + } + ResolvedType superclassType = resolvedTypeToWeave.getSuperclass(); + String superclassTypename = (superclassType == null ? null : superclassType.getName()); + + // PR336654 added the 'typesForWeaving.contains(superclassTypename)' clause. + // Without it we can delete all type mungers on the parents and yet we only + // add back in the declare parents related ones, not the regular ITDs. + if (superclassType != null && !superclassType.isTypeHierarchyComplete() && superclassType.isExposedToWeaver() + && typesForWeaving.contains(superclassTypename)) { + weaveParentsFor(typesForWeaving, superclassTypename, superclassType); + } + + ResolvedType[] interfaceTypes = resolvedTypeToWeave.getDeclaredInterfaces(); + for (ResolvedType resolvedSuperInterface : interfaceTypes) { + if (!resolvedSuperInterface.isTypeHierarchyComplete()) { + String interfaceTypename = resolvedSuperInterface.getName(); + if (resolvedSuperInterface.isExposedToWeaver()) { // typesForWeaving.contains(interfaceTypename)) { + weaveParentsFor(typesForWeaving, interfaceTypename, resolvedSuperInterface); + } + } + } + ContextToken tok = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.PROCESSING_DECLARE_PARENTS, + resolvedTypeToWeave.getName()); + // If A was processed before B (and was declared 'class A implements B') then there is no need to complete B again, it + // will have been done whilst processing A. + if (!resolvedTypeToWeave.isTypeHierarchyComplete()) { + weaveParentTypeMungers(resolvedTypeToWeave); + } + CompilationAndWeavingContext.leavingPhase(tok); + typesForWeaving.remove(typeToWeave); + resolvedTypeToWeave.tagAsTypeHierarchyComplete(); + } + + public void prepareToProcessReweavableState() { + } + + public void processReweavableStateIfPresent(String className, BcelObjectType classType) { + // If the class is marked reweavable, check any aspects around when it + // was built are in this world + WeaverStateInfo wsi = classType.getWeaverState(); + // System.out.println(">> processReweavableStateIfPresent " + className + " wsi=" + wsi); + if (wsi != null && wsi.isReweavable()) { // Check all necessary types + // are around! + world.showMessage(IMessage.INFO, WeaverMessages.format(WeaverMessages.PROCESSING_REWEAVABLE, className, classType + .getSourceLocation().getSourceFile()), null, null); + Set aspectsPreviouslyInWorld = wsi.getAspectsAffectingType(); + // keep track of them just to ensure unique missing aspect error + // reporting + Set alreadyConfirmedReweavableState = new HashSet(); + for (String requiredTypeSignature : aspectsPreviouslyInWorld) { + // for (Iterator iter = aspectsPreviouslyInWorld.iterator(); iter.hasNext();) { + // String requiredTypeName = (String) iter.next(); + if (!alreadyConfirmedReweavableState.contains(requiredTypeSignature)) { + ResolvedType rtx = world.resolve(UnresolvedType.forSignature(requiredTypeSignature), true); + boolean exists = !rtx.isMissing(); + if (!world.isOverWeaving()) { + if (!exists) { + world.getLint().missingAspectForReweaving.signal(new String[] { rtx.getName(), className }, + classType.getSourceLocation(), null); + // world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.MISSING_REWEAVABLE_TYPE, + // requiredTypeName, className), classType.getSourceLocation(), null); + } else { + // weaved in aspect that are not declared in aop.xml + // trigger an error for now + // may cause headhache for LTW and packaged lib + // without aop.xml in + // see #104218 + if (!xcutSet.containsAspect(rtx)) { + world.showMessage(IMessage.ERROR, WeaverMessages.format( + WeaverMessages.REWEAVABLE_ASPECT_NOT_REGISTERED, rtx.getName(), className), null, null); + } else if (!world.getMessageHandler().isIgnoring(IMessage.INFO)) { + world.showMessage(IMessage.INFO, WeaverMessages.format(WeaverMessages.VERIFIED_REWEAVABLE_TYPE, + rtx.getName(), rtx.getSourceLocation().getSourceFile()), null, null); + } + alreadyConfirmedReweavableState.add(requiredTypeSignature); + } + } + } + } + // old: + // classType.setJavaClass(Utility.makeJavaClass(classType.getJavaClass + // ().getFileName(), wsi.getUnwovenClassFileData())); + // new: reweavable default with clever diff + if (!world.isOverWeaving()) { + byte[] ucfd = wsi.getUnwovenClassFileData(); + if (ucfd.length == 0) { + // Size 0 indicates the class was previously overwoven, so you need to be overweaving now! + world.getMessageHandler().handleMessage( + MessageUtil.error( + WeaverMessages.format(WeaverMessages.MUST_KEEP_OVERWEAVING_ONCE_START, + className))); +// onType.getName(), annoX.getTypeName(), annoX.getValidTargets()), +// decA.getSourceLocation())); + } else { + byte[] bytes = wsi.getUnwovenClassFileData(classType.getJavaClass().getBytes()); + JavaClass newJavaClass = Utility.makeJavaClass(classType.getJavaClass().getFileName(), bytes); + classType.setJavaClass(newJavaClass, true); + classType.getResolvedTypeX().ensureConsistent(); + } + } + // } else { + // classType.resetState(); + } + } + + private void weaveAndNotify(UnwovenClassFile classFile, BcelObjectType classType, IWeaveRequestor requestor) throws IOException { + trace.enter("weaveAndNotify", this, new Object[] { classFile, classType, requestor }); + + ContextToken tok = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.WEAVING_TYPE, classType + .getResolvedTypeX().getName()); + LazyClassGen clazz = weaveWithoutDump(classFile, classType); + classType.finishedWith(); + // clazz is null if the classfile was unchanged by weaving... + if (clazz != null) { + UnwovenClassFile[] newClasses = getClassFilesFor(clazz); + // OPTIMIZE can we avoid using the string name at all in + // UnwovenClassFile instances? + // Copy the char[] across as it means the + // WeaverAdapter.removeFromMap() can be fast! + if (newClasses[0].getClassName().equals(classFile.getClassName())) { + newClasses[0].setClassNameAsChars(classFile.getClassNameAsChars()); + } + for (int i = 0; i < newClasses.length; i++) { + requestor.acceptResult(newClasses[i]); + } + } else { + requestor.acceptResult(classFile); + } + classType.weavingCompleted(); + CompilationAndWeavingContext.leavingPhase(tok); + + trace.exit("weaveAndNotify"); + } + + /** + * helper method - will return NULL if the underlying delegate is an EclipseSourceType and not a BcelObjectType + */ + public BcelObjectType getClassType(String forClass) { + return BcelWorld.getBcelObjectType(world.resolve(forClass)); + } + + public void addParentTypeMungers(String typeName) { + weaveParentTypeMungers(world.resolve(typeName)); + } + + public void addNormalTypeMungers(String typeName) { + weaveNormalTypeMungers(world.resolve(typeName)); + } + + public UnwovenClassFile[] getClassFilesFor(LazyClassGen clazz) { + List childClasses = clazz.getChildClasses(world); + UnwovenClassFile[] ret = new UnwovenClassFile[1 + childClasses.size()]; + ret[0] = new UnwovenClassFile(clazz.getFileName(), clazz.getClassName(), clazz.getJavaClassBytesIncludingReweavable(world)); + int index = 1; + for (Iterator iter = childClasses.iterator(); iter.hasNext();) { + UnwovenClassFile.ChildClass element = iter.next(); + UnwovenClassFile childClass = new UnwovenClassFile(clazz.getFileName() + "$" + element.name, element.bytes); + ret[index++] = childClass; + } + return ret; + } + + /** + * Weaves new parents and annotations onto a type ("declare parents" and "declare @type") + * + * Algorithm: 1. First pass, do parents then do annotations. During this pass record: - any parent mungers that don't match but + * have a non-wild annotation type pattern - any annotation mungers that don't match 2. Multiple subsequent passes which go over + * the munger lists constructed in the first pass, repeatedly applying them until nothing changes. FIXME asc confirm that + * algorithm is optimal ?? + */ + public void weaveParentTypeMungers(ResolvedType onType) { + if (onType.isRawType() || onType.isParameterizedType()) { + onType = onType.getGenericType(); + } + onType.clearInterTypeMungers(); + + List decpToRepeat = new ArrayList(); + + boolean aParentChangeOccurred = false; + boolean anAnnotationChangeOccurred = false; + // First pass - apply all decp mungers + for (DeclareParents decp : declareParentsList) { + boolean typeChanged = applyDeclareParents(decp, onType); + if (typeChanged) { + aParentChangeOccurred = true; + } else { + decpToRepeat.add(decp); + } + } + + // Still first pass - apply all dec @type mungers + for (DeclareAnnotation decA : xcutSet.getDeclareAnnotationOnTypes()) { + boolean typeChanged = applyDeclareAtType(decA, onType, true); + if (typeChanged) { + anAnnotationChangeOccurred = true; + } + } + + while ((aParentChangeOccurred || anAnnotationChangeOccurred) && !decpToRepeat.isEmpty()) { + anAnnotationChangeOccurred = aParentChangeOccurred = false; + List decpToRepeatNextTime = new ArrayList(); + for (Iterator iter = decpToRepeat.iterator(); iter.hasNext();) { + DeclareParents decp = iter.next(); + boolean typeChanged = applyDeclareParents(decp, onType); + if (typeChanged) { + aParentChangeOccurred = true; + } else { + decpToRepeatNextTime.add(decp); + } + } + + for (DeclareAnnotation decA : xcutSet.getDeclareAnnotationOnTypes()) { + boolean typeChanged = applyDeclareAtType(decA, onType, false); + if (typeChanged) { + anAnnotationChangeOccurred = true; + } + } + decpToRepeat = decpToRepeatNextTime; + } + } + + /** + * Apply a declare @type - return true if we change the type + */ + private boolean applyDeclareAtType(DeclareAnnotation decA, ResolvedType onType, boolean reportProblems) { + boolean didSomething = false; + if (decA.matches(onType)) { + AnnotationAJ theAnnotation = decA.getAnnotation(); + // can be null for broken code! + if (theAnnotation == null) { + return false; + } + if (onType.hasAnnotation(theAnnotation.getType())) { + // Could put out a lint here for an already annotated type ... + // if (reportProblems) { + // world.getLint().elementAlreadyAnnotated.signal( + // new + // String[]{onType.toString(),decA.getAnnotationTypeX().toString + // ()}, + // onType.getSourceLocation(),new + // ISourceLocation[]{decA.getSourceLocation()}); + // } + return false; + } + + AnnotationAJ annoX = decA.getAnnotation(); + + // check the annotation is suitable for the target + boolean problemReported = verifyTargetIsOK(decA, onType, annoX, reportProblems); + + if (!problemReported) { + AsmRelationshipProvider.addDeclareAnnotationRelationship(world.getModelAsAsmManager(), decA.getSourceLocation(), + onType.getSourceLocation(), false); + // TAG: WeavingMessage + if (!getWorld().getMessageHandler().isIgnoring(IMessage.WEAVEINFO)) { + getWorld().getMessageHandler().handleMessage( + WeaveMessage.constructWeavingMessage( + WeaveMessage.WEAVEMESSAGE_ANNOTATES, + new String[] { onType.toString(), Utility.beautifyLocation(onType.getSourceLocation()), + decA.getAnnotationString(), "type", decA.getAspect().toString(), + Utility.beautifyLocation(decA.getSourceLocation()) })); + } + didSomething = true; + ResolvedTypeMunger newAnnotationTM = new AnnotationOnTypeMunger(annoX); + newAnnotationTM.setSourceLocation(decA.getSourceLocation()); + onType.addInterTypeMunger(new BcelTypeMunger(newAnnotationTM, decA.getAspect().resolve(world)), false); + decA.copyAnnotationTo(onType); + } + } + return didSomething; + } + + /** + * Checks for an @target() on the annotation and if found ensures it allows the annotation to be attached to the target type + * that matched. + */ + private boolean verifyTargetIsOK(DeclareAnnotation decA, ResolvedType onType, AnnotationAJ annoX, boolean outputProblems) { + boolean problemReported = false; + if (annoX.specifiesTarget()) { + if ((onType.isAnnotation() && !annoX.allowedOnAnnotationType()) || (!annoX.allowedOnRegularType())) { + if (outputProblems) { + if (decA.isExactPattern()) { + world.getMessageHandler().handleMessage( + MessageUtil.error( + WeaverMessages.format(WeaverMessages.INCORRECT_TARGET_FOR_DECLARE_ANNOTATION, + onType.getName(), annoX.getTypeName(), annoX.getValidTargets()), + decA.getSourceLocation())); + } else { + if (world.getLint().invalidTargetForAnnotation.isEnabled()) { + world.getLint().invalidTargetForAnnotation.signal(new String[] { onType.getName(), annoX.getTypeName(), + annoX.getValidTargets() }, decA.getSourceLocation(), + new ISourceLocation[] { onType.getSourceLocation() }); + } + } + } + problemReported = true; + } + } + return problemReported; + } + + /** + * Apply a single declare parents - return true if we change the type + */ + private boolean applyDeclareParents(DeclareParents p, ResolvedType onType) { + boolean didSomething = false; + List newParents = p.findMatchingNewParents(onType, true); + if (!newParents.isEmpty()) { + didSomething = true; + BcelWorld.getBcelObjectType(onType); + // System.err.println("need to do declare parents for: " + onType); + for (ResolvedType newParent : newParents) { + // We set it here so that the imminent matching for ITDs can + // succeed - we still haven't done the necessary changes to the class file + // itself (like transform super calls) - that is done in + // BcelTypeMunger.mungeNewParent() + // classType.addParent(newParent); + onType.addParent(newParent); + NewParentTypeMunger newParentMunger = new NewParentTypeMunger(newParent, p.getDeclaringType()); + if (p.isMixin()) { + newParentMunger.setIsMixin(true); + } + newParentMunger.setSourceLocation(p.getSourceLocation()); + onType.addInterTypeMunger(new BcelTypeMunger(newParentMunger, xcutSet.findAspectDeclaringParents(p)), false); + } + } + return didSomething; + } + + public void weaveNormalTypeMungers(ResolvedType onType) { + ContextToken tok = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.PROCESSING_TYPE_MUNGERS, + onType.getName()); + if (onType.isRawType() || onType.isParameterizedType()) { + onType = onType.getGenericType(); + } + for (ConcreteTypeMunger m : typeMungerList) { + if (!m.isLateMunger() && m.matches(onType)) { + onType.addInterTypeMunger(m, false); + } + } + CompilationAndWeavingContext.leavingPhase(tok); + } + + // exposed for ClassLoader dynamic weaving + public LazyClassGen weaveWithoutDump(UnwovenClassFile classFile, BcelObjectType classType) throws IOException { + return weave(classFile, classType, false); + } + + // FOR TESTING + LazyClassGen weave(UnwovenClassFile classFile, BcelObjectType classType) throws IOException { + LazyClassGen ret = weave(classFile, classType, true); + return ret; + } + + private LazyClassGen weave(UnwovenClassFile classFile, BcelObjectType classType, boolean dump) throws IOException { + + try { + if (classType.isSynthetic()) { // Don't touch synthetic classes + if (dump) { + dumpUnchanged(classFile); + } + return null; + } + ReferenceType resolvedClassType = classType.getResolvedTypeX(); + + if (world.isXmlConfigured() && world.getXmlConfiguration().excludesType(resolvedClassType)) { + if (!world.getMessageHandler().isIgnoring(IMessage.INFO)) { + world.getMessageHandler().handleMessage( + MessageUtil.info("Type '" + resolvedClassType.getName() + + "' not woven due to exclusion via XML weaver exclude section")); + + } + if (dump) { + dumpUnchanged(classFile); + } + return null; + } + + List shadowMungers = fastMatch(shadowMungerList, resolvedClassType); + List typeMungers = classType.getResolvedTypeX().getInterTypeMungers(); + + resolvedClassType.checkInterTypeMungers(); + + // Decide if we need to do actual weaving for this class + boolean mightNeedToWeave = shadowMungers.size() > 0 || typeMungers.size() > 0 || classType.isAspect() + || world.getDeclareAnnotationOnMethods().size() > 0 || world.getDeclareAnnotationOnFields().size() > 0; + + // May need bridge methods if on 1.5 and something in our hierarchy is + // affected by ITDs + boolean mightNeedBridgeMethods = world.isInJava5Mode() && !classType.isInterface() + && resolvedClassType.getInterTypeMungersIncludingSupers().size() > 0; + + LazyClassGen clazz = null; + if (mightNeedToWeave || mightNeedBridgeMethods) { + clazz = classType.getLazyClassGen(); + // System.err.println("got lazy gen: " + clazz + ", " + + // clazz.getWeaverState()); + try { + boolean isChanged = false; + + if (mightNeedToWeave) { + isChanged = BcelClassWeaver.weave(world, clazz, shadowMungers, typeMungers, lateTypeMungerList, + inReweavableMode); + } + + checkDeclareTypeErrorOrWarning(world, classType); + + if (mightNeedBridgeMethods) { + isChanged = BcelClassWeaver.calculateAnyRequiredBridgeMethods(world, clazz) || isChanged; + } + + if (isChanged) { + if (dump) { + dump(classFile, clazz); + } + return clazz; + } + } catch (RuntimeException re) { + String classDebugInfo = null; + try { + classDebugInfo = clazz.toLongString(); + } catch (Throwable e) { + new RuntimeException("Crashed whilst crashing with this exception: " + e, e).printStackTrace(); + // recover from crash whilst producing debug string + classDebugInfo = clazz.getClassName(); + } + String messageText = "trouble in: \n" + classDebugInfo; + getWorld().getMessageHandler().handleMessage(new Message(messageText, IMessage.ABORT, re, null)); + } catch (Error re) { + String classDebugInfo = null; + try { + classDebugInfo = clazz.toLongString(); + } catch (OutOfMemoryError oome) { + System.err.println("Ran out of memory creating debug info for an error"); + re.printStackTrace(System.err); + // recover from crash whilst producing debug string + classDebugInfo = clazz.getClassName(); + } catch (Throwable e) { + // recover from crash whilst producing debug string + classDebugInfo = clazz.getClassName(); + } + String messageText = "trouble in: \n" + classDebugInfo; + getWorld().getMessageHandler().handleMessage(new Message(messageText, IMessage.ABORT, re, null)); + } + } else { + checkDeclareTypeErrorOrWarning(world, classType); + } + // this is very odd return behavior trying to keep everyone happy + + // can we remove it from the model now? we know it contains no relationship endpoints... + AsmManager model = world.getModelAsAsmManager(); + if (world.isMinimalModel() && model != null && !classType.isAspect()) { + AspectJElementHierarchy hierarchy = (AspectJElementHierarchy) model.getHierarchy(); + String pkgname = classType.getResolvedTypeX().getPackageName(); + String tname = classType.getResolvedTypeX().getSimpleBaseName(); + IProgramElement typeElement = hierarchy.findElementForType(pkgname, tname); + if (typeElement != null && hasInnerType(typeElement)) { + // Cannot remove it right now (has inner type), schedule it + // for possible deletion later if all inner types are + // removed + candidatesForRemoval.add(typeElement); + } + if (typeElement != null && !hasInnerType(typeElement)) { + IProgramElement parent = typeElement.getParent(); + // parent may have children: PACKAGE DECL, IMPORT-REFERENCE, TYPE_DECL + if (parent != null) { + // if it was the only type we should probably remove + // the others too. + parent.removeChild(typeElement); + if (parent.getKind().isSourceFile()) { + removeSourceFileIfNoMoreTypeDeclarationsInside(hierarchy, typeElement, parent); + } else { + hierarchy.forget(null, typeElement); + // At this point, the child has been removed. We + // should now check if the parent is in our + // 'candidatesForRemoval' set. If it is then that + // means we were going to remove it but it had a + // child. Now we can check if it still has a child - + // if it doesn't it can also be removed! + + walkUpRemovingEmptyTypesAndPossiblyEmptySourceFile(hierarchy, tname, parent); + } + + } + } + } + + if (dump) { + dumpUnchanged(classFile); + return clazz; + } else { + // ATAJ: the class was not weaved, but since it gets there early it + // may have new generated inner classes + // attached to it to support LTW perX aspectOf support (see + // BcelPerClauseAspectAdder) + // that aggressively defines the inner $mayHaveAspect + // interface. + if (clazz != null && !clazz.getChildClasses(world).isEmpty()) { + return clazz; + } + return null; + } + } finally { + world.demote(); + } + } + + private void walkUpRemovingEmptyTypesAndPossiblyEmptySourceFile(AspectJElementHierarchy hierarchy, String tname, + IProgramElement typeThatHasChildRemoved) { + // typeThatHasChildRemoved might be a source file, type or a method/ctor + // - for a method/ctor find the type/sourcefile + while (typeThatHasChildRemoved != null + && !(typeThatHasChildRemoved.getKind().isType() || typeThatHasChildRemoved.getKind().isSourceFile())) { + // this will take us 'up' through methods that contain anonymous + // inner classes + typeThatHasChildRemoved = typeThatHasChildRemoved.getParent(); + } + // now typeThatHasChildRemoved points to the type or sourcefile that has + // had something removed + if (candidatesForRemoval.contains(typeThatHasChildRemoved) && !hasInnerType(typeThatHasChildRemoved)) { + // now we can get rid of it + IProgramElement parent = typeThatHasChildRemoved.getParent(); + if (parent != null) { + parent.removeChild(typeThatHasChildRemoved); + candidatesForRemoval.remove(typeThatHasChildRemoved); + if (parent.getKind().isSourceFile()) { + removeSourceFileIfNoMoreTypeDeclarationsInside(hierarchy, typeThatHasChildRemoved, parent); + // System.out.println("Removed on second pass: " + + // typeThatHasChildRemoved.getName()); + } else { + // System.out.println("On later pass, parent of type " + + // typeThatHasChildRemoved.getName() + // + " was found not to be a sourcefile, recursing up..."); + walkUpRemovingEmptyTypesAndPossiblyEmptySourceFile(hierarchy, tname, parent); + } + } + } + } + + private void removeSourceFileIfNoMoreTypeDeclarationsInside(AspectJElementHierarchy hierarchy, IProgramElement typeElement, + IProgramElement sourceFileNode) { + IProgramElement compilationUnit = sourceFileNode; + boolean anyOtherTypeDeclarations = false; + for (IProgramElement child : compilationUnit.getChildren()) { + IProgramElement.Kind k = child.getKind(); + if (k.isType()) { + anyOtherTypeDeclarations = true; + break; + } + } + // If the compilation unit node contained no + // other types, there is no need to keep it + if (!anyOtherTypeDeclarations) { + IProgramElement cuParent = compilationUnit.getParent(); + if (cuParent != null) { + compilationUnit.setParent(null); + cuParent.removeChild(compilationUnit); + } + // need to update some caches and structures too? + hierarchy.forget(sourceFileNode, typeElement); + } else { + hierarchy.forget(null, typeElement); + } + } + + // ---- writing + + // TODO could be smarter - really only matters if inner type has been woven, but there is a chance we haven't woven it *yet* + private boolean hasInnerType(IProgramElement typeNode) { + for (IProgramElement child : typeNode.getChildren()) { + IProgramElement.Kind kind = child.getKind(); + if (kind.isType()) { + return true; + } + // if (kind == IProgramElement.Kind.ASPECT) { + // return true; + // } + if (kind.isType() || kind == IProgramElement.Kind.METHOD || kind == IProgramElement.Kind.CONSTRUCTOR) { + boolean b = hasInnerType(child); + if (b) { + return b; + } + } + } + return false; + } + + private void checkDeclareTypeErrorOrWarning(BcelWorld world2, BcelObjectType classType) { + List dteows = world.getDeclareTypeEows(); + for (DeclareTypeErrorOrWarning dteow : dteows) { + if (dteow.getTypePattern().matchesStatically(classType.getResolvedTypeX())) { + if (dteow.isError()) { + world.getMessageHandler().handleMessage( + MessageUtil.error(dteow.getMessage(), classType.getResolvedTypeX().getSourceLocation())); + } else { + world.getMessageHandler().handleMessage( + MessageUtil.warn(dteow.getMessage(), classType.getResolvedTypeX().getSourceLocation())); + } + } + } + } + + private void dumpUnchanged(UnwovenClassFile classFile) throws IOException { + if (zipOutputStream != null) { + writeZipEntry(getEntryName(classFile.getJavaClass().getClassName()), classFile.getBytes()); + } else { + classFile.writeUnchangedBytes(); + } + } + + private String getEntryName(String className) { + // XXX what does bcel's getClassName do for inner names + return className.replace('.', '/') + ".class"; + } + + private void dump(UnwovenClassFile classFile, LazyClassGen clazz) throws IOException { + if (zipOutputStream != null) { + String mainClassName = classFile.getJavaClass().getClassName(); + writeZipEntry(getEntryName(mainClassName), clazz.getJavaClass(world).getBytes()); + List childClasses = clazz.getChildClasses(world); + if (!childClasses.isEmpty()) { + for (Iterator i = childClasses.iterator(); i.hasNext();) { + UnwovenClassFile.ChildClass c = i.next(); + writeZipEntry(getEntryName(mainClassName + "$" + c.name), c.bytes); + } + } + } else { + classFile.writeWovenBytes(clazz.getJavaClass(world).getBytes(), clazz.getChildClasses(world)); + } + } + + private void writeZipEntry(String name, byte[] bytes) throws IOException { + ZipEntry newEntry = new ZipEntry(name); // ??? get compression scheme + // right + + zipOutputStream.putNextEntry(newEntry); + zipOutputStream.write(bytes); + zipOutputStream.closeEntry(); + } + + /** + * Perform a fast match of the specified list of shadowmungers against the specified type. A subset of those that might match is + * returned. + * + * @param list list of all shadow mungers that might match + * @param type the target type + * @return a list of shadow mungers that might match with those that cannot (according to fast match rules) removed + */ + private List fastMatch(List list, ResolvedType type) { + if (list == null) { + return Collections.emptyList(); + } + boolean isOverweaving = world.isOverWeaving(); + WeaverStateInfo typeWeaverState = (isOverweaving ? type.getWeaverState() : null); + + // here we do the coarsest grained fast match with no kind constraints + // this will remove all obvious non-matches and see if we need to do any + // weaving + FastMatchInfo info = new FastMatchInfo(type, null, world); + + List result = new ArrayList(); + + if (world.areInfoMessagesEnabled() && world.isTimingEnabled()) { + for (ShadowMunger munger : list) { + if (typeWeaverState != null) { // will only be null if overweaving is ON and there is weaverstate + ResolvedType declaringAspect = munger.getDeclaringType(); + if (typeWeaverState.isAspectAlreadyApplied(declaringAspect)) { + continue; + } + } + Pointcut pointcut = munger.getPointcut(); + long starttime = System.nanoTime(); + FuzzyBoolean fb = pointcut.fastMatch(info); + long endtime = System.nanoTime(); + world.recordFastMatch(pointcut, endtime - starttime); + if (fb.maybeTrue()) { + result.add(munger); + } + } + } else { + for (ShadowMunger munger : list) { + if (typeWeaverState != null) { // will only be null if overweaving is ON and there is weaverstate + ResolvedType declaringAspect = munger.getConcreteAspect();// getDeclaringType(); + if (typeWeaverState.isAspectAlreadyApplied(declaringAspect)) { + continue; + } + } + Pointcut pointcut = munger.getPointcut(); + FuzzyBoolean fb = pointcut.fastMatch(info); + if (fb.maybeTrue()) { + result.add(munger); + } + } + } + return result; + } + + public void setReweavableMode(boolean xNotReweavable) { + inReweavableMode = !xNotReweavable; + WeaverStateInfo.setReweavableModeDefaults(!xNotReweavable, false, true); + } + + public boolean isReweavable() { + return inReweavableMode; + } + + public World getWorld() { + return world; + } + + public void tidyUp() { + if (trace.isTraceEnabled()) { + trace.enter("tidyUp", this); + } + shadowMungerList = null; // setup by prepareForWeave + typeMungerList = null; // setup by prepareForWeave + lateTypeMungerList = null; // setup by prepareForWeave + declareParentsList = null; // setup by prepareForWeave + if (trace.isTraceEnabled()) { + trace.exit("tidyUp"); + } + } + + public void write(CompressingDataOutputStream dos) throws IOException { + xcutSet.write(dos); + } + + // only called for testing + public void setShadowMungers(List shadowMungers) { + shadowMungerList = shadowMungers; + } +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/BcelWeavingSupport.java b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelWeavingSupport.java new file mode 100644 index 000000000..120df80c2 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelWeavingSupport.java @@ -0,0 +1,71 @@ +/* ******************************************************************* + * Copyright (c) 2008 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement initial implementation + * ******************************************************************/ +package org.aspectj.weaver.bcel; + +import org.aspectj.weaver.Advice; +import org.aspectj.weaver.AjAttribute; +import org.aspectj.weaver.ConcreteTypeMunger; +import org.aspectj.weaver.IWeavingSupport; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.ResolvedTypeMunger; +import org.aspectj.weaver.ast.Var; +import org.aspectj.weaver.patterns.PerClause; +import org.aspectj.weaver.patterns.Pointcut; + +/** + * Bcel implementation of the weaving support required in a BcelWorld which will actually modify bytecode. + * + * @author Andy Clement + */ +public class BcelWeavingSupport implements IWeavingSupport { + + public Advice createAdviceMunger(AjAttribute.AdviceAttribute attribute, Pointcut pointcut, Member signature, + ResolvedType concreteAspect) { + // System.err.println("concrete advice: " + signature + " context " + + // sourceContext); + return new BcelAdvice(attribute, pointcut, signature, concreteAspect); + } + + public ConcreteTypeMunger makeCflowStackFieldAdder(ResolvedMember cflowField) { + return new BcelCflowStackFieldAdder(cflowField); + } + + public ConcreteTypeMunger makeCflowCounterFieldAdder(ResolvedMember cflowField) { + return new BcelCflowCounterFieldAdder(cflowField); + } + + /** + * Register a munger for perclause @AJ aspect so that we add aspectOf(..) to them as needed + * + * @param aspect + * @param kind + * @return munger + */ + public ConcreteTypeMunger makePerClauseAspect(ResolvedType aspect, PerClause.Kind kind) { + return new BcelPerClauseAspectAdder(aspect, kind); + } + + public Var makeCflowAccessVar(ResolvedType formalType, Member cflowField, int arrayIndex) { + return new BcelCflowAccessVar(formalType, cflowField, arrayIndex); + } + + public ConcreteTypeMunger concreteTypeMunger(ResolvedTypeMunger munger, ResolvedType aspectType) { + return new BcelTypeMunger(munger, aspectType); + } + + public ConcreteTypeMunger createAccessForInlineMunger(ResolvedType aspect) { + return new BcelAccessForInlineMunger(aspect); + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/BcelWorld.java b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelWorld.java new file mode 100644 index 000000000..4ade1e125 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/BcelWorld.java @@ -0,0 +1,1329 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * Alexandre Vasseur perClause support for @AJ aspects + * ******************************************************************/ + +package org.aspectj.weaver.bcel; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Modifier; +import java.net.MalformedURLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; + +import org.aspectj.apache.bcel.Constants; +import org.aspectj.apache.bcel.classfile.ClassParser; +import org.aspectj.apache.bcel.classfile.ConstantPool; +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.generic.FieldInstruction; +import org.aspectj.apache.bcel.generic.INVOKEINTERFACE; +import org.aspectj.apache.bcel.generic.Instruction; +import org.aspectj.apache.bcel.generic.InstructionHandle; +import org.aspectj.apache.bcel.generic.InvokeInstruction; +import org.aspectj.apache.bcel.generic.MULTIANEWARRAY; +import org.aspectj.apache.bcel.generic.ObjectType; +import org.aspectj.apache.bcel.generic.Type; +import org.aspectj.apache.bcel.util.ClassLoaderReference; +import org.aspectj.apache.bcel.util.ClassLoaderRepository; +import org.aspectj.apache.bcel.util.ClassPath; +import org.aspectj.apache.bcel.util.NonCachingClassLoaderRepository; +import org.aspectj.apache.bcel.util.Repository; +import org.aspectj.asm.AsmManager; +import org.aspectj.asm.IRelationship; +import org.aspectj.asm.internal.CharOperation; +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.IMessageHandler; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.Message; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.bridge.WeaveMessage; +import org.aspectj.weaver.Advice; +import org.aspectj.weaver.AdviceKind; +import org.aspectj.weaver.AnnotationAJ; +import org.aspectj.weaver.AnnotationOnTypeMunger; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.Checker; +import org.aspectj.weaver.ICrossReferenceHandler; +import org.aspectj.weaver.IWeavingSupport; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.MemberImpl; +import org.aspectj.weaver.MemberKind; +import org.aspectj.weaver.NewParentTypeMunger; +import org.aspectj.weaver.ReferenceType; +import org.aspectj.weaver.ReferenceTypeDelegate; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedMemberImpl; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.ResolvedTypeMunger; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.ShadowMunger; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.World; +import org.aspectj.weaver.loadtime.definition.Definition; +import org.aspectj.weaver.loadtime.definition.DocumentParser; +import org.aspectj.weaver.model.AsmRelationshipProvider; +import org.aspectj.weaver.patterns.DeclareAnnotation; +import org.aspectj.weaver.patterns.DeclareParents; +import org.aspectj.weaver.patterns.ParserException; +import org.aspectj.weaver.patterns.PatternParser; +import org.aspectj.weaver.patterns.TypePattern; +import org.aspectj.weaver.tools.Trace; +import org.aspectj.weaver.tools.TraceFactory; + +public class BcelWorld extends World implements Repository { + + private final ClassPathManager classPath; + protected Repository delegate; + private BcelWeakClassLoaderReference loaderRef; + private final BcelWeavingSupport bcelWeavingSupport = new BcelWeavingSupport(); + private boolean isXmlConfiguredWorld = false; + private WeavingXmlConfig xmlConfiguration; + private List typeDelegateResolvers; + + private static Trace trace = TraceFactory.getTraceFactory().getTrace(BcelWorld.class); + + public BcelWorld() { + this(""); + } + + public BcelWorld(String cp) { + this(makeDefaultClasspath(cp), IMessageHandler.THROW, null); + } + + public IRelationship.Kind determineRelKind(ShadowMunger munger) { + AdviceKind ak = ((Advice) munger).getKind(); + if (ak.getKey() == AdviceKind.Before.getKey()) { + return IRelationship.Kind.ADVICE_BEFORE; + } else if (ak.getKey() == AdviceKind.After.getKey()) { + return IRelationship.Kind.ADVICE_AFTER; + } else if (ak.getKey() == AdviceKind.AfterThrowing.getKey()) { + return IRelationship.Kind.ADVICE_AFTERTHROWING; + } else if (ak.getKey() == AdviceKind.AfterReturning.getKey()) { + return IRelationship.Kind.ADVICE_AFTERRETURNING; + } else if (ak.getKey() == AdviceKind.Around.getKey()) { + return IRelationship.Kind.ADVICE_AROUND; + } else if (ak.getKey() == AdviceKind.CflowEntry.getKey() || ak.getKey() == AdviceKind.CflowBelowEntry.getKey() + || ak.getKey() == AdviceKind.InterInitializer.getKey() || ak.getKey() == AdviceKind.PerCflowEntry.getKey() + || ak.getKey() == AdviceKind.PerCflowBelowEntry.getKey() || ak.getKey() == AdviceKind.PerThisEntry.getKey() + || ak.getKey() == AdviceKind.PerTargetEntry.getKey() || ak.getKey() == AdviceKind.Softener.getKey() + || ak.getKey() == AdviceKind.PerTypeWithinEntry.getKey()) { + // System.err.println("Dont want a message about this: "+ak); + return null; + } + throw new RuntimeException("Shadow.determineRelKind: What the hell is it? " + ak); + } + + @Override + public void reportMatch(ShadowMunger munger, Shadow shadow) { + if (getCrossReferenceHandler() != null) { + getCrossReferenceHandler().addCrossReference(munger.getSourceLocation(), // What is being applied + shadow.getSourceLocation(), // Where is it being applied + determineRelKind(munger).getName(), // What kind of advice? + ((Advice) munger).hasDynamicTests() // Is a runtime test being stuffed in the code? + ); + } + + if (!getMessageHandler().isIgnoring(IMessage.WEAVEINFO)) { + reportWeavingMessage(munger, shadow); + } + + if (getModel() != null) { + AsmRelationshipProvider.addAdvisedRelationship(getModelAsAsmManager(), shadow, munger); + } + } + + /* + * Report a message about the advice weave that has occurred. Some messing about to make it pretty ! This code is just asking + * for an NPE to occur ... + */ + private void reportWeavingMessage(ShadowMunger munger, Shadow shadow) { + Advice advice = (Advice) munger; + AdviceKind aKind = advice.getKind(); + // Only report on interesting advice kinds ... + if (aKind == null || advice.getConcreteAspect() == null) { + // We suspect someone is programmatically driving the weaver + // (e.g. IdWeaveTestCase in the weaver testcases) + return; + } + if (!(aKind.equals(AdviceKind.Before) || aKind.equals(AdviceKind.After) || aKind.equals(AdviceKind.AfterReturning) + || aKind.equals(AdviceKind.AfterThrowing) || aKind.equals(AdviceKind.Around) || aKind.equals(AdviceKind.Softener))) { + return; + } + + // synchronized blocks are implemented with multiple monitor_exit instructions in the bytecode + // (one for normal exit from the method, one for abnormal exit), we only want to tell the user + // once we have advised the end of the sync block, even though under the covers we will have + // woven both exit points + if (shadow.getKind() == Shadow.SynchronizationUnlock) { + if (advice.lastReportedMonitorExitJoinpointLocation == null) { + // this is the first time through, let's continue... + advice.lastReportedMonitorExitJoinpointLocation = shadow.getSourceLocation(); + } else { + if (areTheSame(shadow.getSourceLocation(), advice.lastReportedMonitorExitJoinpointLocation)) { + // Don't report it again! + advice.lastReportedMonitorExitJoinpointLocation = null; + return; + } + // hmmm, this means some kind of nesting is going on, urgh + advice.lastReportedMonitorExitJoinpointLocation = shadow.getSourceLocation(); + } + } + + String description = advice.getKind().toString(); + String advisedType = shadow.getEnclosingType().getName(); + String advisingType = advice.getConcreteAspect().getName(); + Message msg = null; + if (advice.getKind().equals(AdviceKind.Softener)) { + msg = WeaveMessage.constructWeavingMessage(WeaveMessage.WEAVEMESSAGE_SOFTENS, new String[] { advisedType, + beautifyLocation(shadow.getSourceLocation()), advisingType, beautifyLocation(munger.getSourceLocation()) }, + advisedType, advisingType); + } else { + boolean runtimeTest = advice.hasDynamicTests(); + String joinPointDescription = shadow.toString(); + msg = WeaveMessage + .constructWeavingMessage(WeaveMessage.WEAVEMESSAGE_ADVISES, + new String[] { joinPointDescription, advisedType, beautifyLocation(shadow.getSourceLocation()), + description, advisingType, beautifyLocation(munger.getSourceLocation()), + (runtimeTest ? " [with runtime test]" : "") }, advisedType, advisingType); + // Boolean.toString(runtimeTest)}); + } + getMessageHandler().handleMessage(msg); + } + + private boolean areTheSame(ISourceLocation locA, ISourceLocation locB) { + if (locA == null) { + return locB == null; + } + if (locB == null) { + return false; + } + if (locA.getLine() != locB.getLine()) { + return false; + } + File fA = locA.getSourceFile(); + File fB = locA.getSourceFile(); + if (fA == null) { + return fB == null; + } + if (fB == null) { + return false; + } + return fA.getName().equals(fB.getName()); + } + + /* + * Ensure we report a nice source location - particular in the case where the source info is missing (binary weave). + */ + private String beautifyLocation(ISourceLocation isl) { + StringBuffer nice = new StringBuffer(); + if (isl == null || isl.getSourceFile() == null || isl.getSourceFile().getName().indexOf("no debug info available") != -1) { + nice.append("no debug info available"); + } else { + // can't use File.getName() as this fails when a Linux box encounters a path created on Windows and vice-versa + int takeFrom = isl.getSourceFile().getPath().lastIndexOf('/'); + if (takeFrom == -1) { + takeFrom = isl.getSourceFile().getPath().lastIndexOf('\\'); + } + int binary = isl.getSourceFile().getPath().lastIndexOf('!'); + if (binary != -1 && binary < takeFrom) { + // we have been woven by a binary aspect + String pathToBinaryLoc = isl.getSourceFile().getPath().substring(0, binary + 1); + if (pathToBinaryLoc.indexOf(".jar") != -1) { + // only want to add the extra info if we're from a jar file + int lastSlash = pathToBinaryLoc.lastIndexOf('/'); + if (lastSlash == -1) { + lastSlash = pathToBinaryLoc.lastIndexOf('\\'); + } + nice.append(pathToBinaryLoc.substring(lastSlash + 1)); + } + } + nice.append(isl.getSourceFile().getPath().substring(takeFrom + 1)); + if (isl.getLine() != 0) { + nice.append(":").append(isl.getLine()); + } + // if it's a binary file then also want to give the file name + if (isl.getSourceFileName() != null) { + nice.append("(from " + isl.getSourceFileName() + ")"); + } + } + return nice.toString(); + } + + private static List makeDefaultClasspath(String cp) { + List classPath = new ArrayList(); + classPath.addAll(getPathEntries(cp)); + classPath.addAll(getPathEntries(ClassPath.getClassPath())); + return classPath; + + } + + private static List getPathEntries(String s) { + List ret = new ArrayList(); + StringTokenizer tok = new StringTokenizer(s, File.pathSeparator); + while (tok.hasMoreTokens()) { + ret.add(tok.nextToken()); + } + return ret; + } + + public BcelWorld(List classPath, IMessageHandler handler, ICrossReferenceHandler xrefHandler) { + // this.aspectPath = new ClassPathManager(aspectPath, handler); + this.classPath = new ClassPathManager(classPath, handler); + setMessageHandler(handler); + setCrossReferenceHandler(xrefHandler); + // Tell BCEL to use us for resolving any classes + delegate = this; + } + + public BcelWorld(ClassPathManager cpm, IMessageHandler handler, ICrossReferenceHandler xrefHandler) { + classPath = cpm; + setMessageHandler(handler); + setCrossReferenceHandler(xrefHandler); + // Tell BCEL to use us for resolving any classes + delegate = this; + } + + /** + * Build a World from a ClassLoader, for LTW support + * + * @param loader + * @param handler + * @param xrefHandler + */ + public BcelWorld(ClassLoader loader, IMessageHandler handler, ICrossReferenceHandler xrefHandler) { + classPath = null; + loaderRef = new BcelWeakClassLoaderReference(loader); + setMessageHandler(handler); + setCrossReferenceHandler(xrefHandler); + // Tell BCEL to use us for resolving any classes + // delegate = getClassLoaderRepositoryFor(loader); + } + + public void ensureRepositorySetup() { + if (delegate == null) { + delegate = getClassLoaderRepositoryFor(loaderRef); + } + } + + public Repository getClassLoaderRepositoryFor(ClassLoaderReference loader) { + if (bcelRepositoryCaching) { + return new ClassLoaderRepository(loader); + } else { + return new NonCachingClassLoaderRepository(loader); + } + } + + public void addPath(String name) { + classPath.addPath(name, this.getMessageHandler()); + } + + // ---- various interactions with bcel + + public static Type makeBcelType(UnresolvedType type) { + return Type.getType(type.getErasureSignature()); + } + + static Type[] makeBcelTypes(UnresolvedType[] types) { + Type[] ret = new Type[types.length]; + for (int i = 0, len = types.length; i < len; i++) { + ret[i] = makeBcelType(types[i]); + } + return ret; + } + + public static Type[] makeBcelTypes(String[] types) { + if (types == null || types.length==0 ) { + return null; + } + Type[] ret = new Type[types.length]; + for (int i=0, len=types.length; i", new ResolvedType[] { INT }); + } else if (i instanceof MULTIANEWARRAY) { + MULTIANEWARRAY arrayInstruction = (MULTIANEWARRAY) i; + UnresolvedType ut = null; + short dimensions = arrayInstruction.getDimensions(); + ObjectType ot = arrayInstruction.getLoadClassType(cpg); + if (ot != null) { + ut = fromBcel(ot); + ut = UnresolvedType.makeArray(ut, dimensions); + } else { + Type t = arrayInstruction.getType(cpg); + ut = fromBcel(t); + } + ResolvedType[] parms = new ResolvedType[dimensions]; + for (int ii = 0; ii < dimensions; ii++) { + parms[ii] = INT; + } + retval = MemberImpl.method(ut, Modifier.PUBLIC, UnresolvedType.VOID, "", parms); + + } else if (i.opcode == Constants.NEWARRAY) { + // NEWARRAY arrayInstruction = (NEWARRAY)i; + Type ot = i.getType(); + UnresolvedType ut = fromBcel(ot); + retval = MemberImpl.method(ut, Modifier.PUBLIC, UnresolvedType.VOID, "", new ResolvedType[] { INT }); + } else { + throw new BCException("Cannot create array construction signature for this non-array instruction:" + i); + } + return retval; + } + + public Member makeJoinPointSignatureForMethodInvocation(LazyClassGen cg, InvokeInstruction ii) { + ConstantPool cpg = cg.getConstantPool(); + String name = ii.getName(cpg); + String declaring = ii.getClassName(cpg); + UnresolvedType declaringType = null; + + String signature = ii.getSignature(cpg); + + // 307147 + if (name.startsWith("ajc$privMethod$")) { + // The invoke is on a privileged accessor. These may be created for different + // kinds of target, not necessarily just private methods. In bug 307147 it is + // for a private method. This code is identifying the particular case in 307147 + try { + declaringType = UnresolvedType.forName(declaring); + String typeNameAsFoundInAccessorName = declaringType.getName().replace('.', '_'); + int indexInAccessorName = name.lastIndexOf(typeNameAsFoundInAccessorName); + if (indexInAccessorName != -1) { + String methodName = name.substring(indexInAccessorName+typeNameAsFoundInAccessorName.length()+1); + ResolvedType resolvedDeclaringType = declaringType.resolve(this); + ResolvedMember[] methods = resolvedDeclaringType.getDeclaredMethods(); + for (ResolvedMember method: methods) { + if (method.getName().equals(methodName) && method.getSignature().equals(signature) && Modifier.isPrivate(method.getModifiers())) { + return method; + } + } + } + } catch (Exception e) { + // Remove this once confident above code isn't having unexpected side effects + // Added 1.8.7 + e.printStackTrace(); + } + } + + int modifier = (ii instanceof INVOKEINTERFACE) ? Modifier.INTERFACE + : (ii.opcode == Constants.INVOKESTATIC) ? Modifier.STATIC : (ii.opcode == Constants.INVOKESPECIAL && !name + .equals("")) ? Modifier.PRIVATE : 0; + + // in Java 1.4 and after, static method call of super class within + // subclass method appears + // as declared by the subclass in the bytecode - but they are not + // see #104212 + if (ii.opcode == Constants.INVOKESTATIC) { + ResolvedType appearsDeclaredBy = resolve(declaring); + // look for the method there + for (Iterator iterator = appearsDeclaredBy.getMethods(true, true); iterator.hasNext();) { + ResolvedMember method = iterator.next(); + if (Modifier.isStatic(method.getModifiers())) { + if (name.equals(method.getName()) && signature.equals(method.getSignature())) { + // we found it + declaringType = method.getDeclaringType(); + break; + } + } + } + } + + if (declaringType == null) { + if (declaring.charAt(0) == '[') { + declaringType = UnresolvedType.forSignature(declaring); + } else { + declaringType = UnresolvedType.forName(declaring); + } + } + return MemberImpl.method(declaringType, modifier, name, signature); + } + + @Override + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("BcelWorld("); + // buf.append(shadowMungerMap); + buf.append(")"); + return buf.toString(); + } + + /** + * Retrieve a bcel delegate for an aspect - this will return NULL if the delegate is an EclipseSourceType and not a + * BcelObjectType - this happens quite often when incrementally compiling. + */ + public static BcelObjectType getBcelObjectType(ResolvedType concreteAspect) { + if (concreteAspect == null) { + return null; + } + if (!(concreteAspect instanceof ReferenceType)) { // Might be Missing + return null; + } + ReferenceTypeDelegate rtDelegate = ((ReferenceType) concreteAspect).getDelegate(); + if (rtDelegate instanceof BcelObjectType) { + return (BcelObjectType) rtDelegate; + } else { + return null; + } + } + + public void tidyUp() { + // At end of compile, close any open files so deletion of those archives + // is possible + classPath.closeArchives(); + typeMap.report(); + typeMap.demote(true); + // ResolvedType.resetPrimitives(); + } + + // / The repository interface methods + + @Override + public JavaClass findClass(String className) { + return lookupJavaClass(classPath, className); + } + + @Override + public JavaClass loadClass(String className) throws ClassNotFoundException { + return lookupJavaClass(classPath, className); + } + + @Override + public void storeClass(JavaClass clazz) { + // doesn't need to do anything + } + + @Override + public void removeClass(JavaClass clazz) { + throw new RuntimeException("Not implemented"); + } + + @Override + public JavaClass loadClass(Class clazz) throws ClassNotFoundException { + throw new RuntimeException("Not implemented"); + } + + @Override + public void clear() { + delegate.clear(); + // throw new RuntimeException("Not implemented"); + } + + /** + * The aim of this method is to make sure a particular type is 'ok'. Some operations on the delegate for a type modify it and + * this method is intended to undo that... see pr85132 + */ + @Override + public void validateType(UnresolvedType type) { + ResolvedType result = typeMap.get(type.getSignature()); + if (result == null) { + return; // We haven't heard of it yet + } + if (!result.isExposedToWeaver()) { + return; // cant need resetting + } + result.ensureConsistent(); + // If we want to rebuild it 'from scratch' then: + // ClassParser cp = new ClassParser(new + // ByteArrayInputStream(newbytes),new String(cs)); + // try { + // rt.setDelegate(makeBcelObjectType(rt,cp.parse(),true)); + // } catch (ClassFormatException e) { + // e.printStackTrace(); + // } catch (IOException e) { + // e.printStackTrace(); + // } + } + + /** + * Apply a single declare parents - return true if we change the type + */ + private boolean applyDeclareParents(DeclareParents p, ResolvedType onType) { + boolean didSomething = false; + List newParents = p.findMatchingNewParents(onType, true); + if (!newParents.isEmpty()) { + didSomething = true; + BcelObjectType classType = BcelWorld.getBcelObjectType(onType); + // System.err.println("need to do declare parents for: " + onType); + for (ResolvedType newParent : newParents) { + // We set it here so that the imminent matching for ITDs can + // succeed - we still haven't done the necessary changes to the class file + // itself (like transform super calls) - that is done in + // BcelTypeMunger.mungeNewParent() + // classType.addParent(newParent); + onType.addParent(newParent); + ResolvedTypeMunger newParentMunger = new NewParentTypeMunger(newParent, p.getDeclaringType()); + newParentMunger.setSourceLocation(p.getSourceLocation()); + onType.addInterTypeMunger(new BcelTypeMunger(newParentMunger, getCrosscuttingMembersSet() + .findAspectDeclaringParents(p)), false); + } + } + return didSomething; + } + + /** + * Apply a declare @type - return true if we change the type + */ + private boolean applyDeclareAtType(DeclareAnnotation decA, ResolvedType onType, boolean reportProblems) { + boolean didSomething = false; + if (decA.matches(onType)) { + + if (onType.hasAnnotation(decA.getAnnotation().getType())) { + // already has it + return false; + } + + AnnotationAJ annoX = decA.getAnnotation(); + + // check the annotation is suitable for the target + boolean isOK = checkTargetOK(decA, onType, annoX); + + if (isOK) { + didSomething = true; + ResolvedTypeMunger newAnnotationTM = new AnnotationOnTypeMunger(annoX); + newAnnotationTM.setSourceLocation(decA.getSourceLocation()); + onType.addInterTypeMunger(new BcelTypeMunger(newAnnotationTM, decA.getAspect().resolve(this)), false); + decA.copyAnnotationTo(onType); + } + } + return didSomething; + } + + /** + * Apply the specified declare @field construct to any matching fields in the specified type. + * @param deca the declare annotation targeting fields + * @param type the type to check for members matching the declare annotation + * @return true if something matched and the type was modified + */ + private boolean applyDeclareAtField(DeclareAnnotation deca, ResolvedType type) { + boolean changedType = false; + ResolvedMember[] fields = type.getDeclaredFields(); + for (ResolvedMember field: fields) { + if (deca.matches(field, this)) { + AnnotationAJ anno = deca.getAnnotation(); + if (!field.hasAnnotation(anno.getType())) { + field.addAnnotation(anno); + changedType=true; + } + } + } + return changedType; + } + + /** + * Checks for an @target() on the annotation and if found ensures it allows the annotation to be attached to the target type + * that matched. + */ + private boolean checkTargetOK(DeclareAnnotation decA, ResolvedType onType, AnnotationAJ annoX) { + if (annoX.specifiesTarget()) { + if ((onType.isAnnotation() && !annoX.allowedOnAnnotationType()) || (!annoX.allowedOnRegularType())) { + return false; + } + } + return true; + } + + // Hmmm - very similar to the code in BcelWeaver.weaveParentTypeMungers - + // this code + // doesn't need to produce errors/warnings though as it won't really be + // weaving. + protected void weaveInterTypeDeclarations(ResolvedType onType) { + + List declareParentsList = getCrosscuttingMembersSet().getDeclareParents(); + if (onType.isRawType()) { + onType = onType.getGenericType(); + } + onType.clearInterTypeMungers(); + + List decpToRepeat = new ArrayList(); + + boolean aParentChangeOccurred = false; + boolean anAnnotationChangeOccurred = false; + // First pass - apply all decp mungers + for (Iterator i = declareParentsList.iterator(); i.hasNext();) { + DeclareParents decp = i.next(); + boolean typeChanged = applyDeclareParents(decp, onType); + if (typeChanged) { + aParentChangeOccurred = true; + } else { // Perhaps it would have matched if a 'dec @type' had + // modified the type + if (!decp.getChild().isStarAnnotation()) { + decpToRepeat.add(decp); + } + } + } + + // Still first pass - apply all dec @type mungers + for (DeclareAnnotation decA : getCrosscuttingMembersSet().getDeclareAnnotationOnTypes()) { + boolean typeChanged = applyDeclareAtType(decA, onType, true); + if (typeChanged) { + anAnnotationChangeOccurred = true; + } + } + + // apply declare @field + for (DeclareAnnotation deca: getCrosscuttingMembersSet().getDeclareAnnotationOnFields()) { + if (applyDeclareAtField(deca,onType)) { + anAnnotationChangeOccurred = true; + } + } + + while ((aParentChangeOccurred || anAnnotationChangeOccurred) && !decpToRepeat.isEmpty()) { + anAnnotationChangeOccurred = aParentChangeOccurred = false; + List decpToRepeatNextTime = new ArrayList(); + for (DeclareParents decp: decpToRepeat) { + if (applyDeclareParents(decp, onType)) { + aParentChangeOccurred = true; + } else { + decpToRepeatNextTime.add(decp); + } + } + + for (DeclareAnnotation deca: getCrosscuttingMembersSet().getDeclareAnnotationOnTypes()) { + if (applyDeclareAtType(deca, onType, false)) { + anAnnotationChangeOccurred = true; + } + } + + for (DeclareAnnotation deca: getCrosscuttingMembersSet().getDeclareAnnotationOnFields()) { + if (applyDeclareAtField(deca, onType)) { + anAnnotationChangeOccurred = true; + } + } + decpToRepeat = decpToRepeatNextTime; + } + + } + + @Override + public IWeavingSupport getWeavingSupport() { + return bcelWeavingSupport; + } + + @Override + public void reportCheckerMatch(Checker checker, Shadow shadow) { + IMessage iMessage = new Message(checker.getMessage(shadow), shadow.toString(), checker.isError() ? IMessage.ERROR + : IMessage.WARNING, shadow.getSourceLocation(), null, new ISourceLocation[] { checker.getSourceLocation() }, true, + 0, -1, -1); + + getMessageHandler().handleMessage(iMessage); + + if (getCrossReferenceHandler() != null) { + getCrossReferenceHandler() + .addCrossReference( + checker.getSourceLocation(), + shadow.getSourceLocation(), + (checker.isError() ? IRelationship.Kind.DECLARE_ERROR.getName() : IRelationship.Kind.DECLARE_WARNING + .getName()), false); + + } + + if (getModel() != null) { + AsmRelationshipProvider.addDeclareErrorOrWarningRelationship(getModelAsAsmManager(), shadow, checker); + } + + } + + public AsmManager getModelAsAsmManager() { + return (AsmManager) getModel(); // For now... always an AsmManager in a bcel environment + } + + void raiseError(String message) { + getMessageHandler().handleMessage(MessageUtil.error(message)); + } + + /** + * These are aop.xml files that can be used to alter the aspects that actually apply from those passed in - and also their scope + * of application to other files in the system. + * + * @param xmlFiles list of File objects representing any aop.xml files passed in to configure the build process + */ + public void setXmlFiles(List xmlFiles) { + if (!isXmlConfiguredWorld && !xmlFiles.isEmpty()) { + raiseError("xml configuration files only supported by the compiler when -xmlConfigured option specified"); + return; + } + if (!xmlFiles.isEmpty()) { + xmlConfiguration = new WeavingXmlConfig(this, WeavingXmlConfig.MODE_COMPILE); + } + for (File xmlfile : xmlFiles) { + try { + Definition d = DocumentParser.parse(xmlfile.toURI().toURL()); + xmlConfiguration.add(d); + } catch (MalformedURLException e) { + raiseError("Unexpected problem processing XML config file '" + xmlfile.getName() + "' :" + e.getMessage()); + } catch (Exception e) { + raiseError("Unexpected problem processing XML config file '" + xmlfile.getName() + "' :" + e.getMessage()); + } + } + } + + /** + * Add a scoped aspects where the scoping was defined in an aop.xml file and this world is being used in a LTW configuration + */ + public void addScopedAspect(String name, String scope) { + this.isXmlConfiguredWorld = true; + if (xmlConfiguration == null) { + xmlConfiguration = new WeavingXmlConfig(this, WeavingXmlConfig.MODE_LTW); + } + xmlConfiguration.addScopedAspect(name, scope); + } + + public void setXmlConfigured(boolean b) { + this.isXmlConfiguredWorld = b; + } + + @Override + public boolean isXmlConfigured() { + return isXmlConfiguredWorld && xmlConfiguration != null; + } + + public WeavingXmlConfig getXmlConfiguration() { + return xmlConfiguration; + } + + @Override + public boolean isAspectIncluded(ResolvedType aspectType) { + if (!isXmlConfigured()) { + return true; + } + return xmlConfiguration.specifiesInclusionOfAspect(aspectType.getName()); + } + + @Override + public TypePattern getAspectScope(ResolvedType declaringType) { + return xmlConfiguration.getScopeFor(declaringType.getName()); + } + + @Override + public boolean hasUnsatisfiedDependency(ResolvedType aspectType) { + String aspectName = aspectType.getName(); + + if (aspectType.hasAnnotations()) { + AnnotationAJ[] annos = aspectType.getAnnotations(); + for (AnnotationAJ anno: annos) { + if (anno.getTypeName().equals("org.aspectj.lang.annotation.RequiredTypes")) { + String values = anno.getStringFormOfValue("value"); // Example: "[A,org.foo.Bar]" + if (values != null && values.length() > 2) { + values = values.substring(1,values.length()-1); + StringTokenizer tokenizer = new StringTokenizer(values,","); + boolean anythingMissing = false; + while (tokenizer.hasMoreElements()) { + String requiredTypeName = tokenizer.nextToken(); + ResolvedType rt = resolve(UnresolvedType.forName(requiredTypeName)); + if (rt.isMissing()) { + if (!getMessageHandler().isIgnoring(IMessage.INFO)) { + getMessageHandler().handleMessage( + MessageUtil.info("deactivating aspect '" + aspectName + "' as it requires type '" + + requiredTypeName + "' which cannot be found on the classpath")); + } + anythingMissing = true; + if (aspectRequiredTypes == null) { + aspectRequiredTypes = new HashMap(); + } + // Record that it has an invalid type reference + aspectRequiredTypes.put(aspectName,requiredTypeName); + } + } + if (anythingMissing) { + return true; + } + else { + return false; + } + } + else { + // no value specified for annotation + return false; + } + } + } + } + if (aspectRequiredTypes == null) { + // no aspects require anything, so there can be no unsatisfied dependencies + return false; + } + if (!aspectRequiredTypesProcessed.contains(aspectName)) { + String requiredTypeName = aspectRequiredTypes.get(aspectName); + if (requiredTypeName==null) { + aspectRequiredTypesProcessed.add(aspectName); + return false; + } else { + ResolvedType rt = resolve(UnresolvedType.forName(requiredTypeName)); + if (!rt.isMissing()) { + aspectRequiredTypesProcessed.add(aspectName); + aspectRequiredTypes.remove(aspectName); + return false; + } else { + if (!getMessageHandler().isIgnoring(IMessage.INFO)) { + getMessageHandler().handleMessage( + MessageUtil.info("deactivating aspect '" + aspectName + "' as it requires type '" + + requiredTypeName + "' which cannot be found on the classpath")); + } + aspectRequiredTypesProcessed.add(aspectName); + return true; + } + } + } + return aspectRequiredTypes.containsKey(aspectName); + } + + private List aspectRequiredTypesProcessed = new ArrayList(); + private Map aspectRequiredTypes = null; + + public void addAspectRequires(String aspectClassName, String requiredType) { + if (aspectRequiredTypes == null) { + aspectRequiredTypes = new HashMap(); + } + aspectRequiredTypes.put(aspectClassName,requiredType); + } + + /** + * A WeavingXmlConfig is initially a collection of definitions from XML files - once the world is ready and weaving is running + * it will initialize and transform those definitions into an optimized set of values (eg. resolve type patterns and string + * names to real entities). It can then answer questions quickly: (1) is this aspect included in the weaving? (2) Is there a + * scope specified for this aspect and does it include type X? + * + */ + static class WeavingXmlConfig { + + final static int MODE_COMPILE = 1; + final static int MODE_LTW = 2; + + private int mode; + + private boolean initialized = false; // Lazily done + private List definitions = new ArrayList(); + + private List resolvedIncludedAspects = new ArrayList(); + private Map scopes = new HashMap(); + + // these are not set for LTW mode (exclusion of these fast match patterns is handled before the weaver/world are used) + private List includedFastMatchPatterns = Collections.emptyList(); + private List includedPatterns = Collections.emptyList(); + private List excludedFastMatchPatterns = Collections.emptyList(); + private List excludedPatterns = Collections.emptyList(); + + private BcelWorld world; + + public WeavingXmlConfig(BcelWorld bcelWorld, int mode) { + this.world = bcelWorld; + this.mode = mode; + } + + public void add(Definition d) { + definitions.add(d); + } + + public void addScopedAspect(String aspectName, String scope) { + ensureInitialized(); + resolvedIncludedAspects.add(aspectName); + try { + TypePattern scopePattern = new PatternParser(scope).parseTypePattern(); + scopePattern.resolve(world); + scopes.put(aspectName, scopePattern); + if (!world.getMessageHandler().isIgnoring(IMessage.INFO)) { + world.getMessageHandler().handleMessage( + MessageUtil.info("Aspect '" + aspectName + "' is scoped to apply against types matching pattern '" + + scopePattern.toString() + "'")); + } + } catch (Exception e) { + world.getMessageHandler().handleMessage( + MessageUtil.error("Unable to parse scope as type pattern. Scope was '" + scope + "': " + e.getMessage())); + } + } + + public void ensureInitialized() { + if (!initialized) { + try { + resolvedIncludedAspects = new ArrayList(); + // Process the definitions into something more optimal + for (Definition definition : definitions) { + List aspectNames = definition.getAspectClassNames(); + for (String name : aspectNames) { + resolvedIncludedAspects.add(name); + // TODO check for existence? + // ResolvedType resolvedAspect = resolve(UnresolvedType.forName(name)); + // if (resolvedAspect.isMissing()) { + // // ERROR + // } else { + // resolvedIncludedAspects.add(resolvedAspect); + // } + String scope = definition.getScopeForAspect(name); + if (scope != null) { + // Resolve the type pattern + try { + TypePattern scopePattern = new PatternParser(scope).parseTypePattern(); + scopePattern.resolve(world); + scopes.put(name, scopePattern); + if (!world.getMessageHandler().isIgnoring(IMessage.INFO)) { + world.getMessageHandler().handleMessage( + MessageUtil.info("Aspect '" + name + + "' is scoped to apply against types matching pattern '" + + scopePattern.toString() + "'")); + } + } catch (Exception e) { + // TODO definitions should remember which file they came from, for inclusion in this message + world.getMessageHandler().handleMessage( + MessageUtil.error("Unable to parse scope as type pattern. Scope was '" + scope + "': " + + e.getMessage())); + } + } + } + try { + List includePatterns = definition.getIncludePatterns(); + if (includePatterns.size() > 0) { + includedPatterns = new ArrayList(); + includedFastMatchPatterns = new ArrayList(); + } + for (String includePattern : includePatterns) { + if (includePattern.endsWith("..*")) { + // from 'blah.blah.blah..*' leave the 'blah.blah.blah.' + includedFastMatchPatterns.add(includePattern.substring(0, includePattern.length() - 2)); + } else { + TypePattern includedPattern = new PatternParser(includePattern).parseTypePattern(); + includedPatterns.add(includedPattern); + } + } + List excludePatterns = definition.getExcludePatterns(); + if (excludePatterns.size() > 0) { + excludedPatterns = new ArrayList(); + excludedFastMatchPatterns = new ArrayList(); + } + for (String excludePattern : excludePatterns) { + if (excludePattern.endsWith("..*")) { + // from 'blah.blah.blah..*' leave the 'blah.blah.blah.' + excludedFastMatchPatterns.add(excludePattern.substring(0, excludePattern.length() - 2)); + } else { + TypePattern excludedPattern = new PatternParser(excludePattern).parseTypePattern(); + excludedPatterns.add(excludedPattern); + } + } + } catch (ParserException pe) { + // TODO definitions should remember which file they came from, for inclusion in this message + world.getMessageHandler().handleMessage( + MessageUtil.error("Unable to parse type pattern: " + pe.getMessage())); + + } + } + } finally { + initialized = true; + } + } + } + + public boolean specifiesInclusionOfAspect(String name) { + ensureInitialized(); + return resolvedIncludedAspects.contains(name); + } + + public TypePattern getScopeFor(String name) { + return scopes.get(name); + } + + // Can't quite follow the same rules for exclusion as used for loadtime weaving: + // "The set of types to be woven are those types matched by at least one weaver include element and not matched by any + // weaver + // exclude element. If there are no weaver include statements then all non-excluded types are included." + // Since if the weaver is seeing it during this kind of build, the type is implicitly included. So all we should check + // for is exclusion + public boolean excludesType(ResolvedType type) { + if (mode == MODE_LTW) { + return false; + } + String typename = type.getName(); + boolean excluded = false; + for (String excludedPattern : excludedFastMatchPatterns) { + if (typename.startsWith(excludedPattern)) { + excluded = true; + break; + } + } + if (!excluded) { + for (TypePattern excludedPattern : excludedPatterns) { + if (excludedPattern.matchesStatically(type)) { + excluded = true; + break; + } + } + } + return excluded; + } + + } + + @Override + public TypeMap getTypeMap() { + return typeMap; + } + + @Override + public boolean isLoadtimeWeaving() { + return false; + } + + public void addTypeDelegateResolver(TypeDelegateResolver typeDelegateResolver) { + if (typeDelegateResolvers == null) { + typeDelegateResolvers = new ArrayList(); + } + typeDelegateResolvers.add(typeDelegateResolver); + } + + @Override + public void classWriteEvent(char[][] compoundName) { + typeMap.classWriteEvent(new String(CharOperation.concatWith(compoundName, '.'))); + } + + /** + * Force demote a type. + */ + public void demote(ResolvedType type) { + typeMap.demote(type); + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/ClassPathManager.java b/weaver/src/main/java/org/aspectj/weaver/bcel/ClassPathManager.java new file mode 100644 index 000000000..b20911245 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/ClassPathManager.java @@ -0,0 +1,578 @@ +/* ******************************************************************* + * Copyright (c) 2002, 2017 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Palo Alto Research Center, Incorporated (PARC). + * ******************************************************************/ +package org.aspectj.weaver.bcel; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import org.aspectj.bridge.IMessageHandler; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.LangUtil; +import org.aspectj.util.SoftHashMap; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.tools.Trace; +import org.aspectj.weaver.tools.TraceFactory; + +/** + * @author Andy Clement + * @author Mario Ivankovits + */ +public class ClassPathManager { + + private static Trace trace = TraceFactory.getTraceFactory().getTrace(ClassPathManager.class); + + private static int maxOpenArchives = -1; + + private static URI JRT_URI = URI.create("jrt:/"); //$NON-NLS-1$ + + private static final int MAXOPEN_DEFAULT = 1000; + + private List entries; + + // In order to control how many open files we have, we maintain a list. + // The max number is configured through the property: + // org.aspectj.weaver.openarchives + // and it defaults to 1000 + private List openArchives = new ArrayList(); + + static { + String openzipsString = getSystemPropertyWithoutSecurityException("org.aspectj.weaver.openarchives", + Integer.toString(MAXOPEN_DEFAULT)); + maxOpenArchives = Integer.parseInt(openzipsString); + if (maxOpenArchives < 20) { + maxOpenArchives = 1000; + } + } + + public ClassPathManager(List classpath, IMessageHandler handler) { + if (trace.isTraceEnabled()) { + trace.enter("", this, new Object[] { classpath==null?"null":classpath.toString(), handler }); + } + entries = new ArrayList(); + for (String classpathEntry: classpath) { + addPath(classpathEntry,handler); + } + if (trace.isTraceEnabled()) { + trace.exit(""); + } + } + + protected ClassPathManager() { + } + + public void addPath(String name, IMessageHandler handler) { + File f = new File(name); + String lc = name.toLowerCase(); + if (!f.isDirectory()) { + if (!f.isFile()) { + if (!lc.endsWith(".jar") || lc.endsWith(".zip")) { + // heuristic-only: ending with .jar or .zip means probably a zip file + MessageUtil.info(handler, WeaverMessages.format(WeaverMessages.ZIPFILE_ENTRY_MISSING, name)); + } else { + MessageUtil.info(handler, WeaverMessages.format(WeaverMessages.DIRECTORY_ENTRY_MISSING, name)); + } + return; + } + try { + if (lc.endsWith(LangUtil.JRT_FS)) { // Java9 + entries.add(new JImageEntry()); + } else { + entries.add(new ZipFileEntry(f)); + } + } catch (IOException ioe) { + MessageUtil.warn(handler, + WeaverMessages.format(WeaverMessages.ZIPFILE_ENTRY_INVALID, name, ioe.getMessage())); + return; + } + } else { + entries.add(new DirEntry(f)); + } + } + + public ClassFile find(UnresolvedType type) { + if (trace.isTraceEnabled()) { + trace.enter("find", this, type); + } + String name = type.getName(); + for (Iterator i = entries.iterator(); i.hasNext();) { + Entry entry = i.next(); + try { + ClassFile ret = entry.find(name); + if (trace.isTraceEnabled()) { + trace.event("searching for "+type+" in "+entry.toString()); + } + if (ret != null) { + if (trace.isTraceEnabled()) { + trace.exit("find", ret); + } + return ret; + } + } catch (IOException ioe) { + // this is NOT an error: it's valid to have missing classpath entries + if (trace.isTraceEnabled()) { + trace.error("Removing classpath entry for "+entry,ioe); + } + i.remove(); + } + } + if (trace.isTraceEnabled()) { + trace.exit("find", null); + } + return null; + } + + @Override + public String toString() { + StringBuffer buf = new StringBuffer(); + boolean start = true; + for (Iterator i = entries.iterator(); i.hasNext();) { + if (start) { + start = false; + } else { + buf.append(File.pathSeparator); + } + buf.append(i.next()); + } + return buf.toString(); + } + + public abstract static class ClassFile { + public abstract InputStream getInputStream() throws IOException; + public abstract String getPath(); + public abstract void close(); + } + + abstract static class Entry { + public abstract ClassFile find(String name) throws IOException; + } + + static class ByteBasedClassFile extends ClassFile { + + private byte[] bytes; + private ByteArrayInputStream bais; + private String path; + + public ByteBasedClassFile(byte[] bytes, String path) { + this.bytes = bytes; + this.path = path; + } + + @Override + public InputStream getInputStream() throws IOException { + this.bais = new ByteArrayInputStream(bytes); + return this.bais; + } + + @Override + public String getPath() { + return this.path; + } + + @Override + public void close() { + if (this.bais!=null) { + try { + this.bais.close(); + } catch (IOException e) { + } + this.bais = null; + } + } + + } + + static class FileClassFile extends ClassFile { + private File file; + private FileInputStream fis; + + public FileClassFile(File file) { + this.file = file; + } + + @Override + public InputStream getInputStream() throws IOException { + fis = new FileInputStream(file); + return fis; + } + + @Override + public void close() { + try { + if (fis != null) + fis.close(); + } catch (IOException ioe) { + throw new BCException("Can't close class file : " + file.getName(), ioe); + } finally { + fis = null; + } + } + + @Override + public String getPath() { + return file.getPath(); + } + } + + class DirEntry extends Entry { + private String dirPath; + + public DirEntry(File dir) { + this.dirPath = dir.getPath(); + } + + public DirEntry(String dirPath) { + this.dirPath = dirPath; + } + + @Override + public ClassFile find(String name) { + File f = new File(dirPath + File.separator + name.replace('.', File.separatorChar) + ".class"); + if (f.isFile()) + return new FileClassFile(f); + else + return null; + } + + @Override + public String toString() { + return dirPath; + } + } + + static class ZipEntryClassFile extends ClassFile { + private ZipEntry entry; + private ZipFileEntry zipFile; + private InputStream is; + + public ZipEntryClassFile(ZipFileEntry zipFile, ZipEntry entry) { + this.zipFile = zipFile; + this.entry = entry; + } + + @Override + public InputStream getInputStream() throws IOException { + is = zipFile.getZipFile().getInputStream(entry); + return is; + } + + @Override + public void close() { + try { + if (is != null) + is.close(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + is = null; + } + } + + @Override + public String getPath() { + return entry.getName(); + } + + } + + /** + * Maintains a shared package cache for java runtime image. This maps packages (for example: + * java/lang) to a starting root position in the filesystem (for example: /modules/java.base/java/lang). + * When searching for a type we work out the package name, use it to find where in the filesystem + * to start looking then run from there. Once found we do cache what we learn to make subsequent + * lookups of that type even faster. Maintaining just a package cache rather than complete type cache + * helps reduce memory usage but still gives reasonably fast lookup performance. + */ + static class JImageEntry extends Entry { + + private static FileSystem fs = null; + + private final static Map fileCache = new SoftHashMap(); + + private final static Map packageCache = new HashMap(); + + private static boolean packageCacheInitialized = false; + + public JImageEntry() { + if (fs == null) { + try { + fs = FileSystems.getFileSystem(JRT_URI); + } catch (Throwable t) { + throw new IllegalStateException("Unexpectedly unable to initialize a JRT filesystem", t); + } + } + buildPackageMap(); + } + + class PackageCacheBuilderVisitor extends SimpleFileVisitor { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + if (file.getNameCount() > 3 && file.toString().endsWith(".class")) { + int fnc = file.getNameCount(); + if (fnc > 3) { // There is a package name - e.g. /modules/java.base/java/lang/Object.class + Path packagePath = file.subpath(2, fnc-1); // e.g. java/lang + String packagePathString = packagePath.toString(); + packageCache.put(packagePathString, file.subpath(0, fnc-1)); // java/lang -> /modules/java.base/java/lang + } + } + return FileVisitResult.CONTINUE; + } + } + + /** + * Create a map from package names to the specific directory of the package members in the filesystem. + */ + private synchronized void buildPackageMap() { + if (!packageCacheInitialized) { + packageCacheInitialized = true; + Iterable roots = fs.getRootDirectories(); + PackageCacheBuilderVisitor visitor = new PackageCacheBuilderVisitor(); + try { + for (java.nio.file.Path path : roots) { + Files.walkFileTree(path, visitor); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + class TypeIdentifier extends SimpleFileVisitor { + + // What are we looking for? + private String name; + + // If set, where did we find it? + public Path found; + + // Basic metric count of how many files we checked before finding it + public int filesSearchedCount; + + public TypeIdentifier(String name) { + this.name = name; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + filesSearchedCount++; + if (file.getNameCount() > 2 && file.toString().endsWith(".class")) { + int fnc = file.getNameCount(); + Path filePath = file.subpath(2, fnc); + String filePathString = filePath.toString(); + if (filePathString.equals(name)) { + fileCache.put(filePathString, file); + found = file; + return FileVisitResult.TERMINATE; + } + } + return FileVisitResult.CONTINUE; + } + } + + private Path searchForFileAndCache(final Path startPath, final String name) { + TypeIdentifier locator = new TypeIdentifier(name); + try { + Files.walkFileTree(startPath, locator); + } catch (IOException e) { + throw new RuntimeException(e); + } + return locator.found; + } + + @Override + public ClassFile find(String name) throws IOException { + String fileName = name.replace('.', '/') + ".class"; + Path file = fileCache.get(fileName); + if (file == null) { + // Check the packages map to see if we know about this package + int idx = fileName.lastIndexOf('/'); + if (idx == -1) { + // Package not here + return null; + } + Path packageStart = null; + String packageName = null; + if (idx !=-1 ) { + packageName = fileName.substring(0, idx); + packageStart = packageCache.get(packageName); + if (packageStart != null) { + file = searchForFileAndCache(packageStart, fileName); + } + } + } + if (file == null) { + return null; + } + byte[] bs = Files.readAllBytes(file); + ClassFile cf = new ByteBasedClassFile(bs, fileName); + return cf; + } + + static Map getPackageCache() { + return packageCache; + } + + static Map getFileCache() { + return fileCache; + } + + } + + class ZipFileEntry extends Entry { + private File file; + private ZipFile zipFile; + + public ZipFileEntry(File file) throws IOException { + this.file = file; + } + + public ZipFileEntry(ZipFile zipFile) { + this.zipFile = zipFile; + } + + public ZipFile getZipFile() { + return zipFile; + } + + @Override + public ClassFile find(String name) throws IOException { + ensureOpen(); + String key = name.replace('.', '/') + ".class"; + ZipEntry entry = zipFile.getEntry(key); + if (entry != null) + return new ZipEntryClassFile(this, entry); + else + return null; // This zip will be closed when necessary... + } + + public List getAllClassFiles() throws IOException { + ensureOpen(); + List ret = new ArrayList(); + for (Enumeration e = zipFile.entries(); e.hasMoreElements();) { + ZipEntry entry = e.nextElement(); + String name = entry.getName(); + if (hasClassExtension(name)) + ret.add(new ZipEntryClassFile(this, entry)); + } + // if (ret.isEmpty()) close(); + return ret; + } + + private void ensureOpen() throws IOException { + if (zipFile != null && openArchives.contains(zipFile)) { + if (isReallyOpen()) + return; + } + if (openArchives.size() >= maxOpenArchives) { + closeSomeArchives(openArchives.size() / 10); // Close 10% of + // those open + } + zipFile = new ZipFile(file); + if (!isReallyOpen()) { + throw new FileNotFoundException("Can't open archive: " + file.getName() + " (size() check failed)"); + } + openArchives.add(zipFile); + } + + private boolean isReallyOpen() { + try { + zipFile.size(); // this will fail if the file has been closed + // for + // some reason; + return true; + } catch (IllegalStateException ex) { + // this means the zip file is closed... + return false; + } + + } + + public void closeSomeArchives(int n) { + for (int i = n - 1; i >= 0; i--) { + ZipFile zf = openArchives.get(i); + try { + zf.close(); + } catch (IOException e) { + e.printStackTrace(); + } + openArchives.remove(i); + } + } + + public void close() { + if (zipFile == null) + return; + try { + openArchives.remove(zipFile); + zipFile.close(); + } catch (IOException ioe) { + throw new BCException("Can't close archive: " + file.getName(), ioe); + } finally { + zipFile = null; + } + } + + @Override + public String toString() { + return file.getName(); + } + } + + /* private */static boolean hasClassExtension(String name) { + return name.toLowerCase().endsWith((".class")); + } + + public void closeArchives() { + for (Entry entry : entries) { + if (entry instanceof ZipFileEntry) { + ((ZipFileEntry) entry).close(); + } + openArchives.clear(); + } + } + + // Copes with the security manager + private static String getSystemPropertyWithoutSecurityException(String aPropertyName, String aDefaultValue) { + try { + return System.getProperty(aPropertyName, aDefaultValue); + } catch (SecurityException ex) { + return aDefaultValue; + } + } + + // Mainly exposed for testing + public List getEntries() { + return entries; + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/ExceptionRange.java b/weaver/src/main/java/org/aspectj/weaver/bcel/ExceptionRange.java new file mode 100644 index 000000000..5e74627c3 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/ExceptionRange.java @@ -0,0 +1,151 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.bcel; + +import org.aspectj.apache.bcel.generic.InstructionHandle; +import org.aspectj.apache.bcel.generic.InstructionList; +import org.aspectj.weaver.UnresolvedType; + +/** + * exceptionRanges are set initially to be low priority. The various setPriority methods should be used accordingly. The priority is + * used when we pack the exception table into a method... the exception table should be sorted from high to low priority. Exceptions + * we generate for advice is either high priority (higher than anything coming from the original method... most kinds of + * non-execution advice) or low priority (lower than anything coming from the original method, for execution advice). + * + *

+ * ??? This does not account for handler, or any other "statement-level" advice. When such statement level advice happens, we may + * want to go to a float level, so we can set the priority of advice to be lower than anything it encloses, and higher than anything + * enclosing it. + */ + +/* + * we're actually using the fact that we're an instruction targeter, for the handler + */ +public final class ExceptionRange extends Range { + + private InstructionHandle handler; + private final UnresolvedType exceptionType; + private final int priority; + + // ---- initialization + + /** + * After this constructor is called, this range is not well situated unless {@link #associateWithTargets} is called + * + * XXX priority should be fixed + */ + public ExceptionRange(InstructionList body, UnresolvedType exceptionType, int priority) { + super(body); + this.exceptionType = exceptionType; + this.priority = priority; + } + + /** + * @param insideExisting + */ + public ExceptionRange(InstructionList body, UnresolvedType exceptionType, boolean insideExisting) { + this(body, exceptionType, insideExisting ? Integer.MAX_VALUE : -1); + } + + public void associateWithTargets(InstructionHandle start, InstructionHandle end, InstructionHandle handler) { + // assert body.contains(start) && body.contains(end) && body.contains(handler) + this.start = start; + this.end = end; + this.handler = handler; + start.addTargeter(this); + end.addTargeter(this); + handler.addTargeter(this); + } + + // ---- + + public InstructionHandle getHandler() { + return handler; + } + + public UnresolvedType getCatchType() { + return exceptionType; + } + + public int getPriority() { + return priority; + } + + // ---- from object + + public String toString() { + String str; + if (exceptionType == null) { + str = "finally"; + } else { + str = "catch " + exceptionType; + } + // if (priority >= 0 && priority < Integer.MAX_VALUE) { + // str += " (priority " + priority + ")"; + // } + return str; + } + + public boolean equals(Object other) { + if (!(other instanceof ExceptionRange)) + return false; + ExceptionRange o = (ExceptionRange) other; + return o.getStart() == getStart() && o.getEnd() == getEnd() && o.handler == handler + && ((o.exceptionType == null) ? (exceptionType == null) : o.exceptionType.equals(exceptionType)) + && o.priority == priority; + } + + private volatile int hashCode = 0; + + public int hashCode() { + if (hashCode == 0) { + int ret = 17; + ret = 37 * ret + getStart().hashCode(); + ret = 37 * ret + getEnd().hashCode(); + ret = 37 * ret + handler.hashCode(); + ret = 37 * ret + ((exceptionType == null) ? 0 : exceptionType.hashCode()); + ret = 37 * ret + priority; + hashCode = ret; + } + return hashCode; + } + + public void updateTarget(InstructionHandle oldIh, InstructionHandle newIh, InstructionList newBody) { + super.updateTarget(oldIh, newIh, newBody); + // we're guaranteed that start, end, and handler are distinct instruction handles. + if (oldIh == handler) { + handler = newIh; + } + } + + public static boolean isExceptionStart(InstructionHandle ih) { + if (!isRangeHandle(ih)) + return false; + Range r = getRange(ih); + if (!(r instanceof ExceptionRange)) + return false; + ExceptionRange er = (ExceptionRange) r; + return er.getStart() == ih; + } + + public static boolean isExceptionEnd(InstructionHandle ih) { + if (!isRangeHandle(ih)) + return false; + Range r = getRange(ih); + if (!(r instanceof ExceptionRange)) + return false; + ExceptionRange er = (ExceptionRange) r; + return er.getEnd() == ih; + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/ExtensibleURLClassLoader.java b/weaver/src/main/java/org/aspectj/weaver/bcel/ExtensibleURLClassLoader.java new file mode 100644 index 000000000..3480b8172 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/ExtensibleURLClassLoader.java @@ -0,0 +1,113 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM Corporation + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Matthew Webster, Adrian Colyer, + * Martin Lippert initial implementation + * Andy Clement + * Roy Varghese - Bug 473555 + * ******************************************************************/ + +package org.aspectj.weaver.bcel; + +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.security.CodeSource; + +import org.aspectj.util.FileUtil; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.UnresolvedType; + +public abstract class ExtensibleURLClassLoader extends URLClassLoader { + + private ClassPathManager classPath; + + public ExtensibleURLClassLoader(URL[] urls, ClassLoader parent) { + super(urls, parent); + + // System.err.println("? ExtensibleURLClassLoader.() path=" + WeavingAdaptor.makeClasspath(urls)); + try { + classPath = new ClassPathManager(FileUtil.makeClasspath(urls), null); + } catch (ExceptionInInitializerError ex) { + ex.printStackTrace(System.out); + throw ex; + } + } + + protected void addURL(URL url) { + super.addURL(url); // amc - this call was missing and is needed in + // WeavingURLClassLoader chains + classPath.addPath(url.getPath(), null); + } + + protected Class findClass(String name) throws ClassNotFoundException { + // System.err.println("? ExtensibleURLClassLoader.findClass(" + name + ")"); + try { + byte[] bytes = getBytes(name); + if (bytes != null) { + return defineClass(name, bytes); + } else { + throw new ClassNotFoundException(name); + } + } catch (IOException ex) { + throw new ClassNotFoundException(name); + } + } + + protected Class defineClass(String name, byte[] b, CodeSource cs) throws IOException { + // System.err.println("? ExtensibleURLClassLoader.defineClass(" + name + ",[" + b.length + "])"); + return defineClass(name, b, 0, b.length, cs); + } + + protected byte[] getBytes(String name) throws IOException { + byte[] b = null; + UnresolvedType unresolvedType = null; + try { + unresolvedType = UnresolvedType.forName(name); + } catch (BCException bce) { + if (bce.getMessage().indexOf("nameToSignature") == -1) { + bce.printStackTrace(System.err); + } + return null; + } + ClassPathManager.ClassFile classFile = classPath.find(unresolvedType); + if (classFile != null) { + try { + b = FileUtil.readAsByteArray(classFile.getInputStream()); + } finally { + classFile.close(); + } + } + return b; + } + + private Class defineClass(String name, byte[] bytes /* ClassPathManager.ClassFile classFile */) throws IOException { + String packageName = getPackageName(name); + if (packageName != null) { + Package pakkage = getPackage(packageName); + if (pakkage == null) { + definePackage(packageName, null, null, null, null, null, null, null); + } + } + + return defineClass(name, bytes, null); + } + + private String getPackageName(String className) { + int offset = className.lastIndexOf('.'); + return (offset == -1) ? null : className.substring(0, offset); + } + + @Override + public void close() throws IOException { + super.close(); + classPath.closeArchives(); + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/FakeAnnotation.java b/weaver/src/main/java/org/aspectj/weaver/bcel/FakeAnnotation.java new file mode 100644 index 000000000..4194e66e9 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/FakeAnnotation.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * initial implementation Andy Clement + *******************************************************************************/ +package org.aspectj.weaver.bcel; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.List; + +import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; +import org.aspectj.apache.bcel.classfile.annotation.NameValuePair; + +/** + * For implementing declare @type interacting with declare @parents during compilation - we need to be able to add an annotation to + * 'binary type binding' (this is how types are seen during incremental compilation). Unlike a SourceTypeBinding - a + * BinaryTypeBinding does not allow easy interaction with its annotations - so what we do is take the eclipse annotation, suck out + * the name/signature and visibility and put that information in a 'FakeAnnotation'. The FakeAnnotation is attached to the BCEL + * delegate for the binary type binding - this will allow type resolution to succeed correctly. The FakeAnnotation never makes it to + * disk, since the weaver does the job properly, attaching a real annotation. + */ +public class FakeAnnotation extends AnnotationGen { + + private String name; + private String sig; + private boolean isRuntimeVisible; + + public FakeAnnotation(String name, String sig, boolean isRuntimeVisible) { + super(null, null, true, null); + this.name = name; + this.sig = sig; + this.isRuntimeVisible = isRuntimeVisible; + } + + public String getTypeName() { + return name; + } + + public String getTypeSignature() { + return sig; + } + + public void addElementNameValuePair(NameValuePair evp) { + // doesnt need to know about name/value pairs + } + + public void dump(DataOutputStream dos) throws IOException { + // should be serialized + } + + public int getTypeIndex() { + return 0; + } + + public List getValues() { + return null; + } + + public boolean isRuntimeVisible() { + return isRuntimeVisible; + } + + protected void setIsRuntimeVisible(boolean b) { + } + + public String toShortString() { + return "@" + this.name; + } + + public String toString() { + return this.name; + } +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/IfFinder.java b/weaver/src/main/java/org/aspectj/weaver/bcel/IfFinder.java new file mode 100644 index 000000000..c66aa8939 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/IfFinder.java @@ -0,0 +1,56 @@ +/* ******************************************************************* + * Copyright (c) 2006 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement initial implementation + * ******************************************************************/ +package org.aspectj.weaver.bcel; + +import org.aspectj.weaver.patterns.AbstractPatternNodeVisitor; +import org.aspectj.weaver.patterns.AndPointcut; +import org.aspectj.weaver.patterns.IfPointcut; +import org.aspectj.weaver.patterns.NotPointcut; +import org.aspectj.weaver.patterns.OrPointcut; + +/** + * Look for an if() pointcut + */ +class IfFinder extends AbstractPatternNodeVisitor { + boolean hasIf = false; + + public Object visit(IfPointcut node, Object data) { + if (node.alwaysFalse() || node.alwaysTrue()) { + // IfFalse / IfTrue + } else { + hasIf = true; + } + return node; + } + + public Object visit(AndPointcut node, Object data) { + if (!hasIf) + node.getLeft().accept(this, data); + if (!hasIf) + node.getRight().accept(this, data); + return node; + } + + public Object visit(NotPointcut node, Object data) { + if (!hasIf) + node.getNegatedPointcut().accept(this, data); + return node; + } + + public Object visit(OrPointcut node, Object data) { + if (!hasIf) + node.getLeft().accept(this, data); + if (!hasIf) + node.getRight().accept(this, data); + return node; + } +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/LazyClassGen.java b/weaver/src/main/java/org/aspectj/weaver/bcel/LazyClassGen.java new file mode 100644 index 000000000..83189e1d4 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/LazyClassGen.java @@ -0,0 +1,1940 @@ +/* ******************************************************************* + * Copyright (c) 2002-2010 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * Andy Clement 6Jul05 generics - signature attribute + * Abraham Nevado + * ******************************************************************/ + +package org.aspectj.weaver.bcel; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.PrintStream; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.Vector; + +import org.aspectj.apache.bcel.Constants; +import org.aspectj.apache.bcel.classfile.Attribute; +import org.aspectj.apache.bcel.classfile.ConstantPool; +import org.aspectj.apache.bcel.classfile.Field; +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.classfile.Method; +import org.aspectj.apache.bcel.classfile.Signature; +import org.aspectj.apache.bcel.classfile.Synthetic; +import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; +import org.aspectj.apache.bcel.generic.BasicType; +import org.aspectj.apache.bcel.generic.ClassGen; +import org.aspectj.apache.bcel.generic.FieldGen; +import org.aspectj.apache.bcel.generic.InstructionConstants; +import org.aspectj.apache.bcel.generic.InstructionFactory; +import org.aspectj.apache.bcel.generic.InstructionHandle; +import org.aspectj.apache.bcel.generic.InstructionList; +import org.aspectj.apache.bcel.generic.ObjectType; +import org.aspectj.apache.bcel.generic.Type; +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.SourceLocation; +import org.aspectj.weaver.AjAttribute; +import org.aspectj.weaver.AjAttribute.WeaverState; +import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.MemberKind; +import org.aspectj.weaver.NameMangler; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.RuntimeVersion; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.SignatureUtils; +import org.aspectj.weaver.TypeVariable; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.UnresolvedType.TypeKind; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.WeaverStateInfo; +import org.aspectj.weaver.World; +import org.aspectj.weaver.bcel.asm.AsmDetector; +import org.aspectj.weaver.bcel.asm.StackMapAdder; + +/** + * Lazy lazy lazy. We don't unpack the underlying class unless necessary. Things like new methods and annotations accumulate in here + * until they must be written out, don't add them to the underlying MethodGen! Things are slightly different if this represents an + * Aspect. + */ +public final class LazyClassGen { + + private static final Type[] ARRAY_7STRING_INT = new Type[] { Type.STRING, Type.STRING, Type.STRING, Type.STRING, Type.STRING, + Type.STRING, Type.STRING, Type.INT }; + + private static final Type[] ARRAY_8STRING_INT = new Type[] { Type.STRING, Type.STRING, Type.STRING, Type.STRING, Type.STRING, + Type.STRING, Type.STRING, Type.STRING, Type.INT }; + + private static final Type[] PARAMSIGNATURE_MAKESJP_METHOD = new Type[] { + Type.STRING, Type.INT, Type.STRING, Type.CLASS, Type.CLASS_ARRAY, Type.STRING_ARRAY, Type.CLASS_ARRAY, Type.CLASS, Type.INT + }; + + private static final Type[] PARAMSIGNATURE_MAKESJP_CONSTRUCTOR = new Type[] { + Type.STRING, Type.INT, Type.CLASS, Type.CLASS_ARRAY, Type.STRING_ARRAY, Type.CLASS_ARRAY, Type.INT + }; + + private static final Type[] PARAMSIGNATURE_MAKESJP_CATCHCLAUSE = new Type[] { + Type.STRING, Type.CLASS, Type.CLASS, Type.STRING, Type.INT + }; + + private static final Type[] PARAMSIGNATURE_MAKESJP_FIELD = new Type[] { + Type.STRING, Type.INT, Type.STRING, Type.CLASS, Type.CLASS, Type.INT + }; + + private static final Type[] PARAMSIGNATURE_MAKESJP_INITIALIZER = new Type[] { + Type.STRING, Type.INT, Type.CLASS, Type.INT + }; + + private static final Type[] PARAMSIGNATURE_MAKESJP_MONITOR = new Type[] { + Type.STRING, Type.CLASS, Type.INT + }; + + private static final Type[] PARAMSIGNATURE_MAKESJP_ADVICE = new Type[] { + Type.STRING, Type.INT, Type.STRING, Type.CLASS, Type.CLASS_ARRAY, Type.STRING_ARRAY, + Type.CLASS_ARRAY, Type.CLASS, Type.INT + }; + + + + + + private static final int ACC_SYNTHETIC = 0x1000; + + private static final String[] NO_STRINGS = new String[0]; + + int highestLineNumber = 0; // ---- JSR 45 info + + private final SortedMap inlinedFiles = new TreeMap(); + + private boolean regenerateGenericSignatureAttribute = false; + + private BcelObjectType myType; // XXX is not set for types we create + private ClassGen myGen; + private final ConstantPool cp; + private final World world; + private final String packageName = null; + + private final List fields = new ArrayList(); + private final List methodGens = new ArrayList(); + private final List classGens = new ArrayList(); + private final List annotations = new ArrayList(); + private int childCounter = 0; + + private final InstructionFactory fact; + + private boolean isSerializable = false; + private boolean hasSerialVersionUIDField = false; + private boolean serialVersionUIDRequiresInitialization = false; + private long calculatedSerialVersionUID; + private boolean hasClinit = false; + + private ResolvedType[] extraSuperInterfaces = null; + private ResolvedType superclass = null; + + // --- + + static class InlinedSourceFileInfo { + int highestLineNumber; + int offset; // calculated + + InlinedSourceFileInfo(int highestLineNumber) { + this.highestLineNumber = highestLineNumber; + } + } + + void addInlinedSourceFileInfo(String fullpath, int highestLineNumber) { + Object o = inlinedFiles.get(fullpath); + if (o != null) { + InlinedSourceFileInfo info = (InlinedSourceFileInfo) o; + if (info.highestLineNumber < highestLineNumber) { + info.highestLineNumber = highestLineNumber; + } + } else { + inlinedFiles.put(fullpath, new InlinedSourceFileInfo(highestLineNumber)); + } + } + + void calculateSourceDebugExtensionOffsets() { + int i = roundUpToHundreds(highestLineNumber); + for (InlinedSourceFileInfo element : inlinedFiles.values()) { + element.offset = i; + i = roundUpToHundreds(i + element.highestLineNumber); + } + } + + private static int roundUpToHundreds(int i) { + return ((i / 100) + 1) * 100; + } + + int getSourceDebugExtensionOffset(String fullpath) { + return inlinedFiles.get(fullpath).offset; + } + + // private Unknown getSourceDebugExtensionAttribute() { + // int nameIndex = cp.addUtf8("SourceDebugExtension"); + // String data = getSourceDebugExtensionString(); + // //System.err.println(data); + // byte[] bytes = Utility.stringToUTF(data); + // int length = bytes.length; + // + // return new Unknown(nameIndex, length, bytes, cp); + // } + + // private LazyClassGen() {} + // public static void main(String[] args) { + // LazyClassGen m = new LazyClassGen(); + // m.highestLineNumber = 37; + // m.inlinedFiles.put("boo/baz/foo.java", new InlinedSourceFileInfo( 83)); + // m.inlinedFiles.put("boo/barz/foo.java", new InlinedSourceFileInfo(292)); + // m.inlinedFiles.put("boo/baz/moo.java", new InlinedSourceFileInfo(128)); + // m.calculateSourceDebugExtensionOffsets(); + // System.err.println(m.getSourceDebugExtensionString()); + // } + + // For the entire pathname, we're using package names. This is probably + // wrong. + // private String getSourceDebugExtensionString() { + // StringBuffer out = new StringBuffer(); + // String myFileName = getFileName(); + // // header section + // out.append("SMAP\n"); + // out.append(myFileName); + // out.append("\nAspectJ\n"); + // // stratum section + // out.append("*S AspectJ\n"); + // // file section + // out.append("*F\n"); + // out.append("1 "); + // out.append(myFileName); + // out.append("\n"); + // int i = 2; + // for (Iterator iter = inlinedFiles.keySet().iterator(); iter.hasNext();) { + // String element = (String) iter.next(); + // int ii = element.lastIndexOf('/'); + // if (ii == -1) { + // out.append(i++); out.append(' '); + // out.append(element); out.append('\n'); + // } else { + // out.append("+ "); out.append(i++); out.append(' '); + // out.append(element.substring(ii+1)); out.append('\n'); + // out.append(element); out.append('\n'); + // } + // } + // // emit line section + // out.append("*L\n"); + // out.append("1#1,"); + // out.append(highestLineNumber); + // out.append(":1,1\n"); + // i = 2; + // for (Iterator iter = inlinedFiles.values().iterator(); iter.hasNext();) { + // InlinedSourceFileInfo element = (InlinedSourceFileInfo) iter.next(); + // out.append("1#"); + // out.append(i++); out.append(','); + // out.append(element.highestLineNumber); out.append(":"); + // out.append(element.offset + 1); out.append(",1\n"); + // } + // // end section + // out.append("*E\n"); + // // and finish up... + // return out.toString(); + // } + + // ---- end JSR45-related stuff + + /** Emit disassembled class and newline to out */ + public static void disassemble(String path, String name, PrintStream out) throws IOException { + if (null == out) { + return; + } + // out.println("classPath: " + classPath); + + BcelWorld world = new BcelWorld(path); + + UnresolvedType ut = UnresolvedType.forName(name); + ut.setNeedsModifiableDelegate(true); + LazyClassGen clazz = new LazyClassGen(BcelWorld.getBcelObjectType(world.resolve(ut))); + clazz.print(out); + out.println(); + } + + public String getNewGeneratedNameTag() { + return new Integer(childCounter++).toString(); + } + + // ---- + + public LazyClassGen(String class_name, String super_class_name, String file_name, int access_flags, String[] interfaces, + World world) { + myGen = new ClassGen(class_name, super_class_name, file_name, access_flags, interfaces); + cp = myGen.getConstantPool(); + fact = new InstructionFactory(myGen, cp); + regenerateGenericSignatureAttribute = true; + this.world = world; + } + + public void setMajorMinor(int major, int minor) { + myGen.setMajor(major); + myGen.setMinor(minor); + } + + public int getMajor() { + return myGen.getMajor(); + } + + public int getMinor() { + return myGen.getMinor(); + } + + // Non child type, so it comes from a real type in the world. + public LazyClassGen(BcelObjectType myType) { + myGen = new ClassGen(myType.getJavaClass()); + cp = myGen.getConstantPool(); + fact = new InstructionFactory(myGen, cp); + this.myType = myType; + world = myType.getResolvedTypeX().getWorld(); + + /* Does this class support serialization */ + if (implementsSerializable(getType())) { + isSerializable = true; + + // ResolvedMember[] fields = getType().getDeclaredFields(); + // for (int i = 0; i < fields.length; i++) { + // ResolvedMember field = fields[i]; + // if (field.getName().equals("serialVersionUID") + // && field.isStatic() && field.getType().equals(UnresolvedType.LONG)) + // { + // hasSerialVersionUIDField = true; + // } + // } + hasSerialVersionUIDField = hasSerialVersionUIDField(getType()); + + ResolvedMember[] methods = getType().getDeclaredMethods(); + for (int i = 0; i < methods.length; i++) { + ResolvedMember method = methods[i]; + if (method.getName().equals("")) { + if (method.getKind() != Member.STATIC_INITIALIZATION) { + throw new RuntimeException("qui?"); + } + hasClinit = true; + } + } + + // Do we need to calculate an SUID and add it? + if (!getType().isInterface() && !hasSerialVersionUIDField && world.isAddSerialVerUID()) { + calculatedSerialVersionUID = myGen.getSUID(); + FieldGen fg = new FieldGen(Constants.ACC_PRIVATE | Constants.ACC_FINAL | Constants.ACC_STATIC, BasicType.LONG, + "serialVersionUID", getConstantPool()); + addField(fg); + hasSerialVersionUIDField = true; + serialVersionUIDRequiresInitialization = true; + // warn about what we've done? + if (world.getLint().calculatingSerialVersionUID.isEnabled()) { + world.getLint().calculatingSerialVersionUID.signal( + new String[] { getClassName(), Long.toString(calculatedSerialVersionUID) + "L" }, null, null); + } + } + } + + ResolvedMember[] methods = myType.getDeclaredMethods(); + for (int i = 0; i < methods.length; i++) { + addMethodGen(new LazyMethodGen((BcelMethod) methods[i], this)); + } + + // Method[] methods = myGen.getMethods(); + // for (int i = 0; i < methods.length; i++) { + // addMethodGen(new LazyMethodGen(methods[i], this)); + // } + + ResolvedMember[] fields = myType.getDeclaredFields(); + for (int i = 0; i < fields.length; i++) { + this.fields.add((BcelField) fields[i]); + } + } + + public static boolean hasSerialVersionUIDField(ResolvedType type) { + + ResolvedMember[] fields = type.getDeclaredFields(); + for (int i = 0; i < fields.length; i++) { + ResolvedMember field = fields[i]; + if (field.getName().equals("serialVersionUID") && Modifier.isStatic(field.getModifiers()) + && field.getType().equals(UnresolvedType.LONG)) { + return true; + } + } + + return false; + } + + // public void addAttribute(Attribute i) { + // myGen.addAttribute(i); + // } + + // ---- + + public String getInternalClassName() { + return getConstantPool().getConstantString_CONSTANTClass(myGen.getClassNameIndex()); + // getConstantPool().getConstantString( + // myGen.getClassNameIndex(), + // Constants.CONSTANT_Class); + + } + + public String getInternalFileName() { + String str = getInternalClassName(); + int index = str.lastIndexOf('/'); + if (index == -1) { + return getFileName(); + } else { + return str.substring(0, index + 1) + getFileName(); + } + } + + /** + * Returns the packagename - if its the default package we return an empty string + */ + public String getPackageName() { + if (packageName != null) { + return packageName; + } + String str = getInternalClassName(); + int index = str.indexOf("<"); + if (index != -1) { + str = str.substring(0, index); // strip off the generics guff + } + index = str.lastIndexOf("/"); + if (index == -1) { + return ""; + } + return str.substring(0, index).replace('/', '.'); + } + + public void addMethodGen(LazyMethodGen gen) { + // assert gen.getClassName() == super.getClassName(); + methodGens.add(gen); + if (highestLineNumber < gen.highestLineNumber) { + highestLineNumber = gen.highestLineNumber; + } + } + + public boolean removeMethodGen(LazyMethodGen gen) { + return methodGens.remove(gen); + } + + public void addMethodGen(LazyMethodGen gen, ISourceLocation sourceLocation) { + addMethodGen(gen); + if (!gen.getMethod().isPrivate()) { + warnOnAddedMethod(gen.getMethod(), sourceLocation); + } + } + + public void errorOnAddedField(FieldGen field, ISourceLocation sourceLocation) { + if (isSerializable && !hasSerialVersionUIDField) { + getWorld().getLint().serialVersionUIDBroken.signal( + new String[] { myType.getResolvedTypeX().getName(), field.getName() }, sourceLocation, null); + } + } + + public void warnOnAddedInterface(String name, ISourceLocation sourceLocation) { + warnOnModifiedSerialVersionUID(sourceLocation, "added interface " + name); + } + + public void warnOnAddedMethod(Method method, ISourceLocation sourceLocation) { + warnOnModifiedSerialVersionUID(sourceLocation, "added non-private method " + method.getName()); + } + + public void warnOnAddedStaticInitializer(Shadow shadow, ISourceLocation sourceLocation) { + if (!hasClinit) { + warnOnModifiedSerialVersionUID(sourceLocation, "added static initializer"); + } + } + + public void warnOnModifiedSerialVersionUID(ISourceLocation sourceLocation, String reason) { + if (isSerializable && !hasSerialVersionUIDField) { + getWorld().getLint().needsSerialVersionUIDField.signal(new String[] { myType.getResolvedTypeX().getName().toString(), + reason }, sourceLocation, null); + } + } + + public World getWorld() { + return world; + } + + public List getMethodGens() { + return methodGens; // ???Collections.unmodifiableList(methodGens); + } + + public List getFieldGens() { + return fields; + } + + public boolean fieldExists(String name) { +// Field[] allFields = myGen.getFields(); +// if (allFields!=null) { +// for (int i=0;i Short.MAX_VALUE) { + reportClassTooBigProblem(); + return; + } + + if (annotations.size() > 0) { + for (AnnotationGen element : annotations) { + myGen.addAnnotation(element); + } + // Attribute[] annAttributes = + // org.aspectj.apache.bcel.classfile.Utility.getAnnotationAttributes( + // getConstantPool(),annotations); + // for (int i = 0; i < annAttributes.length; i++) { + // Attribute attribute = annAttributes[i]; + // System.err.println("Adding attribute for "+attribute); + // myGen.addAttribute(attribute); + // } + } + + // Add a weaver version attribute to the file being produced (if + // necessary...) + if (!myGen.hasAttribute("org.aspectj.weaver.WeaverVersion")) { + myGen.addAttribute(Utility.bcelAttribute(new AjAttribute.WeaverVersionInfo(), getConstantPool())); + } + + // see 389678: TODO more finessing possible here? + if (world.isOverWeaving()) { + if (myGen.hasAttribute(WeaverState.AttributeName) && myType!=null && myType.getWeaverState() != null) { + myGen.removeAttribute(myGen.getAttribute(WeaverState.AttributeName)); + myGen.addAttribute(Utility.bcelAttribute(new AjAttribute.WeaverState(myType.getWeaverState()), getConstantPool())); + } + } else { + if (!myGen.hasAttribute(WeaverState.AttributeName) && myType != null && myType.getWeaverState() != null) { + myGen.addAttribute(Utility.bcelAttribute(new AjAttribute.WeaverState(myType.getWeaverState()), getConstantPool())); + } + } + + // FIXME ATAJ needed only for slow Aspects.aspectOf() - keep or remove + // make a lot of test fail since the test compare weaved class file + // based on some test data as text files... + // if (!myGen.isInterface()) { + // addAjClassField(); + // } + + addAjcInitializers(); + + // 17Feb05 - ASC - Skip this for now - it crashes IBM 1.4.2 jvms + // (pr80430). Will be revisited when contents + // of attribute are confirmed to be correct. + boolean sourceDebugExtensionSupportSwitchedOn = false; + + if (sourceDebugExtensionSupportSwitchedOn) { + calculateSourceDebugExtensionOffsets(); + } + + int len = methodGens.size(); + myGen.setMethods(Method.NoMethods); + + for (LazyMethodGen gen : methodGens) { + // we skip empty clinits + if (isEmptyClinit(gen)) { + continue; + } + myGen.addMethod(gen.getMethod()); + } + + len = fields.size(); + myGen.setFields(Field.NoFields); + for (int i = 0; i < len; i++) { + BcelField gen = fields.get(i); + myGen.addField(gen.getField(cp)); + } + + if (sourceDebugExtensionSupportSwitchedOn) { + if (inlinedFiles.size() != 0) { + if (hasSourceDebugExtensionAttribute(myGen)) { + world.showMessage(IMessage.WARNING, WeaverMessages.format(WeaverMessages.OVERWRITE_JSR45, getFileName()), null, + null); + } + // myGen.addAttribute(getSourceDebugExtensionAttribute()); + } + } + + fixupGenericSignatureAttribute(); + } + + /** + * When working with Java generics, a signature attribute is attached to the type which indicates how it was declared. This + * routine ensures the signature attribute for the class we are about to write out is correct. Basically its responsibilities + * are: + *

    + *
  1. + * Checking whether the attribute needs changing (ie. did weaving change the type hierarchy) - if it did, remove the old + * attribute + *
  2. + * Check if we need an attribute at all, are we generic? are our supertypes parameterized/generic? + *
  3. + * Build the new attribute which includes all typevariable, supertype and superinterface information + *
+ */ + private void fixupGenericSignatureAttribute() { + + if (getWorld() != null && !getWorld().isInJava5Mode()) { + return; + } + + // TODO asc generics Temporarily assume that types we generate dont need + // a signature attribute (closure/etc).. will need + // revisiting no doubt... + // if (myType == null) { + // return; + // } + + // 1. Has anything changed that would require us to modify this + // attribute? + if (!regenerateGenericSignatureAttribute) { + return; + } + + // 2. Find the old attribute + Signature sigAttr = null; + if (myType != null) { // if null, this is a type built from scratch, it + // won't already have a sig attribute + sigAttr = (Signature) myGen.getAttribute("Signature"); + } + + // 3. Do we need an attribute? + boolean needAttribute = false; + // If we had one before, we definetly still need one as types can't be + // 'removed' from the hierarchy + if (sigAttr != null) { + needAttribute = true; + } + + // check the interfaces + if (!needAttribute) { + if (myType != null) { + ResolvedType[] interfaceRTXs = myType.getDeclaredInterfaces(); + for (int i = 0; i < interfaceRTXs.length; i++) { + ResolvedType typeX = interfaceRTXs[i]; + if (typeX.isGenericType() || typeX.isParameterizedType()) { + needAttribute = true; + } + } + if (extraSuperInterfaces != null) { + for (int i = 0; i < extraSuperInterfaces.length; i++) { + ResolvedType interfaceType = extraSuperInterfaces[i]; + if (interfaceType.isGenericType() || interfaceType.isParameterizedType()) { + needAttribute = true; + } + } + } + } + + if (myType == null) { + ResolvedType superclassRTX = superclass; + if (superclassRTX != null) { + if (superclassRTX.isGenericType() || superclassRTX.isParameterizedType()) { + needAttribute = true; + } + } + } else { + // check the supertype + ResolvedType superclassRTX = getSuperClass(); + if (superclassRTX.isGenericType() || superclassRTX.isParameterizedType()) { + needAttribute = true; + } + } + } + + if (needAttribute) { + StringBuffer signature = new StringBuffer(); + // first, the type variables... + if (myType != null) { + TypeVariable[] tVars = myType.getTypeVariables(); + if (tVars.length > 0) { + signature.append("<"); + for (int i = 0; i < tVars.length; i++) { + TypeVariable variable = tVars[i]; + signature.append(variable.getSignatureForAttribute()); + } + signature.append(">"); + } + } + // now the supertype + String supersig = getSuperClass().getSignatureForAttribute(); + signature.append(supersig); + if (myType != null) { + ResolvedType[] interfaceRTXs = myType.getDeclaredInterfaces(); + for (int i = 0; i < interfaceRTXs.length; i++) { + String s = interfaceRTXs[i].getSignatureForAttribute(); + signature.append(s); + } + if (extraSuperInterfaces != null) { + for (int i = 0; i < extraSuperInterfaces.length; i++) { + String s = extraSuperInterfaces[i].getSignatureForAttribute(); + signature.append(s); + } + } + } + if (sigAttr != null) { + myGen.removeAttribute(sigAttr); + } + myGen.addAttribute(createSignatureAttribute(signature.toString())); + } + } + + /** + * Helper method to create a signature attribute based on a string signature: e.g. "Ljava/lang/Object;LI;" + */ + private Signature createSignatureAttribute(String signature) { + int nameIndex = cp.addUtf8("Signature"); + int sigIndex = cp.addUtf8(signature); + return new Signature(nameIndex, 2, sigIndex, cp); + } + + /** + * + */ + private void reportClassTooBigProblem() { + // PR 59208 + // we've generated a class that is just toooooooooo big (you've been + // generating programs + // again haven't you? come on, admit it, no-one writes classes this big + // by hand). + // create an empty myGen so that we can give back a return value that + // doesn't upset the + // rest of the process. + myGen = new ClassGen(myGen.getClassName(), myGen.getSuperclassName(), myGen.getFileName(), myGen.getModifiers(), + myGen.getInterfaceNames()); + // raise an error against this compilation unit. + getWorld().showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.CLASS_TOO_BIG, this.getClassName()), + new SourceLocation(new File(myGen.getFileName()), 0), null); + } + + private static boolean hasSourceDebugExtensionAttribute(ClassGen gen) { + return gen.hasAttribute("SourceDebugExtension"); + } + + public JavaClass getJavaClass(BcelWorld world) { + writeBack(world); + return myGen.getJavaClass(); + } + + public byte[] getJavaClassBytesIncludingReweavable(BcelWorld world) { + writeBack(world); + byte[] wovenClassFileData = myGen.getJavaClass().getBytes(); + // At 1.6 stackmaps are optional, whilst at 1.7 and later they + // are required (unless turning off the verifier) + if ((myGen.getMajor() == Constants.MAJOR_1_6 && world.shouldGenerateStackMaps()) || myGen.getMajor() > Constants.MAJOR_1_6) { + if (!AsmDetector.isAsmAround) { + throw new BCException("Unable to find Asm for stackmap generation (Looking for 'aj.org.objectweb.asm.ClassReader'). Stackmap generation for woven code is required to avoid verify errors on a Java 1.7 or higher runtime"); + }; + wovenClassFileData = StackMapAdder.addStackMaps(world, wovenClassFileData); + } + + WeaverStateInfo wsi = myType.getWeaverState();// getOrCreateWeaverStateInfo(); + if (wsi != null && wsi.isReweavable() && !world.isOverWeaving()) { // && !reweavableDataInserted + // reweavableDataInserted = true; + return wsi.replaceKeyWithDiff(wovenClassFileData); + } else { + return wovenClassFileData; + } + } + + public void addGeneratedInner(LazyClassGen newClass) { + classGens.add(newClass); + } + + public void addInterface(ResolvedType newInterface, ISourceLocation sourceLocation) { + regenerateGenericSignatureAttribute = true; + + if (extraSuperInterfaces == null) { + extraSuperInterfaces = new ResolvedType[1]; + extraSuperInterfaces[0] = newInterface; + } else { + ResolvedType[] x = new ResolvedType[extraSuperInterfaces.length + 1]; + System.arraycopy(extraSuperInterfaces, 0, x, 1, extraSuperInterfaces.length); + x[0] = newInterface; + extraSuperInterfaces = x; + } + myGen.addInterface(newInterface.getRawName()); + if (!newInterface.equals(UnresolvedType.SERIALIZABLE)) { + warnOnAddedInterface(newInterface.getName(), sourceLocation); + } + } + + public void setSuperClass(ResolvedType newSuperclass) { + regenerateGenericSignatureAttribute = true; + superclass = newSuperclass; + // myType.addParent(typeX); // used for the attribute + if (newSuperclass.getGenericType() != null) { + newSuperclass = newSuperclass.getGenericType(); + } + myGen.setSuperclassName(newSuperclass.getName()); // used in the real + // class data + } + + // public String getSuperClassname() { + // return myGen.getSuperclassName(); + // } + + public ResolvedType getSuperClass() { + if (superclass != null) { + return superclass; + } + return myType.getSuperclass(); + } + + public String[] getInterfaceNames() { + return myGen.getInterfaceNames(); + } + + // non-recursive, may be a bug, ha ha. + private List getClassGens() { + List ret = new ArrayList(); + ret.add(this); + ret.addAll(classGens); + return ret; + } + + public List getChildClasses(BcelWorld world) { + if (classGens.isEmpty()) { + return Collections.emptyList(); + } + List ret = new ArrayList(); + for (LazyClassGen clazz : classGens) { + byte[] bytes = clazz.getJavaClass(world).getBytes(); + String name = clazz.getName(); + int index = name.lastIndexOf('$'); + // XXX this could be bad, check use of dollar signs. + name = name.substring(index + 1); + ret.add(new UnwovenClassFile.ChildClass(name, bytes)); + } + return ret; + } + + @Override + public String toString() { + return toShortString(); + } + + public String toShortString() { + String s = org.aspectj.apache.bcel.classfile.Utility.accessToString(myGen.getModifiers(), true); + if (!s.equals("")) { + s += " "; + } + s += org.aspectj.apache.bcel.classfile.Utility.classOrInterface(myGen.getModifiers()); + s += " "; + s += myGen.getClassName(); + return s; + } + + public String toLongString() { + ByteArrayOutputStream s = new ByteArrayOutputStream(); + print(new PrintStream(s)); + return new String(s.toByteArray()); + } + + public void print() { + print(System.out); + } + + public void print(PrintStream out) { + List classGens = getClassGens(); + for (Iterator iter = classGens.iterator(); iter.hasNext();) { + LazyClassGen element = iter.next(); + element.printOne(out); + if (iter.hasNext()) { + out.println(); + } + } + } + + private void printOne(PrintStream out) { + out.print(toShortString()); + out.print(" extends "); + out.print(org.aspectj.apache.bcel.classfile.Utility.compactClassName(myGen.getSuperclassName(), false)); + + int size = myGen.getInterfaces().length; + + if (size > 0) { + out.print(" implements "); + for (int i = 0; i < size; i++) { + out.print(myGen.getInterfaceNames()[i]); + if (i < size - 1) { + out.print(", "); + } + } + } + out.print(":"); + out.println(); + // XXX make sure to pass types correctly around, so this doesn't happen. + if (myType != null) { + myType.printWackyStuff(out); + } + Field[] fields = myGen.getFields(); + for (int i = 0, len = fields.length; i < len; i++) { + out.print(" "); + out.println(fields[i]); + } + List methodGens = getMethodGens(); + for (Iterator iter = methodGens.iterator(); iter.hasNext();) { + LazyMethodGen gen = iter.next(); + // we skip empty clinits + if (isEmptyClinit(gen)) { + continue; + } + gen.print(out, (myType != null ? myType.getWeaverVersionAttribute() : WeaverVersionInfo.UNKNOWN)); + if (iter.hasNext()) { + out.println(); + } + } + // out.println(" ATTRIBS: " + Arrays.asList(myGen.getAttributes())); + + out.println("end " + toShortString()); + } + + private boolean isEmptyClinit(LazyMethodGen gen) { + + if (!gen.getName().equals("")) { + return false; + } + // System.err.println("checking clinig: " + gen); + InstructionHandle start = gen.getBody().getStart(); + while (start != null) { + if (Range.isRangeHandle(start) || (start.getInstruction().opcode == Constants.RETURN)) { + start = start.getNext(); + } else { + return false; + } + } + + return true; + } + + public ConstantPool getConstantPool() { + return cp; + } + + public String getName() { + return myGen.getClassName(); + } + + public boolean isWoven() { + return myType.getWeaverState() != null; + } + + public boolean isReweavable() { + if (myType.getWeaverState() == null) { + return true; + } + return myType.getWeaverState().isReweavable(); + } + + public Set getAspectsAffectingType() { + if (myType.getWeaverState() == null) { + return null; + } + return myType.getWeaverState().getAspectsAffectingType(); + } + + public WeaverStateInfo getOrCreateWeaverStateInfo(boolean inReweavableMode) { + WeaverStateInfo ret = myType.getWeaverState(); + if (ret != null) { + return ret; + } + ret = new WeaverStateInfo(inReweavableMode); + myType.setWeaverState(ret); + return ret; + } + + public InstructionFactory getFactory() { + return fact; + } + + public LazyMethodGen getStaticInitializer() { + for (LazyMethodGen gen : methodGens) { + // OPTIMIZE persist kind of member into the gen object? for clinit + if (gen.getName().equals("")) { + return gen; + } + } + LazyMethodGen clinit = new LazyMethodGen(Modifier.STATIC, Type.VOID, "", new Type[0], NO_STRINGS, this); + clinit.getBody().insert(InstructionConstants.RETURN); + methodGens.add(clinit); + return clinit; + } + + /** + * Retrieve the ajc$preClinit method - this method captures any initialization AspectJ wants to ensure happens in a class. It is + * called from the static initializer. Maintaining this separation enables overweaving to ignore join points added due to + * earlier weaves. If the ajc$preClinit method cannot be found, it is created and a call to it is placed in the real static + * initializer (the call is placed at the start of the static initializer). + * + * @return the LazyMethodGen representing the ajc$ clinit + */ + public LazyMethodGen getAjcPreClinit() { + if (this.isInterface()) { + throw new IllegalStateException(); + } + for (LazyMethodGen methodGen : methodGens) { + if (methodGen.getName().equals(NameMangler.AJC_PRE_CLINIT_NAME)) { + return methodGen; + } + } + LazyMethodGen ajcPreClinit = new LazyMethodGen(Modifier.PRIVATE | Modifier.STATIC, Type.VOID, + NameMangler.AJC_PRE_CLINIT_NAME, Type.NO_ARGS, NO_STRINGS, this); + ajcPreClinit.getBody().insert(InstructionConstants.RETURN); + methodGens.add(ajcPreClinit); + getStaticInitializer().getBody().insert(Utility.createInvoke(fact, ajcPreClinit)); + return ajcPreClinit; + } + + /** + * factory method for building multiple extended clinit methods. Constructs a new clinit method that invokes the previous one + * and then returns it. The index is used as a name suffix. + * + * @param previousPreClinit + * @param i + */ + public LazyMethodGen createExtendedAjcPreClinit(LazyMethodGen previousPreClinit, int i) { + LazyMethodGen ajcPreClinit = new LazyMethodGen(Modifier.PRIVATE | Modifier.STATIC, Type.VOID, + NameMangler.AJC_PRE_CLINIT_NAME + i, Type.NO_ARGS, NO_STRINGS, this); + ajcPreClinit.getBody().insert(InstructionConstants.RETURN); + methodGens.add(ajcPreClinit); + previousPreClinit.getBody().insert(Utility.createInvoke(fact, ajcPreClinit)); + return ajcPreClinit; + } + + // + + // reflective thisJoinPoint support + private Map tjpFields = new HashMap(); + Map annotationCachingFieldCache = new HashMap(); + private int tjpFieldsCounter = -1; // -1 means not yet initialized + private int annoFieldsCounter = 0; + public static final ObjectType proceedingTjpType = new ObjectType("org.aspectj.lang.ProceedingJoinPoint"); + public static final ObjectType tjpType = new ObjectType("org.aspectj.lang.JoinPoint"); + public static final ObjectType staticTjpType = new ObjectType("org.aspectj.lang.JoinPoint$StaticPart"); + public static final ObjectType typeForAnnotation = new ObjectType("java.lang.annotation.Annotation"); + public static final ObjectType enclosingStaticTjpType = new ObjectType("org.aspectj.lang.JoinPoint$EnclosingStaticPart"); + private static final ObjectType sigType = new ObjectType("org.aspectj.lang.Signature"); + // private static final ObjectType slType = + // new ObjectType("org.aspectj.lang.reflect.SourceLocation"); + private static final ObjectType factoryType = new ObjectType("org.aspectj.runtime.reflect.Factory"); + private static final ObjectType classType = new ObjectType("java.lang.Class"); + + public Field getTjpField(BcelShadow shadow, final boolean isEnclosingJp) { + Field tjpField = tjpFields.get(shadow); + if (tjpField != null) { + return tjpField; + } + + int modifiers = Modifier.STATIC; + + // J9: Can't always be final on Java 9 because it is set outside of clinit + // But must be final in interface + if (shadow.getEnclosingClass().isInterface()) { + modifiers |= Modifier.FINAL; + } + + // XXX - Do we ever inline before or after advice? If we do, then we + // better include them in the check below. (or just change it to + // shadow.getEnclosingMethod().getCanInline()) + + // If the enclosing method is around advice, we could inline the join + // point that has led to this shadow. If we do that then the TJP we are + // creating here must be PUBLIC so it is visible to the type in which the + // advice is inlined. (PR71377) + LazyMethodGen encMethod = shadow.getEnclosingMethod(); + boolean shadowIsInAroundAdvice = false; + if (encMethod != null && encMethod.getName().startsWith(NameMangler.PREFIX + "around")) { + shadowIsInAroundAdvice = true; + } + + if (getType().isInterface() || shadowIsInAroundAdvice) { + modifiers |= Modifier.PUBLIC; + } else { + modifiers |= Modifier.PRIVATE; + } + ObjectType jpType = null; + // Did not have different static joinpoint types in 1.2 + if (world.isTargettingAspectJRuntime12()) { + jpType = staticTjpType; + } else { + jpType = isEnclosingJp ? enclosingStaticTjpType : staticTjpType; + } + if (tjpFieldsCounter == -1) { + // not yet initialized, do it now + if (!world.isOverWeaving()) { + tjpFieldsCounter = 0; + } else { + List existingFields = getFieldGens(); + if (existingFields == null) { + tjpFieldsCounter = 0; + } else { + BcelField lastField = null; + // OPTIMIZE: go from last to first? + for (BcelField field : existingFields) { + if (field.getName().startsWith("ajc$tjp_")) { + lastField = field; + } + } + if (lastField == null) { + tjpFieldsCounter = 0; + } else { + tjpFieldsCounter = Integer.parseInt(lastField.getName().substring(8)) + 1; + // System.out.println("tjp counter starting at " + tjpFieldsCounter); + } + } + } + } + if (!isInterface() && world.isTransientTjpFields()) { + modifiers|=Modifier.TRANSIENT; + } + FieldGen fGen = new FieldGen(modifiers, jpType, "ajc$tjp_" + tjpFieldsCounter++, getConstantPool()); + addField(fGen); + tjpField = fGen.getField(); + tjpFields.put(shadow, tjpField); + return tjpField; + } + + /** + * Create a field in the type containing the shadow where the annotation retrieved during binding can be stored - for later fast + * access. + * + * @param shadow the shadow at which the @annotation result is being cached + * @return a field + */ + public Field getAnnotationCachingField(BcelShadow shadow, ResolvedType toType, boolean isWithin) { + // Multiple annotation types at a shadow. A different field would be required for each + CacheKey cacheKey = new CacheKey(shadow, toType, isWithin); + Field field = annotationCachingFieldCache.get(cacheKey); + // System.out.println(field + " for shadow " + shadow); + if (field == null) { + // private static Annotation ajc$anno$ + StringBuilder sb = new StringBuilder(); + sb.append(NameMangler.ANNOTATION_CACHE_FIELD_NAME); + sb.append(annoFieldsCounter++); + FieldGen annotationCacheField = new FieldGen(Modifier.PRIVATE | Modifier.STATIC, typeForAnnotation, sb.toString(), cp); + addField(annotationCacheField); + field = annotationCacheField.getField(); + annotationCachingFieldCache.put(cacheKey, field); + } + return field; + } + + static class CacheKey { + private Object key; + private ResolvedType annotationType; + + // If the annotation is being accessed via @annotation on a shadow then we can use the shadows toString() (so two shadows + // the same share a variable), but if it is @withincode() or @within() we can't share them (as the shadows may look the same + // but be occurring 'within' different things). In the within cases we continue to use the shadow itself as the key. + CacheKey(BcelShadow shadow, ResolvedType annotationType, boolean isWithin) { + this.key = isWithin ? shadow : shadow.toString(); + this.annotationType = annotationType; + } + + @Override + public int hashCode() { + return key.hashCode() * 37 + annotationType.hashCode(); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof CacheKey)) { + return false; + } + CacheKey oCacheKey = (CacheKey) other; + return key.equals(oCacheKey.key) && annotationType.equals(oCacheKey.annotationType); + } + } + + // FIXME ATAJ needed only for slow Aspects.aspectOf - keep or remove + // private void addAjClassField() { + // // Andy: Why build it again?? + // Field ajClassField = new FieldGen( + // Modifier.PRIVATE | Modifier.FINAL | Modifier.STATIC, + // classType, + // "aj$class", + // getConstantPool()).getField(); + // addField(ajClassField); + // + // InstructionList il = new InstructionList(); + // il.append(new PUSH(getConstantPool(), getClassName())); + // il.append(fact.createInvoke("java.lang.Class", "forName", classType, + // new Type[] {Type.STRING}, Constants.INVOKESTATIC)); + // il.append(fact.createFieldAccess(getClassName(), ajClassField.getName(), + // classType, Constants.PUTSTATIC)); + // + // getStaticInitializer().getBody().insert(il); + // } + + private void addAjcInitializers() { + if (tjpFields.size() == 0 && !serialVersionUIDRequiresInitialization) { + return; + } + InstructionList[] il = null; + + if (tjpFields.size() > 0) { + il = initializeAllTjps(); + } + + if (serialVersionUIDRequiresInitialization) { + InstructionList[] ilSVUID = new InstructionList[1]; + ilSVUID[0] = new InstructionList(); + ilSVUID[0].append(InstructionFactory.PUSH(getConstantPool(), calculatedSerialVersionUID)); + ilSVUID[0].append(getFactory().createFieldAccess(getClassName(), "serialVersionUID", BasicType.LONG, + Constants.PUTSTATIC)); + if (il == null) { + il = ilSVUID; + } else { + InstructionList[] newIl = new InstructionList[il.length + ilSVUID.length]; + System.arraycopy(il, 0, newIl, 0, il.length); + System.arraycopy(ilSVUID, 0, newIl, il.length, ilSVUID.length); + il = newIl; + } + } + + LazyMethodGen prevMethod; + LazyMethodGen nextMethod = null; + if (this.isInterface()) { // Cannot sneak stuff into another static method in an interface + prevMethod = getStaticInitializer(); + } else { + prevMethod = getAjcPreClinit(); + } + for (int counter = 1; counter <= il.length; counter++) { + if (il.length > counter) { + nextMethod = createExtendedAjcPreClinit(prevMethod, counter); + } + prevMethod.getBody().insert(il[counter - 1]); + prevMethod = nextMethod; + } + } + + private InstructionList initInstructionList() { + InstructionList list = new InstructionList(); + InstructionFactory fact = getFactory(); + + // make a new factory + list.append(fact.createNew(factoryType)); + list.append(InstructionFactory.createDup(1)); + + list.append(InstructionFactory.PUSH(getConstantPool(), getFileName())); + + // load the current Class object + // XXX check that this works correctly for inners/anonymous + list.append(fact.PUSHCLASS(cp, myGen.getClassName())); + // XXX do we need to worry about the fact the theorectically this could + // throw + // a ClassNotFoundException + + list.append(fact.createInvoke(factoryType.getClassName(), "", Type.VOID, new Type[] { Type.STRING, classType }, + Constants.INVOKESPECIAL)); + + list.append(InstructionFactory.createStore(factoryType, 0)); + return list; + } + + private InstructionList[] initializeAllTjps() { + Vector lists = new Vector(); + + InstructionList list = initInstructionList(); + lists.add(list); + + List> entries = new ArrayList>(tjpFields.entrySet()); + Collections.sort(entries, new Comparator>() { + @Override + public int compare(Map.Entry a, Map.Entry b) { + return (a.getValue()).getName().compareTo((b.getValue()).getName()); + } + }); + + long estimatedSize = 0; + for (Iterator> i = entries.iterator(); i.hasNext();) { + Map.Entry entry = i.next(); + if (estimatedSize > Constants.MAX_CODE_SIZE) { + estimatedSize = 0; + list = initInstructionList(); + lists.add(list); + } + estimatedSize += entry.getValue().getSignature().getBytes().length; + initializeTjp(fact, list, entry.getValue(), entry.getKey()); + } + InstructionList listArrayModel[] = new InstructionList[1]; + return lists.toArray(listArrayModel); + } + + private void initializeTjp(InstructionFactory fact, InstructionList list, Field field, BcelShadow shadow) { + if (world.getTargetAspectjRuntimeLevel() == RuntimeVersion.V1_9) { + initializeTjpOptimal(fact, list, field, shadow); + return; + } + boolean fastSJP = false; + // avoid fast SJP if it is for an enclosing joinpoint + boolean isFastSJPAvailable = shadow.getWorld().isTargettingRuntime1_6_10() + && !enclosingStaticTjpType.equals(field.getType()); + + Member sig = shadow.getSignature(); + + // load the factory + list.append(InstructionFactory.createLoad(factoryType, 0)); + + // load the kind + list.append(InstructionFactory.PUSH(getConstantPool(), shadow.getKind().getName())); + + // create the signature + if (world.isTargettingAspectJRuntime12() || !isFastSJPAvailable || !sig.getKind().equals(Member.METHOD)) { + list.append(InstructionFactory.createLoad(factoryType, 0)); + } + + String signatureMakerName = SignatureUtils.getSignatureMakerName(sig); + ObjectType signatureType = new ObjectType(SignatureUtils.getSignatureType(sig)); + UnresolvedType[] exceptionTypes = null; + if (world.isTargettingAspectJRuntime12()) { // TAG:SUPPORTING12: We didn't have optimized factory methods in 1.2 + list.append(InstructionFactory.PUSH(cp, SignatureUtils.getSignatureString(sig, shadow.getWorld()))); + list.append(fact.createInvoke(factoryType.getClassName(), signatureMakerName, signatureType, Type.STRINGARRAY1, + Constants.INVOKEVIRTUAL)); + } else if (sig.getKind().equals(Member.METHOD)) { + BcelWorld w = shadow.getWorld(); + + // For methods, push the parts of the signature on. + list.append(InstructionFactory.PUSH(cp, makeString(sig.getModifiers(w)))); + list.append(InstructionFactory.PUSH(cp, sig.getName())); + list.append(InstructionFactory.PUSH(cp, makeString(sig.getDeclaringType()))); + list.append(InstructionFactory.PUSH(cp, makeString(sig.getParameterTypes()))); + list.append(InstructionFactory.PUSH(cp, makeString(sig.getParameterNames(w)))); + exceptionTypes = sig.getExceptions(w); + if (isFastSJPAvailable && exceptionTypes.length == 0) { + fastSJP = true; + } else { + list.append(InstructionFactory.PUSH(cp, makeString(exceptionTypes))); + } + list.append(InstructionFactory.PUSH(cp, makeString(sig.getReturnType()))); + // And generate a call to the variant of makeMethodSig() that takes the strings + if (isFastSJPAvailable) { + fastSJP = true; + } else { + list.append(fact.createInvoke(factoryType.getClassName(), signatureMakerName, signatureType, Type.STRINGARRAY7, + Constants.INVOKEVIRTUAL)); + } + + } else if (sig.getKind().equals(Member.MONITORENTER)) { + list.append(InstructionFactory.PUSH(cp, makeString(sig.getDeclaringType()))); + list.append(fact.createInvoke(factoryType.getClassName(), signatureMakerName, signatureType, Type.STRINGARRAY1, + Constants.INVOKEVIRTUAL)); + } else if (sig.getKind().equals(Member.MONITOREXIT)) { + list.append(InstructionFactory.PUSH(cp, makeString(sig.getDeclaringType()))); + list.append(fact.createInvoke(factoryType.getClassName(), signatureMakerName, signatureType, Type.STRINGARRAY1, + Constants.INVOKEVIRTUAL)); + } else if (sig.getKind().equals(Member.HANDLER)) { + BcelWorld w = shadow.getWorld(); + list.append(InstructionFactory.PUSH(cp, makeString(sig.getDeclaringType()))); + list.append(InstructionFactory.PUSH(cp, makeString(sig.getParameterTypes()))); + list.append(InstructionFactory.PUSH(cp, makeString(sig.getParameterNames(w)))); + list.append(fact.createInvoke(factoryType.getClassName(), signatureMakerName, signatureType, Type.STRINGARRAY3, + Constants.INVOKEVIRTUAL)); + } else if (sig.getKind().equals(Member.CONSTRUCTOR)) { + BcelWorld w = shadow.getWorld(); + if (w.isJoinpointArrayConstructionEnabled() && sig.getDeclaringType().isArray()) { + // its the magical new jp + list.append(InstructionFactory.PUSH(cp, makeString(Modifier.PUBLIC))); + list.append(InstructionFactory.PUSH(cp, makeString(sig.getDeclaringType()))); + list.append(InstructionFactory.PUSH(cp, makeString(sig.getParameterTypes()))); + list.append(InstructionFactory.PUSH(cp, "")); // sig.getParameterNames? + list.append(InstructionFactory.PUSH(cp, ""));// sig.getExceptions? + list.append(fact.createInvoke(factoryType.getClassName(), signatureMakerName, signatureType, Type.STRINGARRAY5, + Constants.INVOKEVIRTUAL)); + } else { + list.append(InstructionFactory.PUSH(cp, makeString(sig.getModifiers(w)))); + list.append(InstructionFactory.PUSH(cp, makeString(sig.getDeclaringType()))); + list.append(InstructionFactory.PUSH(cp, makeString(sig.getParameterTypes()))); + list.append(InstructionFactory.PUSH(cp, makeString(sig.getParameterNames(w)))); + list.append(InstructionFactory.PUSH(cp, makeString(sig.getExceptions(w)))); + list.append(fact.createInvoke(factoryType.getClassName(), signatureMakerName, signatureType, Type.STRINGARRAY5, + Constants.INVOKEVIRTUAL)); + } + } else if (sig.getKind().equals(Member.FIELD)) { + BcelWorld w = shadow.getWorld(); + list.append(InstructionFactory.PUSH(cp, makeString(sig.getModifiers(w)))); + list.append(InstructionFactory.PUSH(cp, sig.getName())); + // see pr227401 + UnresolvedType dType = sig.getDeclaringType(); + if (dType.getTypekind() == TypeKind.PARAMETERIZED || dType.getTypekind() == TypeKind.GENERIC) { + dType = sig.getDeclaringType().resolve(world).getGenericType(); + } + list.append(InstructionFactory.PUSH(cp, makeString(dType))); + list.append(InstructionFactory.PUSH(cp, makeString(sig.getReturnType()))); + list.append(fact.createInvoke(factoryType.getClassName(), signatureMakerName, signatureType, Type.STRINGARRAY4, + Constants.INVOKEVIRTUAL)); + } else if (sig.getKind().equals(Member.ADVICE)) { + BcelWorld w = shadow.getWorld(); + list.append(InstructionFactory.PUSH(cp, makeString(sig.getModifiers(w)))); + list.append(InstructionFactory.PUSH(cp, sig.getName())); + list.append(InstructionFactory.PUSH(cp, makeString(sig.getDeclaringType()))); + list.append(InstructionFactory.PUSH(cp, makeString(sig.getParameterTypes()))); + list.append(InstructionFactory.PUSH(cp, makeString(sig.getParameterNames(w)))); + list.append(InstructionFactory.PUSH(cp, makeString(sig.getExceptions(w)))); + list.append(InstructionFactory.PUSH(cp, makeString((sig.getReturnType())))); + list.append(fact.createInvoke(factoryType.getClassName(), signatureMakerName, signatureType, new Type[] { Type.STRING, + Type.STRING, Type.STRING, Type.STRING, Type.STRING, Type.STRING, Type.STRING }, Constants.INVOKEVIRTUAL)); + } else if (sig.getKind().equals(Member.STATIC_INITIALIZATION)) { + BcelWorld w = shadow.getWorld(); + list.append(InstructionFactory.PUSH(cp, makeString(sig.getModifiers(w)))); + list.append(InstructionFactory.PUSH(cp, makeString(sig.getDeclaringType()))); + list.append(fact.createInvoke(factoryType.getClassName(), signatureMakerName, signatureType, Type.STRINGARRAY2, + Constants.INVOKEVIRTUAL)); + } else { + // TODO looks like this block is unused code + list.append(InstructionFactory.PUSH(cp, SignatureUtils.getSignatureString(sig, shadow.getWorld()))); + list.append(fact.createInvoke(factoryType.getClassName(), signatureMakerName, signatureType, Type.STRINGARRAY1, + Constants.INVOKEVIRTUAL)); + } + + // XXX should load source location from shadow + list.append(Utility.createConstant(fact, shadow.getSourceLine())); + + final String factoryMethod; + + // TAG:SUPPORTING12: We didn't have makeESJP() in 1.2 + if (world.isTargettingAspectJRuntime12()) { + list.append(fact.createInvoke(factoryType.getClassName(), "makeSJP", staticTjpType, new Type[] { Type.STRING, sigType, + Type.INT }, Constants.INVOKEVIRTUAL)); + + // put it in the field + list.append(fact.createFieldAccess(getClassName(), field.getName(), staticTjpType, Constants.PUTSTATIC)); + + } else { + if (staticTjpType.equals(field.getType())) { + factoryMethod = "makeSJP"; + } else if (enclosingStaticTjpType.equals(field.getType())) { + factoryMethod = "makeESJP"; + } else { + throw new Error("should not happen"); + } + + if (fastSJP) { + if (exceptionTypes != null && exceptionTypes.length != 0) { + list.append(fact.createInvoke(factoryType.getClassName(), factoryMethod, field.getType(), ARRAY_8STRING_INT, + Constants.INVOKEVIRTUAL)); + } else { + list.append(fact.createInvoke(factoryType.getClassName(), factoryMethod, field.getType(), ARRAY_7STRING_INT, + Constants.INVOKEVIRTUAL)); + } + } else { + list.append(fact.createInvoke(factoryType.getClassName(), factoryMethod, field.getType(), new Type[] { Type.STRING, + sigType, Type.INT }, Constants.INVOKEVIRTUAL)); + } + + // put it in the field + list.append(fact.createFieldAccess(getClassName(), field.getName(), field.getType(), Constants.PUTSTATIC)); + } + } + + public String getFactoryMethod(Field field, BcelShadow shadow) { + StringBuilder b = new StringBuilder(); + b.append("make"); + MemberKind kind = shadow.getSignature().getKind(); + if (kind.equals(Member.METHOD)) { + b.append("Method"); + } else if (kind.equals(Member.CONSTRUCTOR)) { + b.append("Constructor"); + } else if (kind.equals(Member.HANDLER)) { + b.append("CatchClause"); + } else if (kind.equals(Member.FIELD)) { + b.append("Field"); + } else if (kind.equals(Member.STATIC_INITIALIZATION)) { + b.append("Initializer"); + } else if (kind.equals(Member.MONITORENTER)) { + b.append("Lock"); + } else if (kind.equals(Member.MONITOREXIT)) { + b.append("Unlock"); + } else if (kind.equals(Member.ADVICE)) { + b.append("Advice"); + } else { + throw new IllegalStateException(kind.toString()); + } + if (staticTjpType.equals(field.getType())) { + b.append("SJP"); + } else if (enclosingStaticTjpType.equals(field.getType())) { + b.append("ESJP"); + } + return b.toString(); + } + + /** + * Generate optimal joinpoint initialization code. + * + * As of version 1.9.1 the runtime includes new factory methods for joinpoints that take classes, not strings + * and using them requires different code generation. Using these instead of the old ones means we can avoid + * deferred classloading for these types. By using the LDC instruction that loads classes, it also means + * anything modifying woven code and changing type names will also pick up on these references. + */ + private void initializeTjpOptimal(InstructionFactory fact, InstructionList list, Field field, BcelShadow shadow) { + list.append(InstructionFactory.createLoad(factoryType, 0)); + pushString(list, shadow.getKind().getName()); + String factoryMethod = getFactoryMethod(field, shadow); + Member sig = shadow.getSignature(); + BcelWorld w = shadow.getWorld(); + + if (sig.getKind().equals(Member.METHOD)) { + pushInt(list, sig.getModifiers(w)); + pushString(list, sig.getName()); + pushClass(list, sig.getDeclaringType()); + pushClasses(list, sig.getParameterTypes()); + pushStrings(list, sig.getParameterNames(w)); + pushClasses(list, sig.getExceptions(w)); + pushClass(list, sig.getReturnType()); + pushInt(list, shadow.getSourceLine()); + list.append(fact.createInvoke(factoryType.getClassName(), factoryMethod, field.getType(), + PARAMSIGNATURE_MAKESJP_METHOD, Constants.INVOKEVIRTUAL)); + } else if (sig.getKind().equals(Member.CONSTRUCTOR)) { + if (w.isJoinpointArrayConstructionEnabled() && sig.getDeclaringType().isArray()) { + pushInt(list, Modifier.PUBLIC); + pushClass(list, sig.getDeclaringType()); + pushClasses(list, sig.getParameterTypes()); + pushStrings(list, null); + pushClasses(list, null); + } else { + pushInt(list, sig.getModifiers(w)); + pushClass(list, sig.getDeclaringType()); + pushClasses(list, sig.getParameterTypes()); + pushStrings(list, sig.getParameterNames(w)); + pushClasses(list, sig.getExceptions(w)); + } + pushInt(list, shadow.getSourceLine()); + list.append(fact.createInvoke(factoryType.getClassName(), factoryMethod, field.getType(), + PARAMSIGNATURE_MAKESJP_CONSTRUCTOR, Constants.INVOKEVIRTUAL)); + } else if (sig.getKind().equals(Member.HANDLER)) { + pushClass(list, sig.getDeclaringType()); + pushClass(list, sig.getParameterTypes()[0]); + String pname = null; + String[] pnames = sig.getParameterNames(w); + if (pnames != null && pnames.length>0) { + pname = pnames[0]; + } + pushString(list, pname); + pushInt(list, shadow.getSourceLine()); + list.append(fact.createInvoke(factoryType.getClassName(), factoryMethod, field.getType(), + PARAMSIGNATURE_MAKESJP_CATCHCLAUSE, Constants.INVOKEVIRTUAL)); + } else if (sig.getKind().equals(Member.FIELD)) { + pushInt(list, sig.getModifiers(w)); + pushString(list, sig.getName()); + // see pr227401 + UnresolvedType dType = sig.getDeclaringType(); + if (dType.getTypekind() == TypeKind.PARAMETERIZED || dType.getTypekind() == TypeKind.GENERIC) { + dType = sig.getDeclaringType().resolve(world).getGenericType(); + } + pushClass(list, dType); + pushClass(list, sig.getReturnType()); + pushInt(list,shadow.getSourceLine()); + list.append(fact.createInvoke(factoryType.getClassName(), factoryMethod, field.getType(), + PARAMSIGNATURE_MAKESJP_FIELD, Constants.INVOKEVIRTUAL)); + } else if (sig.getKind().equals(Member.STATIC_INITIALIZATION)) { + pushInt(list, sig.getModifiers(w)); + pushClass(list, sig.getDeclaringType()); + pushInt(list, shadow.getSourceLine()); + list.append(fact.createInvoke(factoryType.getClassName(), factoryMethod, field.getType(), + PARAMSIGNATURE_MAKESJP_INITIALIZER, Constants.INVOKEVIRTUAL)); + } else if (sig.getKind().equals(Member.MONITORENTER)) { + pushClass(list, sig.getDeclaringType()); + pushInt(list, shadow.getSourceLine()); + list.append(fact.createInvoke(factoryType.getClassName(), factoryMethod, field.getType(), + PARAMSIGNATURE_MAKESJP_MONITOR, Constants.INVOKEVIRTUAL)); + } else if (sig.getKind().equals(Member.MONITOREXIT)) { + pushClass(list, sig.getDeclaringType()); + pushInt(list, shadow.getSourceLine()); + list.append(fact.createInvoke(factoryType.getClassName(), factoryMethod, field.getType(), + PARAMSIGNATURE_MAKESJP_MONITOR, Constants.INVOKEVIRTUAL)); + } else if (sig.getKind().equals(Member.ADVICE)) { + pushInt(list, sig.getModifiers(w)); + pushString(list, sig.getName()); + pushClass(list, sig.getDeclaringType()); + pushClasses(list, sig.getParameterTypes()); + pushStrings(list, sig.getParameterNames(w)); + pushClasses(list, sig.getExceptions(w)); + pushClass(list, sig.getReturnType()); + pushInt(list, shadow.getSourceLine()); + list.append(fact.createInvoke(factoryType.getClassName(), factoryMethod, field.getType(), + PARAMSIGNATURE_MAKESJP_ADVICE, Constants.INVOKEVIRTUAL)); + } else { + throw new IllegalStateException("not sure what to do: "+shadow); + } + list.append(fact.createFieldAccess(getClassName(), field.getName(), field.getType(), Constants.PUTSTATIC)); + } + + private void pushStrings(InstructionList list, String[] strings) { + // Build an array loaded with the strings + if (strings == null || strings.length == 0) { + list.append(InstructionFactory.ACONST_NULL); + } else { + list.append(InstructionFactory.PUSH(cp, strings.length)); + list.append(fact.createNewArray(Type.STRING, (short)1)); + for (int s=0;s 0) { + buf.append(':'); + } + buf.append(makeString(types[i])); + } + return buf.toString(); + } + + protected String makeString(String[] names) { + if (names == null) { + return ""; + } + StringBuilder buf = new StringBuilder(); + for (int i = 0, len = names.length; i < len; i++) { + if (i > 0) { + buf.append(':'); + } + buf.append(names[i]); + } + return buf.toString(); + } + + public ResolvedType getType() { + if (myType == null) { + return null; + } + return myType.getResolvedTypeX(); + } + + public BcelObjectType getBcelObjectType() { + return myType; + } + + public String getFileName() { + return myGen.getFileName(); + } + + // for *new* fields + private void addField(FieldGen field) { + makeSyntheticAndTransientIfNeeded(field); + BcelField bcelField = null; + if (getBcelObjectType() != null) { + bcelField = new BcelField(getBcelObjectType(), field.getField()); + } else { + bcelField = new BcelField(getName(), field.getField(), world); + } + fields.add(bcelField); + // myGen.addField(field.getField()); + } + + private void makeSyntheticAndTransientIfNeeded(FieldGen field) { + if (field.getName().startsWith(NameMangler.PREFIX) && !field.getName().startsWith("ajc$interField$") + && !field.getName().startsWith("ajc$instance$")) { + // it's an aj added field + // first do transient + if (!field.isStatic()) { + field.setModifiers(field.getModifiers() | Constants.ACC_TRANSIENT); + } + // then do synthetic + if (getWorld().isInJava5Mode()) { + // add the synthetic modifier flag + field.setModifiers(field.getModifiers() | ACC_SYNTHETIC); + } + if (!hasSyntheticAttribute(field.getAttributes())) { + // belt and braces, do the attribute even on Java 5 in addition + // to the modifier flag + // Attribute[] oldAttrs = field.getAttributes(); + // Attribute[] newAttrs = new Attribute[oldAttrs.length + 1]; + // System.arraycopy(oldAttrs, 0, newAttrs, 0, oldAttrs.length); + ConstantPool cpg = myGen.getConstantPool(); + int index = cpg.addUtf8("Synthetic"); + Attribute synthetic = new Synthetic(index, 0, new byte[0], cpg); + field.addAttribute(synthetic); + // newAttrs[newAttrs.length - 1] = synthetic; + // field.setAttributes(newAttrs); + } + } + } + + private boolean hasSyntheticAttribute(List attributes) { + for (int i = 0; i < attributes.size(); i++) { + if ((attributes.get(i)).getName().equals("Synthetic")) { + return true; + } + } + return false; + } + + public void addField(FieldGen field, ISourceLocation sourceLocation) { + addField(field); + if (!(field.isPrivate() && (field.isStatic() || field.isTransient()))) { + errorOnAddedField(field, sourceLocation); + } + } + + public String getClassName() { + return myGen.getClassName(); + } + + public boolean isInterface() { + return myGen.isInterface(); + } + + public boolean isAbstract() { + return myGen.isAbstract(); + } + + public LazyMethodGen getLazyMethodGen(Member m) { + return getLazyMethodGen(m.getName(), m.getSignature(), false); + } + + public LazyMethodGen getLazyMethodGen(String name, String signature) { + return getLazyMethodGen(name, signature, false); + } + + public LazyMethodGen getLazyMethodGen(String name, String signature, boolean allowMissing) { + for (LazyMethodGen gen : methodGens) { + if (gen.getName().equals(name) && gen.getSignature().equals(signature)) { + return gen; + } + } + + if (!allowMissing) { + throw new BCException("Class " + this.getName() + " does not have a method " + name + " with signature " + signature); + } + + return null; + } + + public void forcePublic() { + myGen.setModifiers(Utility.makePublic(myGen.getModifiers())); + } + + public boolean hasAnnotation(UnresolvedType t) { + + // annotations on the real thing + AnnotationGen agens[] = myGen.getAnnotations(); + if (agens == null) { + return false; + } + for (int i = 0; i < agens.length; i++) { + AnnotationGen gen = agens[i]; + if (t.equals(UnresolvedType.forSignature(gen.getTypeSignature()))) { + return true; + } + } + + // annotations added during this weave + + return false; + } + + public void addAnnotation(AnnotationGen a) { + if (!hasAnnotation(UnresolvedType.forSignature(a.getTypeSignature()))) { + annotations.add(new AnnotationGen(a, getConstantPool(), true)); + } + } + + public void addAttribute(AjAttribute attribute) { + myGen.addAttribute(Utility.bcelAttribute(attribute, getConstantPool())); + } + + public void addAttribute(Attribute attribute) { + myGen.addAttribute(attribute); + } + + public Collection getAttributes() { + return myGen.getAttributes(); + } + + // this test is like asking: + // if + // (UnresolvedType.SERIALIZABLE.resolve(getType().getWorld()).isAssignableFrom + // (getType())) { + // only we don't do that because this forces us to find all the supertypes + // of the type, + // and if one of them is missing we fail, and it's not worth failing just to + // put out + // a warning message! + private boolean implementsSerializable(ResolvedType aType) { + if (aType.getSignature().equals(UnresolvedType.SERIALIZABLE.getSignature())) { + return true; + } + + ResolvedType[] interfaces = aType.getDeclaredInterfaces(); + for (int i = 0; i < interfaces.length; i++) { + if (interfaces[i].isMissing()) { + continue; + } + if (implementsSerializable(interfaces[i])) { + return true; + } + } + ResolvedType superType = aType.getSuperclass(); + if (superType != null && !superType.isMissing()) { + return implementsSerializable(superType); + } + return false; + } + + public boolean isAtLeastJava5() { + return (myGen.getMajor() >= Constants.MAJOR_1_5); + } + + /** + * Return the next available field name with the specified 'prefix', e.g. for prefix 'class$' where class$0, class$1 exist then + * return class$2 + */ + public String allocateField(String prefix) { + int highestAllocated = -1; + List fs = getFieldGens(); + for (BcelField field : fs) { + if (field.getName().startsWith(prefix)) { + try { + int num = Integer.parseInt(field.getName().substring(prefix.length())); + if (num > highestAllocated) { + highestAllocated = num; + } + } catch (NumberFormatException nfe) { + // something wrong with the number on the end of that + // field... + } + } + } + return prefix + Integer.toString(highestAllocated + 1); + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/LazyMethodGen.java b/weaver/src/main/java/org/aspectj/weaver/bcel/LazyMethodGen.java new file mode 100644 index 000000000..2655a3456 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/LazyMethodGen.java @@ -0,0 +1,1870 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.bcel; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Set; + +import org.aspectj.apache.bcel.Constants; +import org.aspectj.apache.bcel.classfile.Attribute; +import org.aspectj.apache.bcel.classfile.ConstantPool; +import org.aspectj.apache.bcel.classfile.Method; +import org.aspectj.apache.bcel.classfile.Synthetic; +import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; +import org.aspectj.apache.bcel.generic.BranchHandle; +import org.aspectj.apache.bcel.generic.ClassGenException; +import org.aspectj.apache.bcel.generic.CodeExceptionGen; +import org.aspectj.apache.bcel.generic.Instruction; +import org.aspectj.apache.bcel.generic.InstructionBranch; +import org.aspectj.apache.bcel.generic.InstructionHandle; +import org.aspectj.apache.bcel.generic.InstructionList; +import org.aspectj.apache.bcel.generic.InstructionSelect; +import org.aspectj.apache.bcel.generic.InstructionTargeter; +import org.aspectj.apache.bcel.generic.LineNumberTag; +import org.aspectj.apache.bcel.generic.LocalVariableTag; +import org.aspectj.apache.bcel.generic.MethodGen; +import org.aspectj.apache.bcel.generic.ObjectType; +import org.aspectj.apache.bcel.generic.Tag; +import org.aspectj.apache.bcel.generic.TargetLostException; +import org.aspectj.apache.bcel.generic.Type; +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.weaver.AjAttribute; +import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; +import org.aspectj.weaver.AnnotationAJ; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.MemberImpl; +import org.aspectj.weaver.NameMangler; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; +import org.aspectj.weaver.tools.Traceable; + +/** + * A LazyMethodGen should be treated as a MethodGen. It's our way of abstracting over the low-level Method objects. It converts + * through {@link MethodGen} to create and to serialize, but that's it. + * + *

+ * At any rate, there are two ways to create LazyMethodGens. One is from a method, which does work through MethodGen to do the + * correct thing. The other is the creation of a completely empty LazyMethodGen, and it is used when we're constructing code from + * scratch. + * + *

+ * We stay away from targeters for rangey things like Shadows and Exceptions. + */ +public final class LazyMethodGen implements Traceable { + + private static final AnnotationAJ[] NO_ANNOTATIONAJ = new AnnotationAJ[] {}; + + private int modifiers; + private Type returnType; + private final String name; + private Type[] argumentTypes; + // private final String[] argumentNames; + private String[] declaredExceptions; + private InstructionList body; + private List attributes; + private List newAnnotations; + private List annotationsForRemoval; + private AnnotationAJ[][] newParameterAnnotations; + private final LazyClassGen enclosingClass; + private BcelMethod memberView; + private AjAttribute.EffectiveSignatureAttribute effectiveSignature; + int highestLineNumber = 0; + boolean wasPackedOptimally = false; + private Method savedMethod = null; + + // Some tools that may post process the output bytecode do not long local variable tables + // to be generated as one reason the tables may be missing in the first place is because + // the bytecode is odd. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=470658 + private final boolean originalMethodHasLocalVariableTable; + + /* + * We use LineNumberTags and not Gens. + * + * This option specifies whether we let the BCEL classes create LineNumberGens and LocalVariableGens or if we make it create + * LineNumberTags and LocalVariableTags. Up until 1.5.1 we always created Gens - then on return from the MethodGen ctor we took + * them apart, reprocessed them all and created Tags. (see unpackLocals/unpackLineNumbers). As we have our own copy of Bcel, why + * not create the right thing straightaway? So setting this to true will call the MethodGen ctor() in such a way that it creates + * Tags - removing the need for unpackLocals/unpackLineNumbers - HOWEVER see the ensureAllLineNumberSetup() method for some + * other relevant info. + * + * Whats the difference between a Tag and a Gen? A Tag is more lightweight, it doesn't know which instructions it targets, it + * relies on the instructions targettingit - this reduces the amount of targeter manipulation we have to do. + */ + + /** + * This is nonnull if this method is the result of an "inlining". We currently copy methods into other classes for around + * advice. We add this field so we can get JSR45 information correct. If/when we do _actual_ inlining, we'll need to subtype + * LineNumberTag to have external line numbers. + */ + String fromFilename = null; + private int maxLocals; + private boolean canInline = true; + private boolean isSynthetic = false; + List matchedShadows; + // Used for interface introduction - this is the type of the interface the method is technically on + public ResolvedType definingType = null; + + static class LightweightBcelMethod extends BcelMethod { + + LightweightBcelMethod(BcelObjectType declaringType, Method method) { + super(declaringType, method); + // TODO Auto-generated constructor stub + } + + } + + public LazyMethodGen(int modifiers, Type returnType, String name, Type[] paramTypes, String[] declaredExceptions, + LazyClassGen enclosingClass) { + // enclosingClass.getName() + ", " + returnType); + this.memberView = null; // should be okay, since constructed ones aren't woven into + this.modifiers = modifiers; + this.returnType = returnType; + this.name = name; + this.argumentTypes = paramTypes; + // this.argumentNames = Utility.makeArgNames(paramTypes.length); + this.declaredExceptions = declaredExceptions; + if (!Modifier.isAbstract(modifiers)) { + body = new InstructionList(); + setMaxLocals(calculateMaxLocals()); + } else { + body = null; + } + this.attributes = new ArrayList(); + this.enclosingClass = enclosingClass; + assertGoodBody(); + this.originalMethodHasLocalVariableTable = true; // it is a new method, we want an lvar table + + // @AJ advice are not inlined by default since requires further analysis and weaving ordering control + // TODO AV - improve - note: no room for improvement as long as aspects are reweavable + // since the inlined version with wrappers and an to be done annotation to keep + // inline state will be garbaged due to reweavable impl + if (memberView != null && isAdviceMethod()) { + if (enclosingClass.getType().isAnnotationStyleAspect()) { + // TODO we could check for @Around advice as well + this.canInline = false; + } + } + } + + private int calculateMaxLocals() { + int ret = Modifier.isStatic(modifiers) ? 0 : 1; // will there be a 'this'? + for (Type type : argumentTypes) { + ret += type.getSize(); + } + return ret; + } + + // build from an existing method, lazy build saves most work for + // initialization + public LazyMethodGen(Method m, LazyClassGen enclosingClass) { + savedMethod = m; + + this.enclosingClass = enclosingClass; + if (!(m.isAbstract() || m.isNative()) && m.getCode() == null) { + throw new RuntimeException("bad non-abstract method with no code: " + m + " on " + enclosingClass); + } + if ((m.isAbstract() || m.isNative()) && m.getCode() != null) { + throw new RuntimeException("bad abstract method with code: " + m + " on " + enclosingClass); + } + this.memberView = new BcelMethod(enclosingClass.getBcelObjectType(), m); + this.originalMethodHasLocalVariableTable = savedMethod.getLocalVariableTable()!=null; + this.modifiers = m.getModifiers(); + this.name = m.getName(); + + // @AJ advice are not inlined by default since requires further analysis + // and weaving ordering control + // TODO AV - improve - note: no room for improvement as long as aspects + // are reweavable + // since the inlined version with wrappers and an to be done annotation + // to keep + // inline state will be garbaged due to reweavable impl + if (memberView != null && isAdviceMethod()) { + if (enclosingClass.getType().isAnnotationStyleAspect()) { + // TODO we could check for @Around advice as well + this.canInline = false; + } + } + } + + private boolean isAbstractOrNative(int modifiers) { + return Modifier.isAbstract(modifiers) || Modifier.isNative(modifiers); + } + + public LazyMethodGen(BcelMethod m, LazyClassGen enclosingClass) { + savedMethod = m.getMethod(); + this.enclosingClass = enclosingClass; + if (!isAbstractOrNative(m.getModifiers()) && savedMethod.getCode() == null) { + throw new RuntimeException("bad non-abstract method with no code: " + m + " on " + enclosingClass); + } + if (isAbstractOrNative(m.getModifiers()) && savedMethod.getCode() != null) { + throw new RuntimeException("bad abstract method with code: " + m + " on " + enclosingClass); + } + // this.memberView = new BcelMethod(enclosingClass.getBcelObjectType(), + // m); + this.memberView = m; + this.modifiers = savedMethod.getModifiers(); + this.name = m.getName(); + this.originalMethodHasLocalVariableTable = savedMethod.getLocalVariableTable() != null; + // @AJ advice are not inlined by default since requires further analysis + // and weaving ordering control + // TODO AV - improve - note: no room for improvement as long as aspects + // are reweavable + // since the inlined version with wrappers and an to be done annotation + // to keep + // inline state will be garbaged due to reweavable impl + if (memberView != null && isAdviceMethod()) { + if (enclosingClass.getType().isAnnotationStyleAspect()) { + // TODO we could check for @Around advice as well + this.canInline = false; + } + } + + } + + public boolean hasDeclaredLineNumberInfo() { + return (memberView != null && memberView.hasDeclarationLineNumberInfo()); + } + + public int getDeclarationLineNumber() { + if (hasDeclaredLineNumberInfo()) { + return memberView.getDeclarationLineNumber(); + } else { + return -1; + } + } + + public int getDeclarationOffset() { + if (hasDeclaredLineNumberInfo()) { + return memberView.getDeclarationOffset(); + } else { + return 0; + } + } + + public void addAnnotation(AnnotationAJ ax) { + initialize(); + if (memberView == null) { + // If member view is null, we manage them in newAnnotations + if (newAnnotations == null) { + newAnnotations = new ArrayList(); + } + newAnnotations.add(ax); + } else { + memberView.addAnnotation(ax); + } + } + + public void removeAnnotation(ResolvedType annotationType) { + initialize(); + if (memberView == null) { + // If member view is null, we manage them in newAnnotations + if (annotationsForRemoval == null) { + annotationsForRemoval = new ArrayList(); + } + annotationsForRemoval.add(annotationType); + } else { + memberView.removeAnnotation(annotationType); + } + } + + public void addParameterAnnotation(int parameterNumber, AnnotationAJ anno) { + initialize(); + if (memberView == null) { + if (newParameterAnnotations == null) { + // time to create it + int pcount = getArgumentTypes().length; + newParameterAnnotations = new AnnotationAJ[pcount][]; + for (int i = 0; i < pcount; i++) { + if (i == parameterNumber) { + newParameterAnnotations[i] = new AnnotationAJ[1]; + newParameterAnnotations[i][0] = anno; + } else { + newParameterAnnotations[i] = NO_ANNOTATIONAJ; + } + } + } else { + AnnotationAJ[] currentAnnoArray = newParameterAnnotations[parameterNumber]; + AnnotationAJ[] newAnnoArray = new AnnotationAJ[currentAnnoArray.length + 1]; + System.arraycopy(currentAnnoArray, 0, newAnnoArray, 0, currentAnnoArray.length); + newAnnoArray[currentAnnoArray.length] = anno; + newParameterAnnotations[parameterNumber] = newAnnoArray; + } + } else { + memberView.addParameterAnnotation(parameterNumber, anno); + } + } + + public ResolvedType[] getAnnotationTypes() { + initialize(); + if (memberView == null && newAnnotations!=null && newAnnotations.size()!=0) { + // TODO Ignoring removed annotations for now + ResolvedType[] annotationTypes = new ResolvedType[newAnnotations.size()]; + for (int a=0,len=newAnnotations.size();a 0) hasExceptionHandlers = true; + int priority = len - 1; + for (int i = 0; i < len; i++, priority--) { + CodeExceptionGen exn = exns[i]; + + InstructionHandle start = Range.genStart(body, getOutermostExceptionStart(exn.getStartPC())); + InstructionHandle end = Range.genEnd(body, getOutermostExceptionEnd(exn.getEndPC())); + // this doesn't necessarily handle overlapping correctly!!! + ExceptionRange er = new ExceptionRange(body, exn.getCatchType() == null ? null : BcelWorld.fromBcel(exn + .getCatchType()), priority); + er.associateWithTargets(start, end, exn.getHandlerPC()); + exn.setStartPC(null); // also removes from target + exn.setEndPC(null); // also removes from target + exn.setHandlerPC(null); // also removes from target + } + gen.removeExceptionHandlers(); + } + } + + private InstructionHandle getOutermostExceptionStart(InstructionHandle ih) { + while (true) { + if (ExceptionRange.isExceptionStart(ih.getPrev())) { + ih = ih.getPrev(); + } else { + return ih; + } + } + } + + private InstructionHandle getOutermostExceptionEnd(InstructionHandle ih) { + while (true) { + if (ExceptionRange.isExceptionEnd(ih.getNext())) { + ih = ih.getNext(); + } else { + return ih; + } + } + } + + /** + * On entry to this method we have a method whose instruction stream contains a few instructions that have line numbers assigned + * to them (LineNumberTags). The aim is to ensure every instruction has the right line number. This is necessary because some of + * them may be extracted out into other methods - and it'd be useful for them to maintain the source line number for debugging. + */ + public void ensureAllLineNumberSetup() { + LineNumberTag lastKnownLineNumberTag = null; + boolean skip = false; + for (InstructionHandle ih = body.getStart(); ih != null; ih = ih.getNext()) { + skip = false; + for (InstructionTargeter targeter : ih.getTargeters()) { + if (targeter instanceof LineNumberTag) { + lastKnownLineNumberTag = (LineNumberTag) targeter; + skip = true; + } + } + if (lastKnownLineNumberTag != null && !skip) { + ih.addTargeter(lastKnownLineNumberTag); + } + } + } + + // =============== + + public int allocateLocal(Type type) { + return allocateLocal(type.getSize()); + } + + public int allocateLocal(int slots) { + int max = getMaxLocals(); + setMaxLocals(max + slots); + return max; + } + + public Method getMethod() { + if (savedMethod != null) { + return savedMethod; // ??? this relies on gentle treatment of + // constant pool + } + + try { + MethodGen gen = pack(); + savedMethod = gen.getMethod(); + return savedMethod; + } catch (ClassGenException e) { + enclosingClass + .getBcelObjectType() + .getResolvedTypeX() + .getWorld() + .showMessage( + IMessage.ERROR, + WeaverMessages.format(WeaverMessages.PROBLEM_GENERATING_METHOD, this.getClassName(), this.getName(), + e.getMessage()), + this.getMemberView() == null ? null : this.getMemberView().getSourceLocation(), null); + // throw e; PR 70201.... let the normal problem reporting + // infrastructure deal with this rather than crashing. + body = null; + MethodGen gen = pack(); + return gen.getMethod(); + } catch (RuntimeException re) { + if (re.getCause() instanceof ClassGenException) { + enclosingClass + .getBcelObjectType() + .getResolvedTypeX() + .getWorld() + .showMessage( + IMessage.ERROR, + WeaverMessages.format(WeaverMessages.PROBLEM_GENERATING_METHOD, this.getClassName(), + this.getName(), re.getCause().getMessage()), + this.getMemberView() == null ? null : this.getMemberView().getSourceLocation(), null); + // throw e; PR 70201.... let the normal problem reporting + // infrastructure deal with this rather than crashing. + body = null; + MethodGen gen = pack(); + return gen.getMethod(); + } + throw re; + } + } + + public void markAsChanged() { + if (wasPackedOptimally) { + throw new RuntimeException("Already packed method is being re-modified: " + getClassName() + " " + toShortString()); + } + initialize(); + savedMethod = null; + } + + // ============================= + + @Override + public String toString() { + BcelObjectType bot = enclosingClass.getBcelObjectType(); + WeaverVersionInfo weaverVersion = (bot == null ? WeaverVersionInfo.CURRENT : bot.getWeaverVersionAttribute()); + return toLongString(weaverVersion); + } + + public String toShortString() { + String access = org.aspectj.apache.bcel.classfile.Utility.accessToString(getAccessFlags()); + + StringBuffer buf = new StringBuffer(); + + if (!access.equals("")) { + buf.append(access); + buf.append(" "); + } + buf.append(org.aspectj.apache.bcel.classfile.Utility.signatureToString(getReturnType().getSignature(), true)); + buf.append(" "); + buf.append(getName()); + buf.append("("); + { + int len = argumentTypes.length; + if (len > 0) { + buf.append(org.aspectj.apache.bcel.classfile.Utility.signatureToString(argumentTypes[0].getSignature(), true)); + for (int i = 1; i < argumentTypes.length; i++) { + buf.append(", "); + buf.append(org.aspectj.apache.bcel.classfile.Utility.signatureToString(argumentTypes[i].getSignature(), true)); + } + } + } + buf.append(")"); + + { + int len = declaredExceptions != null ? declaredExceptions.length : 0; + if (len > 0) { + buf.append(" throws "); + buf.append(declaredExceptions[0]); + for (int i = 1; i < declaredExceptions.length; i++) { + buf.append(", "); + buf.append(declaredExceptions[i]); + } + } + } + return buf.toString(); + } + + public String toLongString(WeaverVersionInfo weaverVersion) { + ByteArrayOutputStream s = new ByteArrayOutputStream(); + print(new PrintStream(s), weaverVersion); + return new String(s.toByteArray()); + } + + public void print(WeaverVersionInfo weaverVersion) { + print(System.out, weaverVersion); + } + + public void print(PrintStream out, WeaverVersionInfo weaverVersion) { + out.print(" " + toShortString()); + printAspectAttributes(out, weaverVersion); + + InstructionList body = getBody(); + if (body == null) { + out.println(";"); + return; + } + out.println(":"); + new BodyPrinter(out).run(); + out.println(" end " + toShortString()); + } + + private void printAspectAttributes(PrintStream out, WeaverVersionInfo weaverVersion) { + ISourceContext context = null; + if (enclosingClass != null && enclosingClass.getType() != null) { + context = enclosingClass.getType().getSourceContext(); + } + List as = Utility.readAjAttributes(getClassName(), attributes.toArray(new Attribute[] {}), context, null, weaverVersion, + new BcelConstantPoolReader(this.enclosingClass.getConstantPool())); + if (!as.isEmpty()) { + out.println(" " + as.get(0)); // XXX assuming exactly one + // attribute, munger... + } + } + + private class BodyPrinter { + Map labelMap = new HashMap(); + + InstructionList body; + PrintStream out; + ConstantPool pool; + + BodyPrinter(PrintStream out) { + this.pool = enclosingClass.getConstantPool(); + this.body = getBodyForPrint(); + this.out = out; + } + + BodyPrinter(PrintStream out, InstructionList il) { + this.pool = enclosingClass.getConstantPool(); + this.body = il; + this.out = out; + } + + void run() { + // killNops(); + assignLabels(); + print(); + } + + // label assignment + void assignLabels() { + LinkedList exnTable = new LinkedList(); + String pendingLabel = null; + // boolean hasPendingTargeters = false; + int lcounter = 0; + for (InstructionHandle ih = body.getStart(); ih != null; ih = ih.getNext()) { + Iterator tIter = ih.getTargeters().iterator(); + while (tIter.hasNext()) { + InstructionTargeter t = tIter.next();// targeters + // [ + // i + // ] + // ; + if (t instanceof ExceptionRange) { + // assert isRangeHandle(h); + ExceptionRange r = (ExceptionRange) t; + if (r.getStart() == ih) { + insertHandler(r, exnTable); + } + } else if (t instanceof InstructionBranch) { + if (pendingLabel == null) { + pendingLabel = "L" + lcounter++; + } + } else { + // assert isRangeHandle(h) + } + } + if (pendingLabel != null) { + labelMap.put(ih, pendingLabel); + if (!Range.isRangeHandle(ih)) { + pendingLabel = null; + } + } + } + int ecounter = 0; + for (ExceptionRange er: exnTable) { + String exceptionLabel = "E" + ecounter++; + labelMap.put(Range.getRealStart(er.getHandler()), exceptionLabel); + labelMap.put(er.getHandler(), exceptionLabel); + } + } + + // printing + + void print() { + int depth = 0; + int currLine = -1; + bodyPrint: for (InstructionHandle ih = body.getStart(); ih != null; ih = ih.getNext()) { + if (Range.isRangeHandle(ih)) { + Range r = Range.getRange(ih); + // don't print empty ranges, that is, ranges who contain no + // actual instructions + for (InstructionHandle xx = r.getStart(); Range.isRangeHandle(xx); xx = xx.getNext()) { + if (xx == r.getEnd()) { + continue bodyPrint; + } + } + + // doesn't handle nested: if (r.getStart().getNext() == + // r.getEnd()) continue; + if (r.getStart() == ih) { + printRangeString(r, depth++); + } else { + if (r.getEnd() != ih) { + throw new RuntimeException("bad"); + } + printRangeString(r, --depth); + } + } else { + printInstruction(ih, depth); + int line = getLineNumber(ih, currLine); + if (line != currLine) { + currLine = line; + out.println(" (line " + line + ")"); + } else { + out.println(); + } + } + } + } + + void printRangeString(Range r, int depth) { + printDepth(depth); + out.println(getRangeString(r, labelMap)); + } + + String getRangeString(Range r, Map labelMap) { + if (r instanceof ExceptionRange) { + ExceptionRange er = (ExceptionRange) r; + return er.toString() + " -> " + labelMap.get(er.getHandler()); + // + // + " PRI " + er.getPriority(); + } else { + return r.toString(); + } + } + + void printDepth(int depth) { + pad(BODY_INDENT); + while (depth > 0) { + out.print("| "); + depth--; + } + } + + void printLabel(String s, int depth) { + int space = Math.max(CODE_INDENT - depth * 2, 0); + if (s == null) { + pad(space); + } else { + space = Math.max(space - (s.length() + 2), 0); + pad(space); + out.print(s); + out.print(": "); + } + } + + void printInstruction(InstructionHandle h, int depth) { + printDepth(depth); + printLabel(labelMap.get(h), depth); + + Instruction inst = h.getInstruction(); + if (inst.isConstantPoolInstruction()) { + out.print(Constants.OPCODE_NAMES[inst.opcode].toUpperCase()); + out.print(" "); + out.print(pool.constantToString(pool.getConstant(inst.getIndex()))); + } else if (inst instanceof InstructionSelect) { + InstructionSelect sinst = (InstructionSelect) inst; + out.println(Constants.OPCODE_NAMES[sinst.opcode].toUpperCase()); + int[] matches = sinst.getMatchs(); + InstructionHandle[] targets = sinst.getTargets(); + InstructionHandle defaultTarget = sinst.getTarget(); + for (int i = 0, len = matches.length; i < len; i++) { + printDepth(depth); + printLabel(null, depth); + out.print(" "); + out.print(matches[i]); + out.print(": \t"); + out.println(labelMap.get(targets[i])); + } + printDepth(depth); + printLabel(null, depth); + out.print(" "); + out.print("default: \t"); + out.print(labelMap.get(defaultTarget)); + } else if (inst instanceof InstructionBranch) { + InstructionBranch brinst = (InstructionBranch) inst; + out.print(Constants.OPCODE_NAMES[brinst.getOpcode()].toUpperCase()); + out.print(" "); + out.print(labelMap.get(brinst.getTarget())); + } else if (inst.isLocalVariableInstruction()) { + // LocalVariableInstruction lvinst = (LocalVariableInstruction) + // inst; + out.print(inst.toString(false).toUpperCase()); + int index = inst.getIndex(); + LocalVariableTag tag = getLocalVariableTag(h, index); + if (tag != null) { + out.print(" // "); + out.print(tag.getType()); + out.print(" "); + out.print(tag.getName()); + } + } else { + out.print(inst.toString(false).toUpperCase()); + } + } + + static final int BODY_INDENT = 4; + static final int CODE_INDENT = 16; + + void pad(int size) { + for (int i = 0; i < size; i++) { + out.print(" "); + } + } + } + + static LocalVariableTag getLocalVariableTag(InstructionHandle ih, int index) { + for (InstructionTargeter t : ih.getTargeters()) { + if (t instanceof LocalVariableTag) { + LocalVariableTag lvt = (LocalVariableTag) t; + if (lvt.getSlot() == index) { + return lvt; + } + } + } + return null; + } + + static int getLineNumber(InstructionHandle ih, int prevLine) { + for (InstructionTargeter t : ih.getTargeters()) { + if (t instanceof LineNumberTag) { + return ((LineNumberTag) t).getLineNumber(); + } + } + return prevLine; + } + + public boolean isStatic() { + return Modifier.isStatic(getAccessFlags()); + } + + public boolean isAbstract() { + return Modifier.isAbstract(getAccessFlags()); + } + + public boolean isBridgeMethod() { + return (getAccessFlags() & Constants.ACC_BRIDGE) != 0; + } + + public void addExceptionHandler(InstructionHandle start, InstructionHandle end, InstructionHandle handlerStart, + ObjectType catchType, boolean highPriority) { + + InstructionHandle start1 = Range.genStart(body, start); + InstructionHandle end1 = Range.genEnd(body, end); + + ExceptionRange er = new ExceptionRange(body, (catchType == null ? null : BcelWorld.fromBcel(catchType)), highPriority); + er.associateWithTargets(start1, end1, handlerStart); + } + + public int getAccessFlags() { + return modifiers; + } + + public int getAccessFlagsWithoutSynchronized() { + if (isSynchronized()) { + return modifiers - Modifier.SYNCHRONIZED; + } + return modifiers; + } + + public boolean isSynchronized() { + return (modifiers & Modifier.SYNCHRONIZED) != 0; + } + + public void setAccessFlags(int newFlags) { + this.modifiers = newFlags; + } + + public Type[] getArgumentTypes() { + initialize(); + return argumentTypes; + } + + public LazyClassGen getEnclosingClass() { + return enclosingClass; + } + + public int getMaxLocals() { + return maxLocals; + } + + public String getName() { + return name; + } + + public String getGenericReturnTypeSignature() { + if (memberView == null) { + return getReturnType().getSignature(); + } else { + return memberView.getGenericReturnType().getSignature(); + } + } + + public Type getReturnType() { + initialize(); + return returnType; + } + + public void setMaxLocals(int maxLocals) { + this.maxLocals = maxLocals; + } + + public InstructionList getBody() { + markAsChanged(); + return body; + } + + public InstructionList getBodyForPrint() { + return body; + } + + public boolean hasBody() { + if (savedMethod != null) { + return savedMethod.getCode() != null; + } + return body != null; + } + + public List getAttributes() { + return attributes; + } + + public String[] getDeclaredExceptions() { + return declaredExceptions; + } + + public String getClassName() { + return enclosingClass.getName(); + } + + // ---- packing! + + public MethodGen pack() { + forceSyntheticForAjcMagicMembers(); + + // killNops(); + int flags = getAccessFlags(); + if (enclosingClass.getWorld().isJoinpointSynchronizationEnabled() + && enclosingClass.getWorld().areSynchronizationPointcutsInUse()) { + flags = getAccessFlagsWithoutSynchronized(); + } + MethodGen gen = new MethodGen(flags, getReturnType(), getArgumentTypes(), null, // getArgumentNames(), + getName(), getEnclosingClass().getName(), new InstructionList(), getEnclosingClass().getConstantPool()); + for (int i = 0, len = declaredExceptions.length; i < len; i++) { + gen.addException(declaredExceptions[i]); + } + + for (Attribute attr : attributes) { + gen.addAttribute(attr); + } + + if (newAnnotations != null) { + for (AnnotationAJ element : newAnnotations) { + gen.addAnnotation(new AnnotationGen(((BcelAnnotation) element).getBcelAnnotation(), gen.getConstantPool(), true)); + } + } + + if (newParameterAnnotations != null) { + for (int i = 0; i < newParameterAnnotations.length; i++) { + AnnotationAJ[] annos = newParameterAnnotations[i]; + for (int j = 0; j < annos.length; j++) { + gen.addParameterAnnotation(i, + new AnnotationGen(((BcelAnnotation) annos[j]).getBcelAnnotation(), gen.getConstantPool(), true)); + } + } + } + + if (memberView != null && memberView.getAnnotations() != null && memberView.getAnnotations().length != 0) { + AnnotationAJ[] ans = memberView.getAnnotations(); + for (int i = 0, len = ans.length; i < len; i++) { + AnnotationGen a = ((BcelAnnotation) ans[i]).getBcelAnnotation(); + gen.addAnnotation(new AnnotationGen(a, gen.getConstantPool(), true)); + } + } + + if (isSynthetic) { + if (enclosingClass.getWorld().isInJava5Mode()) { + gen.setModifiers(gen.getModifiers() | Constants.ACC_SYNTHETIC); + } + if (!hasAttribute("Synthetic")) { + // belt and braces, do the attribute even on Java 5 in addition to the modifier flag + ConstantPool cpg = gen.getConstantPool(); + int index = cpg.addUtf8("Synthetic"); + gen.addAttribute(new Synthetic(index, 0, new byte[0], cpg)); + } + } + + if (hasBody()) { + if (this.enclosingClass.getWorld().shouldFastPackMethods()) { + if (isAdviceMethod() || getName().equals("")) { + packBody(gen); + } else { + optimizedPackBody(gen); + } + } else { + packBody(gen); + } + + gen.setMaxLocals(true); + gen.setMaxStack(); + } else { + gen.setInstructionList(null); + } + return gen; + } + + private boolean hasAttribute(String attributeName) { + for (Attribute attr: attributes) { + if (attr.getName().equals(attributeName)) { + return true; + } + } + return false; + } + + private void forceSyntheticForAjcMagicMembers() { + if (NameMangler.isSyntheticMethod(getName(), inAspect())) { + makeSynthetic(); + } + } + + private boolean inAspect() { + BcelObjectType objectType = enclosingClass.getBcelObjectType(); + return (objectType == null ? false : objectType.isAspect()); + } + + public void makeSynthetic() { + isSynthetic = true; + } + + private static class LVPosition { + InstructionHandle start = null; + InstructionHandle end = null; + } + + /** + * fill the newly created method gen with our body, inspired by InstructionList.copy() + */ + public void packBody(MethodGen gen) { + InstructionList fresh = gen.getInstructionList(); + Map map = copyAllInstructionsExceptRangeInstructionsInto(fresh); + + // at this point, no rangeHandles are in fresh. Let's use that... + + /* + * Update branch targets and insert various attributes. Insert our exceptionHandlers into a sorted list, so they can be + * added in order later. + */ + InstructionHandle oldInstructionHandle = getBody().getStart(); + InstructionHandle newInstructionHandle = fresh.getStart(); + LinkedList exceptionList = new LinkedList(); + + Map localVariables = new HashMap(); + + int currLine = -1; + int lineNumberOffset = (fromFilename == null) ? 0 : getEnclosingClass().getSourceDebugExtensionOffset(fromFilename); + + while (oldInstructionHandle != null) { + if (map.get(oldInstructionHandle) == null) { + // must be a range instruction since they're the only things we + // didn't copy across + handleRangeInstruction(oldInstructionHandle, exceptionList); + // just increment ih. + oldInstructionHandle = oldInstructionHandle.getNext(); + } else { + // assert map.get(ih) == jh + Instruction oldInstruction = oldInstructionHandle.getInstruction(); + Instruction newInstruction = newInstructionHandle.getInstruction(); + + if (oldInstruction instanceof InstructionBranch) { + handleBranchInstruction(map, oldInstruction, newInstruction); + } + + // now deal with line numbers + // and store up info for local variables + for (InstructionTargeter targeter : oldInstructionHandle.getTargeters()) { + if (targeter instanceof LineNumberTag) { + int line = ((LineNumberTag) targeter).getLineNumber(); + if (line != currLine) { + gen.addLineNumber(newInstructionHandle, line + lineNumberOffset); + currLine = line; + } + } else if (targeter instanceof LocalVariableTag) { + LocalVariableTag lvt = (LocalVariableTag) targeter; + LVPosition p = localVariables.get(lvt); + // If we don't know about it, create a new position and + // store + // If we do know about it - update its end position + if (p == null) { + LVPosition newp = new LVPosition(); + newp.start = newp.end = newInstructionHandle; + localVariables.put(lvt, newp); + } else { + p.end = newInstructionHandle; + } + } + } + + // now continue + oldInstructionHandle = oldInstructionHandle.getNext(); + newInstructionHandle = newInstructionHandle.getNext(); + } + } + + addExceptionHandlers(gen, map, exceptionList); + if (originalMethodHasLocalVariableTable || enclosingClass + .getBcelObjectType() + .getResolvedTypeX() + .getWorld().generateNewLvts) { + if (localVariables.size() == 0) { + // Might be a case of 173978 where around advice on an execution join point + // has caused everything to be extracted from the method and thus we + // are left with no local variables, not even the ones for 'this' and + // parameters passed to the method + createNewLocalVariables(gen); + } else { + addLocalVariables(gen, localVariables); + } + } + + // JAVAC adds line number tables (with just one entry) to generated + // accessor methods - this + // keeps some tools that rely on finding at least some form of + // linenumbertable happy. + // Let's check if we have one - if we don't then let's add one. + // TODO Could be made conditional on whether line debug info is being + // produced + if (gen.getLineNumbers().length == 0) { + gen.addLineNumber(gen.getInstructionList().getStart(), 1); + } + } + + private void createNewLocalVariables(MethodGen gen) { + gen.removeLocalVariables(); + // ignore or for now + if (!getName().startsWith("<")) { + int slot = 0; + InstructionHandle start = gen.getInstructionList().getStart(); + InstructionHandle end = gen.getInstructionList().getEnd(); + // Add a 'this' if non-static + if (!isStatic()) { + String cname = this.enclosingClass.getClassName(); + if (cname == null) { + return; // give up for now + } + Type enclosingType = BcelWorld.makeBcelType(UnresolvedType.forName(cname)); + gen.addLocalVariable("this", enclosingType, slot++, start, end); + } + // Add entries for the method arguments + String[] paramNames = (memberView == null ? null : memberView.getParameterNames()); + if (paramNames != null) { + for (int i = 0; i < argumentTypes.length; i++) { + String pname = paramNames[i]; + if (pname == null) { + pname = "arg" + i; + } + gen.addLocalVariable(pname, argumentTypes[i], slot, start, end); + slot += argumentTypes[i].getSize(); + } + } + } + } + + private World getWorld() { + return enclosingClass.getBcelObjectType().getResolvedTypeX().getWorld(); + } + /* + * Optimized packing that does a 'local packing' of the code rather than building a brand new method and packing into it. Only + * usable when the packing is going to be done just once. + */ + public void optimizedPackBody(MethodGen gen) { + InstructionList theBody = getBody(); + InstructionHandle iHandle = theBody.getStart(); + + int currLine = -1; + int lineNumberOffset = (fromFilename == null) ? 0 : getEnclosingClass().getSourceDebugExtensionOffset(fromFilename); + Map localVariables = new HashMap(); + LinkedList exceptionList = new LinkedList(); + Set forDeletion = new HashSet(); + Set branchInstructions = new HashSet(); + // OPTIMIZE sort out in here: getRange()/insertHandler() and type of + // exceptionList + while (iHandle != null) { + Instruction inst = iHandle.getInstruction(); + // InstructionHandle nextInst = iHandle.getNext(); + // OPTIMIZE remove this instructionhandle as it now points to + // nowhere? + if (inst == Range.RANGEINSTRUCTION) { + Range r = Range.getRange(iHandle); + if (r instanceof ExceptionRange) { + ExceptionRange er = (ExceptionRange) r; + if (er.getStart() == iHandle) { + if (!er.isEmpty()) { + // order is important, insert handlers in order of start + insertHandler(er, exceptionList); + } + } + } + forDeletion.add(iHandle); + } else { + if (inst instanceof InstructionBranch) { + branchInstructions.add((BranchHandle) iHandle); + } + + for (InstructionTargeter targeter : iHandle.getTargetersCopy()) { + if (targeter instanceof LineNumberTag) { + int line = ((LineNumberTag) targeter).getLineNumber(); + if (line != currLine) { + gen.addLineNumber(iHandle, line + lineNumberOffset); + currLine = line; + } + } else if (targeter instanceof LocalVariableTag) { + LocalVariableTag lvt = (LocalVariableTag) targeter; + LVPosition p = localVariables.get(lvt); + // If we don't know about it, create a new position + // and store + // If we do know about it - update its end position + if (p == null) { + LVPosition newp = new LVPosition(); + newp.start = newp.end = iHandle; + localVariables.put(lvt, newp); + } else { + p.end = iHandle; + } + } + } + } + iHandle = iHandle.getNext(); + } + for (BranchHandle branchHandle : branchInstructions) { + handleBranchInstruction(branchHandle, forDeletion); + } + // now add exception handlers + for (ExceptionRange r : exceptionList) { + if (r.isEmpty()) { + continue; + } + gen.addExceptionHandler(jumpForward(r.getRealStart(), forDeletion), jumpForward(r.getRealEnd(), forDeletion), + jumpForward(r.getHandler(), forDeletion), + (r.getCatchType() == null) ? null : (ObjectType) BcelWorld.makeBcelType(r.getCatchType())); + } + + for (InstructionHandle handle : forDeletion) { + try { + theBody.delete(handle); + } catch (TargetLostException e) { + e.printStackTrace(); + } + } + gen.setInstructionList(theBody); + if (originalMethodHasLocalVariableTable || getWorld().generateNewLvts) { + if (localVariables.size() == 0) { + // Might be a case of 173978 where around advice on an execution join point + // has caused everything to be extracted from the method and thus we + // are left with no local variables, not even the ones for 'this' and + // parameters passed to the method + createNewLocalVariables(gen); + } else { + addLocalVariables(gen, localVariables); + } + } + // JAVAC adds line number tables (with just one entry) to generated + // accessor methods - this + // keeps some tools that rely on finding at least some form of + // linenumbertable happy. + // Let's check if we have one - if we don't then let's add one. + // TODO Could be made conditional on whether line debug info is being + // produced + if (gen.getLineNumbers().length == 0) { + gen.addLineNumber(gen.getInstructionList().getStart(), 1); + } + wasPackedOptimally = true; + } + + private void addLocalVariables(MethodGen gen, Map localVariables) { + // now add local variables + gen.removeLocalVariables(); + + // this next iteration _might_ be overkill, but we had problems with + // bcel before with duplicate local variables. Now that we're patching + // bcel we should be able to do without it if we're paranoid enough + // through the rest of the compiler. + InstructionHandle methodStart = gen.getInstructionList().getStart(); + InstructionHandle methodEnd = gen.getInstructionList().getEnd(); + + // Determine how many 'slots' are used by parameters to the method. + // Then below we can determine if a local variable is a parameter variable, if it is + // we force its range to from the method start (as it may have been shuffled down + // due to insertion of advice like cflow entry) + int paramSlots = gen.isStatic() ? 0 : 1; + Type[] argTypes = gen.getArgumentTypes(); + if (argTypes != null) { + for (int i = 0; i < argTypes.length; i++) { + if (argTypes[i].getSize() == 2) { + paramSlots += 2; + } else { + paramSlots += 1; + } + } + } + if (!this.enclosingClass.getWorld().generateNewLvts) { + // Here the generateNewLvts option is used to control "Do not damage unusually positioned local + // variables that represent method parameters". Strictly speaking local variables that represent + // method parameters effectively have a bytecode range from 0..end_of_method - however some + // tools generate bytecode that specifies a compressed range. The code below would normally + // extend the parameter local variables to cover the full method but by setting paramSlots to -1 + // here we cause the code below to avoid modifying any local vars that represent method + // parameters. + paramSlots = -1; + } + + Map> duplicatedLocalMap = new HashMap>(); + for (LocalVariableTag tag : localVariables.keySet()) { + // have we already added one with the same slot number and start + // location? + // if so, just continue. + LVPosition lvpos = localVariables.get(tag); + InstructionHandle start = (tag.getSlot() < paramSlots ? methodStart : lvpos.start); + InstructionHandle end = (tag.getSlot() < paramSlots ? methodEnd : lvpos.end); + Set slots = duplicatedLocalMap.get(start); + if (slots == null) { + slots = new HashSet(); + duplicatedLocalMap.put(start, slots); + } else if (slots.contains(new Integer(tag.getSlot()))) { + // we already have a var starting at this tag with this slot + continue; + } + slots.add(Integer.valueOf(tag.getSlot())); + Type t = tag.getRealType(); + if (t == null) { + t = BcelWorld.makeBcelType(UnresolvedType.forSignature(tag.getType())); + } + gen.addLocalVariable(tag.getName(), t, tag.getSlot(), start, end); + } + } + + private void addExceptionHandlers(MethodGen gen, Map map, + LinkedList exnList) { + // now add exception handlers + for (ExceptionRange r : exnList) { + if (r.isEmpty()) { + continue; + } + InstructionHandle rMappedStart = remap(r.getRealStart(), map); + InstructionHandle rMappedEnd = remap(r.getRealEnd(), map); + InstructionHandle rMappedHandler = remap(r.getHandler(), map); + gen.addExceptionHandler(rMappedStart, rMappedEnd, rMappedHandler, (r.getCatchType() == null) ? null + : (ObjectType) BcelWorld.makeBcelType(r.getCatchType())); + } + } + + private void handleBranchInstruction(Map map, Instruction oldInstruction, + Instruction newInstruction) { + InstructionBranch oldBranchInstruction = (InstructionBranch) oldInstruction; + InstructionBranch newBranchInstruction = (InstructionBranch) newInstruction; + InstructionHandle oldTarget = oldBranchInstruction.getTarget(); // old + // target + + // New target is in hash map + newBranchInstruction.setTarget(remap(oldTarget, map)); + + if (oldBranchInstruction instanceof InstructionSelect) { + // Either LOOKUPSWITCH or TABLESWITCH + InstructionHandle[] oldTargets = ((InstructionSelect) oldBranchInstruction).getTargets(); + InstructionHandle[] newTargets = ((InstructionSelect) newBranchInstruction).getTargets(); + + for (int k = oldTargets.length - 1; k >= 0; k--) { + // Update all targets + newTargets[k] = remap(oldTargets[k], map); + newTargets[k].addTargeter(newBranchInstruction); + } + } + } + + private InstructionHandle jumpForward(InstructionHandle t, Set handlesForDeletion) { + InstructionHandle target = t; + if (handlesForDeletion.contains(target)) { + do { + target = target.getNext(); + } while (handlesForDeletion.contains(target)); + } + return target; + } + + /** + * Process a branch instruction with respect to instructions that are about to be deleted. If the target for the branch is a + * candidate for deletion, move it to the next valid instruction after the deleted target. + */ + private void handleBranchInstruction(BranchHandle branchHandle, Set handlesForDeletion) { + InstructionBranch branchInstruction = (InstructionBranch) branchHandle.getInstruction(); + InstructionHandle target = branchInstruction.getTarget(); // old target + + if (handlesForDeletion.contains(target)) { + do { + target = target.getNext(); + } while (handlesForDeletion.contains(target)); + branchInstruction.setTarget(target); + } + + if (branchInstruction instanceof InstructionSelect) { + // Either LOOKUPSWITCH or TABLESWITCH + InstructionSelect iSelect = (InstructionSelect) branchInstruction; + InstructionHandle[] targets = iSelect.getTargets(); + for (int k = targets.length - 1; k >= 0; k--) { + InstructionHandle oneTarget = targets[k]; + if (handlesForDeletion.contains(oneTarget)) { + do { + oneTarget = oneTarget.getNext(); + } while (handlesForDeletion.contains(oneTarget)); + iSelect.setTarget(k, oneTarget); + oneTarget.addTargeter(branchInstruction); + } + } + } + } + + private void handleRangeInstruction(InstructionHandle ih, LinkedList exnList) { + // we're a range instruction + Range r = Range.getRange(ih); + if (r instanceof ExceptionRange) { + ExceptionRange er = (ExceptionRange) r; + if (er.getStart() == ih) { + // System.err.println("er " + er); + if (!er.isEmpty()) { + // order is important, insert handlers in order of start + insertHandler(er, exnList); + } + } + } else { + // we must be a shadow range or something equally useless, + // so forget about doing anything + } + } + + /* + * Make copies of all instructions, append them to the new list and associate old instruction references with the new ones, + * i.e., a 1:1 mapping. + */ + private Map copyAllInstructionsExceptRangeInstructionsInto(InstructionList intoList) { + Map map = new HashMap(); + for (InstructionHandle ih = getBody().getStart(); ih != null; ih = ih.getNext()) { + if (Range.isRangeHandle(ih)) { + continue; + } + Instruction inst = ih.getInstruction(); + Instruction copy = Utility.copyInstruction(inst); + + if (copy instanceof InstructionBranch) { + map.put(ih, intoList.append((InstructionBranch) copy)); + } else { + map.put(ih, intoList.append(copy)); + } + } + return map; + } + + /** + * This procedure should not currently be used. + */ + // public void killNops() { + // InstructionHandle curr = body.getStart(); + // while (true) { + // if (curr == null) break; + // InstructionHandle next = curr.getNext(); + // if (curr.getInstruction() instanceof NOP) { + // InstructionTargeter[] targeters = curr.getTargeters(); + // if (targeters != null) { + // for (int i = 0, len = targeters.length; i < len; i++) { + // InstructionTargeter targeter = targeters[i]; + // targeter.updateTarget(curr, next); + // } + // } + // try { + // body.delete(curr); + // } catch (TargetLostException e) { + // } + // } + // curr = next; + // } + // } + // private static InstructionHandle fNext(InstructionHandle ih) { + // while (true) { + // if (ih.getInstruction()==Range.RANGEINSTRUCTION) ih = ih.getNext(); + // else return ih; + // } + // } + private static InstructionHandle remap(InstructionHandle handle, Map map) { + while (true) { + InstructionHandle ret = map.get(handle); + if (ret == null) { + handle = handle.getNext(); + } else { + return ret; + } + } + } + + // Update to all these comments, ASC 11-01-2005 + // The right thing to do may be to do more with priorities as + // we create new exception handlers, but that is a relatively + // complex task. In the meantime, just taking account of the + // priority here enables a couple of bugs to be fixed to do + // with using return or break in code that contains a finally + // block (pr78021,pr79554). + + // exception ordering. + // What we should be doing is dealing with priority inversions way earlier + // than we are + // and counting on the tree structure. In which case, the below code is in + // fact right. + + // XXX THIS COMMENT BELOW IS CURRENTLY WRONG. + // An exception A preceeds an exception B in the exception table iff: + + // * A and B were in the original method, and A preceeded B in the original + // exception table + // * If A has a higher priority than B, than it preceeds B. + // * If A and B have the same priority, then the one whose START happens + // EARLIEST has LEAST priority. + // in short, the outermost exception has least priority. + // we implement this with a LinkedList. We could possibly implement this + // with a java.util.SortedSet, + // but I don't trust the only implementation, TreeSet, to do the right + // thing. + + /* private */static void insertHandler(ExceptionRange fresh, LinkedList l) { + // Old implementation, simply: l.add(0,fresh); + for (ListIterator iter = l.listIterator(); iter.hasNext();) { + ExceptionRange r = iter.next(); + // int freal = fresh.getRealStart().getPosition(); + // int rreal = r.getRealStart().getPosition(); + if (fresh.getPriority() >= r.getPriority()) { + iter.previous(); + iter.add(fresh); + return; + } + } + + // we have reached the end + l.add(fresh); + } + + public boolean isPrivate() { + return Modifier.isPrivate(getAccessFlags()); + } + + public boolean isProtected() { + return Modifier.isProtected(getAccessFlags()); + } + + public boolean isDefault() { + return !(isProtected() || isPrivate() || isPublic()); + } + + public boolean isPublic() { + return Modifier.isPublic(getAccessFlags()); + } + + // ---- + + /** + * A good body is a body with the following properties: + * + *

    + *
  • For each branch instruction S in body, target T of S is in body. + *
  • For each branch instruction S in body, target T of S has S as a targeter. + *
  • For each instruction T in body, for each branch instruction S that is a targeter of T, S is in body. + *
  • For each non-range-handle instruction T in body, for each instruction S that is a targeter of T, S is either a branch + * instruction, an exception range or a tag + *
  • For each range-handle instruction T in body, there is exactly one targeter S that is a range. + *
  • For each range-handle instruction T in body, the range R targeting T is in body. + *
  • For each instruction T in body, for each exception range R targeting T, R is in body. + *
  • For each exception range R in body, let T := R.handler. T is in body, and R is one of T's targeters + *
  • All ranges are properly nested: For all ranges Q and R, if Q.start preceeds R.start, then R.end preceeds Q.end. + *
+ * + * Where the shorthand "R is in body" means "R.start is in body, R.end is in body, and any InstructionHandle stored in a field + * of R (such as an exception handle) is in body". + */ + + public void assertGoodBody() { + if (true) { + return; // only enable for debugging + } + assertGoodBody(getBody(), toString()); + } + + public static void assertGoodBody(InstructionList il, String from) { + if (true) { + return; // only to be enabled for debugging + } +// if (il == null) { +// return; +// } +// Set body = new HashSet(); +// Stack ranges = new Stack(); +// for (InstructionHandle ih = il.getStart(); ih != null; ih = ih.getNext()) { +// body.add(ih); +// if (ih.getInstruction() instanceof InstructionBranch) { +// body.add(ih.getInstruction()); +// } +// } +// +// for (InstructionHandle ih = il.getStart(); ih != null; ih = ih.getNext()) { +// assertGoodHandle(ih, body, ranges, from); +// Iterator tIter = ih.getTargeters().iterator(); +// while (tIter.hasNext()) { +// assertGoodTargeter(tIter.next(), ih, body, from); +// } +// } + } + +// private static void assertGoodHandle(InstructionHandle ih, Set body, Stack ranges, String from) { +// Instruction inst = ih.getInstruction(); +// if ((inst instanceof InstructionBranch) ^ (ih instanceof BranchHandle)) { +// throw new BCException("bad instruction/handle pair in " + from); +// } +// if (Range.isRangeHandle(ih)) { +// assertGoodRangeHandle(ih, body, ranges, from); +// } else if (inst instanceof InstructionBranch) { +// assertGoodBranchInstruction((BranchHandle) ih, (InstructionBranch) inst, body, ranges, from); +// } +// } + +// private static void assertGoodBranchInstruction(BranchHandle ih, InstructionBranch inst, Set body, Stack ranges, +// String from) { +// if (ih.getTarget() != inst.getTarget()) { +// throw new BCException("bad branch instruction/handle pair in " + from); +// } +// InstructionHandle target = ih.getTarget(); +// assertInBody(target, body, from); +// assertTargetedBy(target, inst, from); +// if (inst instanceof InstructionSelect) { +// InstructionSelect sel = (InstructionSelect) inst; +// InstructionHandle[] itargets = sel.getTargets(); +// for (int k = itargets.length - 1; k >= 0; k--) { +// assertInBody(itargets[k], body, from); +// assertTargetedBy(itargets[k], inst, from); +// } +// } +// } + + /** ih is an InstructionHandle or a BranchInstruction */ +// private static void assertInBody(Object ih, Set body, String from) { +// if (!body.contains(ih)) { +// throw new BCException("thing not in body in " + from); +// } +// } + +// private static void assertGoodRangeHandle(InstructionHandle ih, Set body, Stack ranges, String from) { +// Range r = getRangeAndAssertExactlyOne(ih, from); +// assertGoodRange(r, body, from); +// if (r.getStart() == ih) { +// ranges.push(r); +// } else if (r.getEnd() == ih) { +// if (ranges.peek() != r) { +// throw new BCException("bad range inclusion in " + from); +// } +// ranges.pop(); +// } +// } + +// private static void assertGoodRange(Range r, Set body, String from) { +// assertInBody(r.getStart(), body, from); +// assertRangeHandle(r.getStart(), from); +// assertTargetedBy(r.getStart(), r, from); +// +// assertInBody(r.getEnd(), body, from); +// assertRangeHandle(r.getEnd(), from); +// assertTargetedBy(r.getEnd(), r, from); +// +// if (r instanceof ExceptionRange) { +// ExceptionRange er = (ExceptionRange) r; +// assertInBody(er.getHandler(), body, from); +// assertTargetedBy(er.getHandler(), r, from); +// } +// } + +// private static void assertRangeHandle(InstructionHandle ih, String from) { +// if (!Range.isRangeHandle(ih)) { +// throw new BCException("bad range handle " + ih + " in " + from); +// } +// } + + private static void assertTargetedBy(InstructionHandle target, InstructionTargeter targeter, String from) { + Iterator tIter = target.getTargeters().iterator(); + while (tIter.hasNext()) { + if (((InstructionTargeter) tIter.next()) == targeter) { + return; + } + } + throw new RuntimeException("bad targeting relationship in " + from); + } + + private static void assertTargets(InstructionTargeter targeter, InstructionHandle target, String from) { + if (targeter instanceof Range) { + Range r = (Range) targeter; + if (r.getStart() == target || r.getEnd() == target) { + return; + } + if (r instanceof ExceptionRange) { + if (((ExceptionRange) r).getHandler() == target) { + return; + } + } + } else if (targeter instanceof InstructionBranch) { + InstructionBranch bi = (InstructionBranch) targeter; + if (bi.getTarget() == target) { + return; + } + if (targeter instanceof InstructionSelect) { + InstructionSelect sel = (InstructionSelect) targeter; + InstructionHandle[] itargets = sel.getTargets(); + for (int k = itargets.length - 1; k >= 0; k--) { + if (itargets[k] == target) { + return; + } + } + } + } else if (targeter instanceof Tag) { + return; + } + throw new BCException(targeter + " doesn't target " + target + " in " + from); + } + + private static Range getRangeAndAssertExactlyOne(InstructionHandle ih, String from) { + Range ret = null; + Iterator tIter = ih.getTargeters().iterator(); + if (!tIter.hasNext()) { + throw new BCException("range handle with no range in " + from); + } + while (tIter.hasNext()) { + InstructionTargeter ts = tIter.next(); + if (ts instanceof Range) { + if (ret != null) { + throw new BCException("range handle with multiple ranges in " + from); + } + ret = (Range) ts; + } + } + if (ret == null) { + throw new BCException("range handle with no range in " + from); + } + return ret; + } + +// private static void assertGoodTargeter(InstructionTargeter t, InstructionHandle ih, Set body, String from) { +// assertTargets(t, ih, from); +// if (t instanceof Range) { +// assertGoodRange((Range) t, body, from); +// } else if (t instanceof InstructionBranch) { +// assertInBody(t, body, from); +// } +// } + + // ---- + + boolean isAdviceMethod() { + if (memberView == null) { + return false; + } + return memberView.getAssociatedShadowMunger() != null; + } + + boolean isAjSynthetic() { + if (memberView == null) { + return true; + } + return memberView.isAjSynthetic(); + } + + boolean isSynthetic() { + if (memberView == null) { + return false; + } + return memberView.isSynthetic(); + } + + public ISourceLocation getSourceLocation() { + if (memberView != null) { + return memberView.getSourceLocation(); + } + return null; + } + + public AjAttribute.EffectiveSignatureAttribute getEffectiveSignature() { + // if (memberView == null) return null; + if (effectiveSignature != null) { + return effectiveSignature; + } + return memberView.getEffectiveSignature(); + } + + public void setEffectiveSignature(ResolvedMember member, Shadow.Kind kind, boolean shouldWeave) { + this.effectiveSignature = new AjAttribute.EffectiveSignatureAttribute(member, kind, shouldWeave); + } + + public String getSignature() { + if (memberView != null) { + return memberView.getSignature(); + } + return MemberImpl.typesToSignature(BcelWorld.fromBcel(getReturnType()), BcelWorld.fromBcel(getArgumentTypes()), false); + } + + public String getParameterSignature() { + if (memberView != null) { + return memberView.getParameterSignature(); + } + return MemberImpl.typesToSignature(BcelWorld.fromBcel(getArgumentTypes())); + } + + public BcelMethod getMemberView() { + return memberView; + } + + public void forcePublic() { + markAsChanged(); + modifiers = Utility.makePublic(modifiers); + } + + public boolean getCanInline() { + return canInline; + } + + public void setCanInline(boolean canInline) { + this.canInline = canInline; + } + + public void addAttribute(Attribute attribute) { + attributes.add(attribute); + } + + public String toTraceString() { + return toShortString(); + } + + public ConstantPool getConstantPool() { + return enclosingClass.getConstantPool(); + } + + public static boolean isConstructor(LazyMethodGen aMethod) { + return aMethod.getName().equals(""); + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/Range.java b/weaver/src/main/java/org/aspectj/weaver/bcel/Range.java new file mode 100644 index 000000000..fa162c526 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/Range.java @@ -0,0 +1,243 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.bcel; + +import java.util.Iterator; + +import org.aspectj.apache.bcel.generic.Instruction; +import org.aspectj.apache.bcel.generic.InstructionConstants; +import org.aspectj.apache.bcel.generic.InstructionHandle; +import org.aspectj.apache.bcel.generic.InstructionList; +import org.aspectj.apache.bcel.generic.InstructionTargeter; +import org.aspectj.weaver.BCException; + +abstract class Range implements InstructionTargeter { + + protected InstructionList body; + protected InstructionHandle start; + protected InstructionHandle end; + + // ---- initialization + + protected Range(InstructionList il) { + this.body = il; + } + + // ---- + + final InstructionList getBody() { + return body; + } + + final InstructionHandle getStart() { + return start; + } + + final InstructionHandle getEnd() { + return end; + } + + // ---- + + boolean isEmpty() { + InstructionHandle ih = start; + // System.err.println(" looking for " + end); + while (ih != end) { + // System.err.println(" ih " + ih); + if (!Range.isRangeHandle(ih)) { + return false; + } + ih = ih.getNext(); + } + return true; + } + + static InstructionHandle getRealStart(InstructionHandle ih) { + while (Range.isRangeHandle(ih)) { + ih = ih.getNext(); + } + return ih; + } + + InstructionHandle getRealStart() { + return getRealStart(start); + } + + static InstructionHandle getRealEnd(InstructionHandle ih) { + while (Range.isRangeHandle(ih)) { + ih = ih.getPrev(); + } + return ih; + } + + InstructionHandle getRealEnd() { + return getRealEnd(end); + } + + InstructionHandle getRealNext() { + return getRealStart(end); + } + + // ---- + + InstructionHandle insert(Instruction i, Where where) { + InstructionList il = new InstructionList(); + InstructionHandle ret = il.insert(i); + insert(il, where); + return ret; + } + + void insert(InstructionList freshIl, Where where) { + InstructionHandle h; + if (where == InsideBefore || where == OutsideBefore) { + h = getStart(); + } else { + h = getEnd(); + } + if (where == InsideBefore || where == OutsideAfter) { + body.append(h, freshIl); + } else { + InstructionHandle newStart = body.insert(h, freshIl); + if (where == OutsideBefore) { + // XXX this is slow. There's a better design than this. We should + // never have to retarget branches apart from the creation of ranges. + // basically, we should never weave OutsideBefore. + BcelShadow.retargetAllBranches(h, newStart); + } + } + + } + + InstructionHandle append(Instruction i) { + return insert(i, InsideAfter); + } + + void append(InstructionList i) { + insert(i, InsideAfter); + } + + private static void setLineNumberFromNext(InstructionHandle ih) { + int lineNumber = Utility.getSourceLine(ih.getNext()); + if (lineNumber != -1) { + Utility.setSourceLine(ih, lineNumber); + } + } + + static InstructionHandle genStart(InstructionList body) { + InstructionHandle ih = body.insert(Range.RANGEINSTRUCTION); + setLineNumberFromNext(ih); + return ih; + } + + static InstructionHandle genEnd(InstructionList body) { + return body.append(Range.RANGEINSTRUCTION); + } + + static InstructionHandle genStart(InstructionList body, InstructionHandle ih) { + if (ih == null) { + return genStart(body); + } + InstructionHandle freshIh = body.insert(ih, Range.RANGEINSTRUCTION); + setLineNumberFromNext(freshIh); + return freshIh; + } + + static InstructionHandle genEnd(InstructionList body, InstructionHandle ih) { + if (ih == null) { + return genEnd(body); + } + return body.append(ih, Range.RANGEINSTRUCTION); + } + + // ----- + + public boolean containsTarget(InstructionHandle ih) { + return false; + } + + public final void updateTarget(InstructionHandle old_ih, InstructionHandle new_ih) { + throw new RuntimeException("Ranges must be updated with an enclosing instructionList"); + } + + protected void updateTarget(InstructionHandle old_ih, InstructionHandle new_ih, InstructionList new_il) { + old_ih.removeTargeter(this); + if (new_ih != null) { + new_ih.addTargeter(this); + } + body = new_il; + + if (old_ih == start) { + start = new_ih; + } + if (old_ih == end) { + end = new_ih; + } + } + + public static final boolean isRangeHandle(InstructionHandle ih) { + if (ih == null) { + return false; + } + return ih.getInstruction() == Range.RANGEINSTRUCTION; + } + + protected static final Range getRange(InstructionHandle ih) { + // assert isRangeHandle(ih) + Range ret = null; + Iterator tIter = ih.getTargeters().iterator(); + while (tIter.hasNext()) { + InstructionTargeter targeter = tIter.next(); + if (targeter instanceof Range) { + Range r = (Range) targeter; + if (r.getStart() != ih && r.getEnd() != ih) { + continue; + } + if (ret != null) { + throw new BCException("multiple ranges on same range handle: " + ret + ", " + targeter); + } + ret = r; + } + } + if (ret == null) { + throw new BCException("shouldn't happen"); + } + return ret; + } + + // ---- + + static final Where InsideBefore = new Where("insideBefore"); + static final Where InsideAfter = new Where("insideAfter"); + static final Where OutsideBefore = new Where("outsideBefore"); + static final Where OutsideAfter = new Where("outsideAfter"); + + // ---- constants + + // note that this is STUPIDLY copied by Instruction.copy(), so don't do that. + + public static final Instruction RANGEINSTRUCTION = InstructionConstants.IMPDEP1; + + // ---- + + static class Where { + private String name; + + public Where(String name) { + this.name = name; + } + + public String toString() { + return name; + } + } +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/ShadowRange.java b/weaver/src/main/java/org/aspectj/weaver/bcel/ShadowRange.java new file mode 100644 index 000000000..47c8400c5 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/ShadowRange.java @@ -0,0 +1,244 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.bcel; + +import java.util.Iterator; + +import org.aspectj.apache.bcel.generic.Instruction; +import org.aspectj.apache.bcel.generic.InstructionBranch; +import org.aspectj.apache.bcel.generic.InstructionFactory; +import org.aspectj.apache.bcel.generic.InstructionHandle; +import org.aspectj.apache.bcel.generic.InstructionLV; +import org.aspectj.apache.bcel.generic.InstructionList; +import org.aspectj.apache.bcel.generic.InstructionSelect; +import org.aspectj.apache.bcel.generic.InstructionTargeter; +import org.aspectj.apache.bcel.generic.LocalVariableTag; +import org.aspectj.apache.bcel.generic.RET; +import org.aspectj.apache.bcel.generic.TargetLostException; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.Shadow; + +final class ShadowRange extends Range { + + private BcelShadow shadow; + + // ---- initialization + + /** + * After this constructor is called, this range is not well situated unless both {@link #associateWithTargets} and + * {@link #associateWithShadow} are called. + */ + public ShadowRange(InstructionList body) { + super(body); + } + + protected void associateWithTargets(InstructionHandle start, InstructionHandle end) { + // assert body.contains(start) && body.contains(end); + this.start = start; + this.end = end; + start.addTargeter(this); + end.addTargeter(this); + } + + public void associateWithShadow(BcelShadow shadow) { + this.shadow = shadow; + shadow.setRange(this); + } + + // ---- + + public Shadow.Kind getKind() { + return shadow.getKind(); + } + + @Override + public String toString() { + return shadow.toString(); + } + + void extractInstructionsInto(LazyMethodGen freshMethod, IntMap remap, boolean addReturn) { + LazyMethodGen.assertGoodBody(getBody(), toString()); + freshMethod.assertGoodBody(); + InstructionList freshBody = freshMethod.getBody(); + + for (InstructionHandle oldIh = start.getNext(); oldIh != end; oldIh = oldIh.getNext()) { + // first we copy the instruction itself. + Instruction oldI = oldIh.getInstruction(); + Instruction freshI = (oldI == RANGEINSTRUCTION) ? oldI : Utility.copyInstruction(oldI); + + // Now we add it to the new instruction list. + InstructionHandle freshIh; + if (freshI instanceof InstructionBranch) { + // If it's a targeting instruction, + // update the target(s) to point to the new copy instead of the old copy. + InstructionBranch oldBranch = (InstructionBranch) oldI; + InstructionBranch freshBranch = (InstructionBranch) freshI; + InstructionHandle oldTarget = oldBranch.getTarget(); + oldTarget.removeTargeter(oldBranch); + oldTarget.addTargeter(freshBranch); + if (freshBranch instanceof InstructionSelect) { + InstructionSelect oldSelect = (InstructionSelect) oldI; + InstructionSelect freshSelect = (InstructionSelect) freshI; + InstructionHandle[] oldTargets = freshSelect.getTargets(); + for (int k = oldTargets.length - 1; k >= 0; k--) { + oldTargets[k].removeTargeter(oldSelect); + oldTargets[k].addTargeter(freshSelect); + } + } + freshIh = freshBody.append(freshBranch); + } else { + freshIh = freshBody.append(freshI); + } + + // if source comes before target: + // source <--> target + // --> [process: target.removeTargeter(source); target.addTargeter(sourcecopy)] + // source ---------\ + // v + // sourcecopy <--> target + // --> [ process: sourcecopy.updateTarget(target, targetcopy) ] + // source ----> target + // sourcecopy <--> targetcopy + + // if target comes before source + + // target <--> source + // --> [process: source.updateTarget(target, targetcopy) ] + // target + // targetcopy <--> source + // --> [process: targetcopy.removeTargeter(source); targetcopy.addTargeter(sourcecopy)] + // target source + // v + // targetcopy <--> sourcecopy + + // now deal with the old instruction's targeters. Update them all to point to us + // instead of the old instruction. We use updateTarget to do this. One goal is + // to make sure we remove all targeters from the old guy, so we can successfully + // delete it. + for (InstructionTargeter source : oldIh.getTargetersCopy()) { + if (source instanceof LocalVariableTag) { + Shadow.Kind kind = getKind(); + if (kind == Shadow.AdviceExecution || kind == Shadow.ConstructorExecution || kind == Shadow.MethodExecution + || kind == Shadow.PreInitialization || kind == Shadow.Initialization + || kind == Shadow.StaticInitialization) { + LocalVariableTag sourceLocalVariableTag = (LocalVariableTag) source; + if (sourceLocalVariableTag.getSlot() == 0) { + // might be 'this' so should be renamed if being dumped in a static method 277616 + if (sourceLocalVariableTag.getName().equals("this")) { + sourceLocalVariableTag.setName("ajc$this"); + } + } + // if we're extracting a whole block we can do this... + source.updateTarget(oldIh, freshIh); + } else { + // XXX destroying local variable info + // but only for a call or get join point, so no big deal + source.updateTarget(oldIh, null); + } + } else if (source instanceof Range) { + // exceptions and shadows are just moved + ((Range) source).updateTarget(oldIh, freshIh, freshBody); + } else { + // line numbers can be shared, + // branches will be copied along with us. + source.updateTarget(oldIh, freshIh); + } + } + // we're now done with the old instruction entirely, and will ignore them through + // the rest of this loop. The only time we'll see them again is a second pass to + // delete them. + + // now deal with local variable instructions. If this points to a remapped + // frame location, update the instruction's index. If this doesn't, + // do compaction/expansion: allocate a new local variable, and modify the remap + // to handle it. XXX We're doing the safe thing and allocating ALL these local variables + // as double-wides, in case the location is found to hold a double-wide later. + if (freshI.isLocalVariableInstruction() || freshI instanceof RET) { + // IndexedInstruction indexedI = (IndexedInstruction) freshI; + int oldIndex = freshI.getIndex(); + int freshIndex; + if (!remap.hasKey(oldIndex)) { + freshIndex = freshMethod.allocateLocal(2); + remap.put(oldIndex, freshIndex); + } else { + freshIndex = remap.get(oldIndex); + } + if (freshI instanceof RET) { + freshI.setIndex(freshIndex); + } else { + freshI = ((InstructionLV) freshI).setIndexAndCopyIfNecessary(freshIndex); + freshIh.setInstruction(freshI); + } + } + // System.err.println("JUST COPIED: " + + // oldIh.getInstruction().toString(freshMethod.getEnclosingClass().getConstantPoolGen().getConstantPool()) + // + " INTO " + + // freshIh.getInstruction().toString(freshMethod.getEnclosingClass().getConstantPoolGen().getConstantPool())); + } + + // now go through again and update variable slots that have been altered as a result + // of remapping... + for (InstructionHandle newIh = freshBody.getStart(); newIh != freshBody.getEnd(); newIh = newIh.getNext()) { + Iterator tIter = newIh.getTargeters().iterator(); + while (tIter.hasNext()) { + InstructionTargeter source = tIter.next(); + if (source instanceof LocalVariableTag) { + LocalVariableTag lvt = (LocalVariableTag) source; + if (!lvt.isRemapped() && remap.hasKey(lvt.getSlot())) { + lvt.updateSlot(remap.get(lvt.getSlot())); + } + } + } + } + + // we've now copied out all the instructions. + // now delete the instructions... we've already taken care of the damn + // targets, but since TargetLostException is checked, we have to do this stuff. + try { + for (InstructionHandle oldIh = start.getNext(); oldIh != end;) { + InstructionHandle next = oldIh.getNext(); + body.delete(oldIh); + oldIh = next; + } + } catch (TargetLostException e) { + throw new BCException("shouldn't have gotten a target lost"); + } + + // now add the return, if one is warranted. + InstructionHandle ret = null; + if (addReturn) { + // we really should pull this out somewhere... + ret = freshBody.append(InstructionFactory.createReturn(freshMethod.getReturnType())); + } + // and remap all the old targeters of the end handle of the range to the return. + for (InstructionTargeter t : end.getTargetersCopy()) { + if (t == this) { + continue; + } + if (!addReturn) { + throw new BCException("range has target, but we aren't adding a return"); + } else { + t.updateTarget(end, ret); + } + } + + LazyMethodGen.assertGoodBody(getBody(), toString()); + freshMethod.assertGoodBody(); + } + + public BcelShadow getShadow() { + return shadow; + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/TypeAnnotationAccessVar.java b/weaver/src/main/java/org/aspectj/weaver/bcel/TypeAnnotationAccessVar.java new file mode 100644 index 000000000..e23174389 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/TypeAnnotationAccessVar.java @@ -0,0 +1,80 @@ +/* ******************************************************************* + * Copyright (c) 2005 IBM + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.bcel; + +import org.aspectj.apache.bcel.Constants; +import org.aspectj.apache.bcel.generic.Instruction; +import org.aspectj.apache.bcel.generic.InstructionFactory; +import org.aspectj.apache.bcel.generic.InstructionList; +import org.aspectj.apache.bcel.generic.ObjectType; +import org.aspectj.apache.bcel.generic.Type; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; + +/** + * Used for @this() @target() @args() - represents accessing an annotated 'thing'. Main use is to create the instructions that + * retrieve the annotation from the 'thing' - see createLoadInstructions() + */ +public class TypeAnnotationAccessVar extends BcelVar { + + private BcelVar target; + + public TypeAnnotationAccessVar(ResolvedType type, BcelVar theAnnotatedTargetIsStoredHere) { + super(type, 0); + target = theAnnotatedTargetIsStoredHere; + } + + public String toString() { + return "TypeAnnotationAccessVar(" + getType() + ")"; + } + + public Instruction createLoad(InstructionFactory fact) { + throw new RuntimeException("unimplemented"); + } + + public Instruction createStore(InstructionFactory fact) { + throw new RuntimeException("unimplemented"); + } + + public InstructionList createCopyFrom(InstructionFactory fact, int oldSlot) { + throw new RuntimeException("unimplemented"); + } + + public void appendLoad(InstructionList il, InstructionFactory fact) { + il.append(createLoadInstructions(getType(), fact)); + } + + public InstructionList createLoadInstructions(ResolvedType toType, InstructionFactory fact) { + InstructionList il = new InstructionList(); + Type jlClass = BcelWorld.makeBcelType(UnresolvedType.JL_CLASS); + Type jlaAnnotation = BcelWorld.makeBcelType(UnresolvedType.ANNOTATION); + il.append(target.createLoad(fact)); + il.append(fact.createInvoke("java/lang/Object", "getClass", jlClass, new Type[] {}, Constants.INVOKEVIRTUAL)); + il.append(fact.createConstant(new ObjectType(toType.getName()))); + il.append(fact.createInvoke("java/lang/Class", "getAnnotation", jlaAnnotation, new Type[] { jlClass }, + Constants.INVOKEVIRTUAL)); + il.append(Utility.createConversion(fact, jlaAnnotation, BcelWorld.makeBcelType(toType))); + return il; + + } + + public void appendLoadAndConvert(InstructionList il, InstructionFactory fact, ResolvedType toType) { + il.append(createLoadInstructions(toType, fact)); + + } + + public void insertLoad(InstructionList il, InstructionFactory fact) { + il.insert(createLoadInstructions(getType(), fact)); + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/TypeDelegateResolver.java b/weaver/src/main/java/org/aspectj/weaver/bcel/TypeDelegateResolver.java new file mode 100644 index 000000000..2a29c927a --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/TypeDelegateResolver.java @@ -0,0 +1,28 @@ +/* ******************************************************************* + * Copyright (c) 2010 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement, SpringSource + * ******************************************************************/ +package org.aspectj.weaver.bcel; + +import org.aspectj.weaver.ReferenceType; +import org.aspectj.weaver.ReferenceTypeDelegate; + +/** + * A type delegate resolver is able to create type delegates for a named reference type. A type delegate will implement + * ReferenceTypeDelegate. There are three kind of delegate already in existence: those created for eclipse structures, those + * created for bytecode structures, and those created based on reflection. + * + * @author Andy Clement + */ +public interface TypeDelegateResolver { + + ReferenceTypeDelegate getDelegate(ReferenceType referenceType); + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/UnwovenClassFile.java b/weaver/src/main/java/org/aspectj/weaver/bcel/UnwovenClassFile.java new file mode 100644 index 000000000..7076316f7 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/UnwovenClassFile.java @@ -0,0 +1,200 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.bcel; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.util.FileUtil; +import org.aspectj.weaver.IUnwovenClassFile; + +public class UnwovenClassFile implements IUnwovenClassFile { + protected String filename; + protected char[] charfilename; + protected byte[] bytes; + // protected JavaClass javaClass = null; + // protected byte[] writtenBytes = null; + protected List writtenChildClasses = Collections.emptyList(); + protected String className = null; + protected boolean isModule = false; + + public UnwovenClassFile(String filename, byte[] bytes) { + this.filename = filename; + this.isModule = filename.toLowerCase().endsWith("module-info.java"); + this.bytes = bytes; + } + + /** Use if the classname is known, saves a bytecode parse */ + public UnwovenClassFile(String filename, String classname, byte[] bytes) { + this.filename = filename; + this.isModule = filename.toLowerCase().endsWith("module-info.class"); + this.className = classname; + this.bytes = bytes; + } + + public boolean shouldBeWoven() { + // Skip module-info files for now, they aren't really types + return !isModule; + } + + public String getFilename() { + return filename; + } + + public String makeInnerFileName(String innerName) { + String prefix = filename.substring(0, filename.length() - 6); // strip the .class + return prefix + "$" + innerName + ".class"; + } + + public byte[] getBytes() { + // if (bytes == null) bytes = javaClass.getBytes(); + return bytes; + } + + public JavaClass getJavaClass() { + // XXX need to know when to make a new class and when not to + // XXX this is an important optimization + if (getBytes() == null) { + System.out.println("no bytes for: " + getFilename()); + // Thread.currentThread().dumpStack(); + Thread.dumpStack(); + } + return Utility.makeJavaClass(filename, getBytes()); + // if (javaClass == null) javaClass = Utility.makeJavaClass(filename, getBytes()); + // return javaClass; + } + + public void writeUnchangedBytes() throws IOException { + writeWovenBytes(getBytes(), Collections.emptyList()); + } + + public void writeWovenBytes(byte[] bytes, List childClasses) throws IOException { + writeChildClasses(childClasses); + + // System.err.println("should write: " + getClassName()); + + // System.err.println("about to write: " + this + ", " + writtenBytes + ", "); + // + writtenBytes != null + " && " + unchanged(bytes, writtenBytes) ); + + // if (writtenBytes != null && unchanged(bytes, writtenBytes)) return; + + // System.err.println(" actually wrote it"); + + BufferedOutputStream os = FileUtil.makeOutputStream(new File(filename)); + os.write(bytes); + os.close(); + + // writtenBytes = bytes; + } + + private void writeChildClasses(List childClasses) throws IOException { + // ??? we only really need to delete writtenChildClasses whose + // ??? names aren't in childClasses; however, it's unclear + // ??? how much that will affect performance + deleteAllChildClasses(); + + childClasses.removeAll(writtenChildClasses); // XXX is this right + + for (ChildClass childClass : childClasses) { + writeChildClassFile(childClass.name, childClass.bytes); + } + + writtenChildClasses = childClasses; + + } + + private void writeChildClassFile(String innerName, byte[] bytes) throws IOException { + BufferedOutputStream os = FileUtil.makeOutputStream(new File(makeInnerFileName(innerName))); + os.write(bytes); + os.close(); + } + + protected void deleteAllChildClasses() { + for (ChildClass childClass : writtenChildClasses) { + deleteChildClassFile(childClass.name); + } + } + + protected void deleteChildClassFile(String innerName) { + File childClassFile = new File(makeInnerFileName(innerName)); + childClassFile.delete(); + } + + /* private */static boolean unchanged(byte[] b1, byte[] b2) { + int len = b1.length; + if (b2.length != len) + return false; + for (int i = 0; i < len; i++) { + if (b1[i] != b2[i]) + return false; + } + return true; + } + + public char[] getClassNameAsChars() { + if (charfilename == null) { + charfilename = getClassName().replace('.', '/').toCharArray(); + } + return charfilename; + } + + public String getClassName() { + if (className == null) + className = getJavaClass().getClassName(); // OPTIMIZE quicker way to determine name??? surely? + return className; + } + + @Override + public String toString() { + return "UnwovenClassFile(" + filename + ", " + getClassName() + ")"; + } + + // record + // OPTIMIZE why is the 'short name' used here (the bit after the dollar) - seems we mess about a lot trimming it off only to put + // it back on! + public static class ChildClass { + public final String name; + public final byte[] bytes; + + ChildClass(String name, byte[] bytes) { + this.name = name; + this.bytes = bytes; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof ChildClass)) + return false; + ChildClass o = (ChildClass) other; + return o.name.equals(name) && unchanged(o.bytes, bytes); + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public String toString() { + return "(ChildClass " + name + ")"; + } + } + + public void setClassNameAsChars(char[] classNameAsChars) { + this.charfilename = classNameAsChars; + } +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/UnwovenClassFileWithThirdPartyManagedBytecode.java b/weaver/src/main/java/org/aspectj/weaver/bcel/UnwovenClassFileWithThirdPartyManagedBytecode.java new file mode 100644 index 000000000..18edc8413 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/UnwovenClassFileWithThirdPartyManagedBytecode.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.aspectj.weaver.bcel; + +/** + * @author colyer This subclass of UnwovenClassFile allows a third-party to manage the actual bytes that comprise the class. This + * means the third party can return a reference to an existing array, or create the bytes on demand, or apply any other + * strategy that makes sense. By refering to bytes held elsewhere, the goal is to reduce the overall memory consumption by + * not holding a copy. + */ +public class UnwovenClassFileWithThirdPartyManagedBytecode extends UnwovenClassFile { + + IByteCodeProvider provider; + + public interface IByteCodeProvider { + byte[] getBytes(); + } + + // OPTIMIZE make classname an input char[] + public UnwovenClassFileWithThirdPartyManagedBytecode(String filename, String classname, IByteCodeProvider provider) { + super(filename, classname, null); + this.provider = provider; + } + + public byte[] getBytes() { + return provider.getBytes(); + } +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/Utility.java b/weaver/src/main/java/org/aspectj/weaver/bcel/Utility.java new file mode 100644 index 000000000..4acf032fc --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/Utility.java @@ -0,0 +1,719 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.bcel; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; + +import org.aspectj.apache.bcel.Constants; +import org.aspectj.apache.bcel.classfile.Attribute; +import org.aspectj.apache.bcel.classfile.ClassParser; +import org.aspectj.apache.bcel.classfile.ConstantPool; +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.classfile.Unknown; +import org.aspectj.apache.bcel.classfile.annotation.ArrayElementValue; +import org.aspectj.apache.bcel.classfile.annotation.ElementValue; +import org.aspectj.apache.bcel.classfile.annotation.NameValuePair; +import org.aspectj.apache.bcel.classfile.annotation.SimpleElementValue; +import org.aspectj.apache.bcel.generic.ArrayType; +import org.aspectj.apache.bcel.generic.BasicType; +import org.aspectj.apache.bcel.generic.Instruction; +import org.aspectj.apache.bcel.generic.InstructionByte; +import org.aspectj.apache.bcel.generic.InstructionCP; +import org.aspectj.apache.bcel.generic.InstructionConstants; +import org.aspectj.apache.bcel.generic.InstructionFactory; +import org.aspectj.apache.bcel.generic.InstructionHandle; +import org.aspectj.apache.bcel.generic.InstructionList; +import org.aspectj.apache.bcel.generic.InstructionSelect; +import org.aspectj.apache.bcel.generic.InstructionShort; +import org.aspectj.apache.bcel.generic.InstructionTargeter; +import org.aspectj.apache.bcel.generic.LineNumberTag; +import org.aspectj.apache.bcel.generic.ObjectType; +import org.aspectj.apache.bcel.generic.ReferenceType; +import org.aspectj.apache.bcel.generic.SwitchBuilder; +import org.aspectj.apache.bcel.generic.TargetLostException; +import org.aspectj.apache.bcel.generic.Type; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.weaver.AjAttribute; +import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; +import org.aspectj.weaver.AnnotationAJ; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.ConstantPoolReader; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.Lint; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.Utils; +import org.aspectj.weaver.World; + +public class Utility { + + private final static char PACKAGE_INITIAL_CHAR = AjAttribute.AttributePrefix.charAt(0); + + public static List readAjAttributes(String classname, Attribute[] as, ISourceContext context, World w, + AjAttribute.WeaverVersionInfo version, ConstantPoolReader dataDecompressor) { + List attributes = new ArrayList(); + + // first pass, look for version + List forSecondPass = new ArrayList(); + for (int i = as.length - 1; i >= 0; i--) { + Attribute a = as[i]; + if (a instanceof Unknown) { + Unknown u = (Unknown) a; + String name = u.getName(); + if (name.charAt(0) == PACKAGE_INITIAL_CHAR) { // 'o'rg.aspectj + if (name.startsWith(AjAttribute.AttributePrefix)) { + if (name.endsWith(WeaverVersionInfo.AttributeName)) { + version = (AjAttribute.WeaverVersionInfo) AjAttribute.read(version, name, u.getBytes(), context, w, + dataDecompressor); + if (version.getMajorVersion() > WeaverVersionInfo.getCurrentWeaverMajorVersion()) { + throw new BCException( + "Unable to continue, this version of AspectJ supports classes built with weaver version " + + WeaverVersionInfo.toCurrentVersionString() + " but the class " + classname + + " is version " + version.toString() + ". Please update your AspectJ."); + } + } + forSecondPass.add(u); + } + } + } + } + + // FIXASC why going backwards? is it important + for (int i = forSecondPass.size() - 1; i >= 0; i--) { + Unknown a = forSecondPass.get(i); + String name = a.getName(); + AjAttribute attr = AjAttribute.read(version, name, a.getBytes(), context, w, dataDecompressor); + if (attr != null) { + attributes.add(attr); + } + } + return attributes; + } + + /* + * Ensure we report a nice source location - particular in the case where the source info is missing (binary weave). + */ + public static String beautifyLocation(ISourceLocation isl) { + StringBuffer nice = new StringBuffer(); + if (isl == null || isl.getSourceFile() == null || isl.getSourceFile().getName().indexOf("no debug info available") != -1) { + nice.append("no debug info available"); + } else { + // can't use File.getName() as this fails when a Linux box + // encounters a path created on Windows and vice-versa + int takeFrom = isl.getSourceFile().getPath().lastIndexOf('/'); + if (takeFrom == -1) { + takeFrom = isl.getSourceFile().getPath().lastIndexOf('\\'); + } + nice.append(isl.getSourceFile().getPath().substring(takeFrom + 1)); + if (isl.getLine() != 0) { + nice.append(":").append(isl.getLine()); + } + } + return nice.toString(); + } + + public static Instruction createSuperInvoke(InstructionFactory fact, BcelWorld world, Member signature) { + short kind; + if (Modifier.isInterface(signature.getModifiers())) { + throw new RuntimeException("bad"); + } else if (Modifier.isPrivate(signature.getModifiers()) || signature.getName().equals("")) { + throw new RuntimeException("unimplemented, possibly bad"); + } else if (Modifier.isStatic(signature.getModifiers())) { + throw new RuntimeException("bad"); + } else { + kind = Constants.INVOKESPECIAL; + } + + return fact.createInvoke(signature.getDeclaringType().getName(), signature.getName(), + BcelWorld.makeBcelType(signature.getReturnType()), BcelWorld.makeBcelTypes(signature.getParameterTypes()), kind); + } + + public static Instruction createInvoke(InstructionFactory fact, BcelWorld world, Member signature) { + short kind; + int signatureModifiers = signature.getModifiers(); + if (Modifier.isInterface(signatureModifiers)) { + kind = Constants.INVOKEINTERFACE; + } else if (Modifier.isStatic(signatureModifiers)) { + kind = Constants.INVOKESTATIC; + } else if (Modifier.isPrivate(signatureModifiers) || signature.getName().equals("")) { + kind = Constants.INVOKESPECIAL; + } else { + kind = Constants.INVOKEVIRTUAL; + } + + UnresolvedType targetType = signature.getDeclaringType(); + if (targetType.isParameterizedType()) { + targetType = targetType.resolve(world).getGenericType(); + } + return fact.createInvoke(targetType.getName(), signature.getName(), BcelWorld.makeBcelType(signature.getReturnType()), + BcelWorld.makeBcelTypes(signature.getParameterTypes()), kind); + } + + public static Instruction createGet(InstructionFactory fact, Member signature) { + short kind; + if (Modifier.isStatic(signature.getModifiers())) { + kind = Constants.GETSTATIC; + } else { + kind = Constants.GETFIELD; + } + + return fact.createFieldAccess(signature.getDeclaringType().getName(), signature.getName(), + BcelWorld.makeBcelType(signature.getReturnType()), kind); + } + + public static Instruction createSet(InstructionFactory fact, Member signature) { + short kind; + if (Modifier.isStatic(signature.getModifiers())) { + kind = Constants.PUTSTATIC; + } else { + kind = Constants.PUTFIELD; + } + + return fact.createFieldAccess(signature.getDeclaringType().getName(), signature.getName(), + BcelWorld.makeBcelType(signature.getReturnType()), kind); + } + + public static Instruction createInstanceof(InstructionFactory fact, ReferenceType t) { + int cpoolEntry = (t instanceof ArrayType) ? fact.getConstantPool().addArrayClass((ArrayType) t) : fact.getConstantPool() + .addClass((ObjectType) t); + return new InstructionCP(Constants.INSTANCEOF, cpoolEntry); + } + + public static Instruction createInvoke(InstructionFactory fact, LazyMethodGen m) { + short kind; + if (m.getEnclosingClass().isInterface()) { + if (m.isStatic()) { + // For static methods on interfaces + kind = Constants.INVOKESTATIC; + } else { + kind = Constants.INVOKEINTERFACE; + } + } else if (m.isStatic()) { + kind = Constants.INVOKESTATIC; + } else if (m.isPrivate() || m.getName().equals("")) { + kind = Constants.INVOKESPECIAL; + } else { + kind = Constants.INVOKEVIRTUAL; + } + + return fact.createInvoke(m.getClassName(), m.getName(), m.getReturnType(), m.getArgumentTypes(), kind, m.getEnclosingClass().isInterface()); + } + + /** + * Create an invoke instruction + * + * @param fact + * @param kind INVOKEINTERFACE, INVOKEVIRTUAL.. + * @param member + * @return + */ + public static Instruction createInvoke(InstructionFactory fact, short kind, Member member) { + return fact.createInvoke(member.getDeclaringType().getName(), member.getName(), + BcelWorld.makeBcelType(member.getReturnType()), BcelWorld.makeBcelTypes(member.getParameterTypes()), kind); + } + + private static String[] argNames = new String[] { "arg0", "arg1", "arg2", "arg3", "arg4" }; + + // ??? these should perhaps be cached. Remember to profile this to see if + // it's a problem. + public static String[] makeArgNames(int n) { + String[] ret = new String[n]; + for (int i = 0; i < n; i++) { + if (i < 5) { + ret[i] = argNames[i]; + } else { + ret[i] = "arg" + i; + } + } + return ret; + } + + // Lookup table, for converting between pairs of types, it gives + // us the method name in the Conversions class + private static Hashtable validBoxing = new Hashtable(); + + static { + validBoxing.put("Ljava/lang/Byte;B", "byteObject"); + validBoxing.put("Ljava/lang/Character;C", "charObject"); + validBoxing.put("Ljava/lang/Double;D", "doubleObject"); + validBoxing.put("Ljava/lang/Float;F", "floatObject"); + validBoxing.put("Ljava/lang/Integer;I", "intObject"); + validBoxing.put("Ljava/lang/Long;J", "longObject"); + validBoxing.put("Ljava/lang/Short;S", "shortObject"); + validBoxing.put("Ljava/lang/Boolean;Z", "booleanObject"); + validBoxing.put("BLjava/lang/Byte;", "byteValue"); + validBoxing.put("CLjava/lang/Character;", "charValue"); + validBoxing.put("DLjava/lang/Double;", "doubleValue"); + validBoxing.put("FLjava/lang/Float;", "floatValue"); + validBoxing.put("ILjava/lang/Integer;", "intValue"); + validBoxing.put("JLjava/lang/Long;", "longValue"); + validBoxing.put("SLjava/lang/Short;", "shortValue"); + validBoxing.put("ZLjava/lang/Boolean;", "booleanValue"); + } + + public static void appendConversion(InstructionList il, InstructionFactory fact, ResolvedType fromType, ResolvedType toType) { + if (!toType.isConvertableFrom(fromType) && !fromType.isConvertableFrom(toType)) { + throw new BCException("can't convert from " + fromType + " to " + toType); + } + // XXX I'm sure this test can be simpler but my brain hurts and this works + World w = toType.getWorld(); + if (w == null) { // dbg349636 + throw new IllegalStateException("Debug349636: Unexpectedly found world null for type " + toType.getName()); + } + + if (!w.isInJava5Mode()) { + if (toType.needsNoConversionFrom(fromType)) { + return; + } + } else { + if (toType.needsNoConversionFrom(fromType) && !(toType.isPrimitiveType() ^ fromType.isPrimitiveType())) { + return; + } + } + if (toType.equals(UnresolvedType.VOID)) { + // assert fromType.equals(UnresolvedType.OBJECT) + il.append(InstructionFactory.createPop(fromType.getSize())); + } else if (fromType.equals(UnresolvedType.VOID)) { + // assert toType.equals(UnresolvedType.OBJECT) + il.append(InstructionFactory.createNull(Type.OBJECT)); + return; + } else if (fromType.equals(UnresolvedType.OBJECT)) { + Type to = BcelWorld.makeBcelType(toType); + if (toType.isPrimitiveType()) { + String name = toType.toString() + "Value"; + il.append(fact.createInvoke("org.aspectj.runtime.internal.Conversions", name, to, new Type[] { Type.OBJECT }, + Constants.INVOKESTATIC)); + } else { + il.append(fact.createCheckCast((ReferenceType) to)); + } + } else if (toType.equals(UnresolvedType.OBJECT)) { + // assert fromType.isPrimitive() + Type from = BcelWorld.makeBcelType(fromType); + String name = fromType.toString() + "Object"; + il.append(fact.createInvoke("org.aspectj.runtime.internal.Conversions", name, Type.OBJECT, new Type[] { from }, + Constants.INVOKESTATIC)); + } else if (toType.getWorld().isInJava5Mode() && validBoxing.get(toType.getSignature() + fromType.getSignature()) != null) { + // XXX could optimize by using any java boxing code that may be just + // before the call... + Type from = BcelWorld.makeBcelType(fromType); + Type to = BcelWorld.makeBcelType(toType); + String name = validBoxing.get(toType.getSignature() + fromType.getSignature()); + if (toType.isPrimitiveType()) { + il.append(fact.createInvoke("org.aspectj.runtime.internal.Conversions", name, to, new Type[] { Type.OBJECT }, + Constants.INVOKESTATIC)); + } else { + il.append(fact.createInvoke("org.aspectj.runtime.internal.Conversions", name, Type.OBJECT, new Type[] { from }, + Constants.INVOKESTATIC)); + il.append(fact.createCheckCast((ReferenceType) to)); + } + } else if (fromType.isPrimitiveType()) { + // assert toType.isPrimitive() + Type from = BcelWorld.makeBcelType(fromType); + Type to = BcelWorld.makeBcelType(toType); + try { + Instruction i = fact.createCast(from, to); + if (i != null) { + il.append(i); + } else { + il.append(fact.createCast(from, Type.INT)); + il.append(fact.createCast(Type.INT, to)); + } + } catch (RuntimeException e) { + il.append(fact.createCast(from, Type.INT)); + il.append(fact.createCast(Type.INT, to)); + } + } else { + Type to = BcelWorld.makeBcelType(toType); + // assert ! fromType.isPrimitive() && ! toType.isPrimitive() + il.append(fact.createCheckCast((ReferenceType) to)); + } + } + + public static InstructionList createConversion(InstructionFactory factory, Type fromType, Type toType) { + return createConversion(factory, fromType, toType, false); + } + + public static InstructionList createConversion(InstructionFactory fact, Type fromType, Type toType, boolean allowAutoboxing) { + // System.out.println("cast to: " + toType); + + InstructionList il = new InstructionList(); + + // PR71273 + if ((fromType.equals(Type.BYTE) || fromType.equals(Type.CHAR) || fromType.equals(Type.SHORT)) && (toType.equals(Type.INT))) { + return il; + } + + if (fromType.equals(toType)) { + return il; + } + if (toType.equals(Type.VOID)) { + il.append(InstructionFactory.createPop(fromType.getSize())); + return il; + } + + if (fromType.equals(Type.VOID)) { + if (toType instanceof BasicType) { + throw new BCException("attempting to cast from void to basic type"); + } + il.append(InstructionFactory.createNull(Type.OBJECT)); + return il; + } + + if (fromType.equals(Type.OBJECT)) { + if (toType instanceof BasicType) { + String name = toType.toString() + "Value"; + il.append(fact.createInvoke("org.aspectj.runtime.internal.Conversions", name, toType, new Type[] { Type.OBJECT }, + Constants.INVOKESTATIC)); + return il; + } + } + + if (toType.equals(Type.OBJECT)) { + if (fromType instanceof BasicType) { + String name = fromType.toString() + "Object"; + il.append(fact.createInvoke("org.aspectj.runtime.internal.Conversions", name, Type.OBJECT, new Type[] { fromType }, + Constants.INVOKESTATIC)); + return il; + } else if (fromType instanceof ReferenceType) { + return il; + } else { + throw new RuntimeException(); + } + } + + if (fromType instanceof ReferenceType && ((ReferenceType) fromType).isAssignmentCompatibleWith(toType)) { + return il; + } + + if (allowAutoboxing) { + if (toType instanceof BasicType && fromType instanceof ReferenceType) { + // unboxing + String name = toType.toString() + "Value"; + il.append(fact.createInvoke("org.aspectj.runtime.internal.Conversions", name, toType, new Type[] { Type.OBJECT }, + Constants.INVOKESTATIC)); + return il; + } + + if (fromType instanceof BasicType && toType instanceof ReferenceType) { + // boxing + String name = fromType.toString() + "Object"; + il.append(fact.createInvoke("org.aspectj.runtime.internal.Conversions", name, Type.OBJECT, new Type[] { fromType }, + Constants.INVOKESTATIC)); + il.append(fact.createCast(Type.OBJECT, toType)); + return il; + } + } + + il.append(fact.createCast(fromType, toType)); + return il; + } + + public static Instruction createConstant(InstructionFactory fact, int value) { + Instruction inst; + switch (value) { + case -1: + inst = InstructionConstants.ICONST_M1; + break; + case 0: + inst = InstructionConstants.ICONST_0; + break; + case 1: + inst = InstructionConstants.ICONST_1; + break; + case 2: + inst = InstructionConstants.ICONST_2; + break; + case 3: + inst = InstructionConstants.ICONST_3; + break; + case 4: + inst = InstructionConstants.ICONST_4; + break; + case 5: + inst = InstructionConstants.ICONST_5; + break; + default: + if (value <= Byte.MAX_VALUE && value >= Byte.MIN_VALUE) { + inst = new InstructionByte(Constants.BIPUSH, (byte) value); + } else if (value <= Short.MAX_VALUE && value >= Short.MIN_VALUE) { + inst = new InstructionShort(Constants.SIPUSH, (short) value); + } else { + int ii = fact.getClassGen().getConstantPool().addInteger(value); + inst = new InstructionCP(value <= Constants.MAX_BYTE ? Constants.LDC : Constants.LDC_W, ii); + } + break; + } + return inst; + } + + /** For testing purposes: bit clunky but does work */ + public static int testingParseCounter = 0; + + public static JavaClass makeJavaClass(String filename, byte[] bytes) { + try { + testingParseCounter++; + ClassParser parser = new ClassParser(new ByteArrayInputStream(bytes), filename); + return parser.parse(); + } catch (IOException e) { + throw new BCException("malformed class file"); + } + } + + /** + * replace an instruction handle with another instruction, in this case, a branch instruction. + * + * @param ih the instruction handle to replace. + * @param branchInstruction the branch instruction to replace ih with + * @param enclosingMethod where to find ih's instruction list. + */ + public static void replaceInstruction(InstructionHandle ih, InstructionList replacementInstructions, + LazyMethodGen enclosingMethod) { + InstructionList il = enclosingMethod.getBody(); + InstructionHandle fresh = il.append(ih, replacementInstructions); + deleteInstruction(ih, fresh, enclosingMethod); + } + + /** + * delete an instruction handle and retarget all targeters of the deleted instruction to the next instruction. Obviously, this + * should not be used to delete a control transfer instruction unless you know what you're doing. + * + * @param ih the instruction handle to delete. + * @param enclosingMethod where to find ih's instruction list. + */ + public static void deleteInstruction(InstructionHandle ih, LazyMethodGen enclosingMethod) { + deleteInstruction(ih, ih.getNext(), enclosingMethod); + } + + /** + * delete an instruction handle and retarget all targeters of the deleted instruction to the provided target. + * + * @param ih the instruction handle to delete + * @param retargetTo the instruction handle to retarget targeters of ih to. + * @param enclosingMethod where to find ih's instruction list. + */ + public static void deleteInstruction(InstructionHandle ih, InstructionHandle retargetTo, LazyMethodGen enclosingMethod) { + InstructionList il = enclosingMethod.getBody(); + for (InstructionTargeter targeter : ih.getTargetersCopy()) { + targeter.updateTarget(ih, retargetTo); + } + ih.removeAllTargeters(); + try { + il.delete(ih); + } catch (TargetLostException e) { + throw new BCException("this really can't happen"); + } + } + + /** + * Fix for Bugzilla #39479, #40109 patch contributed by Andy Clement + * + * Need to manually copy Select instructions - if we rely on the the 'fresh' object created by copy(), the InstructionHandle + * array 'targets' inside the Select object will not have been deep copied, so modifying targets in fresh will modify the + * original Select - not what we want ! (It is a bug in BCEL to do with cloning Select objects). + * + *
+	 * declare error:
+	 *     call(* Instruction.copy()) && within(org.aspectj.weaver)
+	 *       && !withincode(* Utility.copyInstruction(Instruction)):
+	 *     "use Utility.copyInstruction to work-around bug in Select.copy()";
+	 * 
+ */ + public static Instruction copyInstruction(Instruction i) { + if (i instanceof InstructionSelect) { + InstructionSelect freshSelect = (InstructionSelect) i; + + // Create a new targets array that looks just like the existing one + InstructionHandle[] targets = new InstructionHandle[freshSelect.getTargets().length]; + for (int ii = 0; ii < targets.length; ii++) { + targets[ii] = freshSelect.getTargets()[ii]; + } + + // Create a new select statement with the new targets array + + return new SwitchBuilder(freshSelect.getMatchs(), targets, freshSelect.getTarget()).getInstruction(); + } else { + return i.copy(); // Use clone for shallow copy... + } + } + + /** returns -1 if no source line attribute */ + // this naive version overruns the JVM stack size, if only Java understood + // tail recursion... + // public static int getSourceLine(InstructionHandle ih) { + // if (ih == null) return -1; + // + // InstructionTargeter[] ts = ih.getTargeters(); + // if (ts != null) { + // for (int j = ts.length - 1; j >= 0; j--) { + // InstructionTargeter t = ts[j]; + // if (t instanceof LineNumberTag) { + // return ((LineNumberTag)t).getLineNumber(); + // } + // } + // } + // return getSourceLine(ih.getNext()); + // } + public static int getSourceLine(InstructionHandle ih) {// ,boolean + // goforwards) { + int lookahead = 0; + // arbitrary rule that we will never lookahead more than 100 + // instructions for a line # + while (lookahead++ < 100) { + if (ih == null) { + return -1; + } + Iterator tIter = ih.getTargeters().iterator(); + while (tIter.hasNext()) { + InstructionTargeter t = tIter.next(); + if (t instanceof LineNumberTag) { + return ((LineNumberTag) t).getLineNumber(); + } + } + // if (goforwards) ih=ih.getNext(); else + ih = ih.getPrev(); + } + // System.err.println("no line information available for: " + ih); + return -1; + } + + // public static int getSourceLine(InstructionHandle ih) { + // return getSourceLine(ih,false); + // } + + // assumes that there is no already extant source line tag. Otherwise we'll + // have to be better. + public static void setSourceLine(InstructionHandle ih, int lineNumber) { + // OPTIMIZE LineNumberTag instances for the same line could be shared + // throughout a method... + ih.addTargeter(new LineNumberTag(lineNumber)); + } + + public static int makePublic(int i) { + return i & ~(Modifier.PROTECTED | Modifier.PRIVATE) | Modifier.PUBLIC; + } + + public static BcelVar[] pushAndReturnArrayOfVars(ResolvedType[] proceedParamTypes, InstructionList il, InstructionFactory fact, + LazyMethodGen enclosingMethod) { + int len = proceedParamTypes.length; + BcelVar[] ret = new BcelVar[len]; + + for (int i = len - 1; i >= 0; i--) { + ResolvedType typeX = proceedParamTypes[i]; + Type type = BcelWorld.makeBcelType(typeX); + int local = enclosingMethod.allocateLocal(type); + + il.append(InstructionFactory.createStore(type, local)); + ret[i] = new BcelVar(typeX, local); + } + return ret; + } + + public static boolean isConstantPushInstruction(Instruction i) { + long ii = Constants.instFlags[i.opcode]; + return ((ii & Constants.PUSH_INST) != 0 && (ii & Constants.CONSTANT_INST) != 0); + } + + /** + * Checks for suppression specified on the member or on the declaring type of that member + */ + public static boolean isSuppressing(Member member, String lintkey) { + boolean isSuppressing = Utils.isSuppressing(member.getAnnotations(), lintkey); + if (isSuppressing) { + return true; + } + UnresolvedType type = member.getDeclaringType(); + if (type instanceof ResolvedType) { + return Utils.isSuppressing(((ResolvedType) type).getAnnotations(), lintkey); + } + return false; + } + + public static List getSuppressedWarnings(AnnotationAJ[] anns, Lint lint) { + if (anns == null) { + return Collections.emptyList(); + } + // Go through the annotation types + List suppressedWarnings = new ArrayList(); + boolean found = false; + for (int i = 0; !found && i < anns.length; i++) { + // Check for the SuppressAjWarnings annotation + if (UnresolvedType.SUPPRESS_AJ_WARNINGS.getSignature().equals( + ((BcelAnnotation) anns[i]).getBcelAnnotation().getTypeSignature())) { + found = true; + // Two possibilities: + // 1. there are no values specified (i.e. @SuppressAjWarnings) + // 2. there are values specified (i.e. @SuppressAjWarnings("A") + // or @SuppressAjWarnings({"A","B"}) + List vals = ((BcelAnnotation) anns[i]).getBcelAnnotation().getValues(); + if (vals == null || vals.isEmpty()) { // (1) + suppressedWarnings.addAll(lint.allKinds()); + } else { // (2) + // We know the value is an array value + ArrayElementValue array = (ArrayElementValue) (vals.get(0)).getValue(); + ElementValue[] values = array.getElementValuesArray(); + for (int j = 0; j < values.length; j++) { + // We know values in the array are strings + SimpleElementValue value = (SimpleElementValue) values[j]; + Lint.Kind lintKind = lint.getLintKind(value.getValueString()); + if (lintKind != null) { + suppressedWarnings.add(lintKind); + } + } + } + } + } + return suppressedWarnings; + } + + // not yet used... + // public static boolean isSimple(Method method) { + // if (method.getCode()==null) return true; + // if (method.getCode().getCode().length>10) return false; + // InstructionList instrucs = new + // InstructionList(method.getCode().getCode()); // expensive! + // InstructionHandle InstrHandle = instrucs.getStart(); + // while (InstrHandle != null) { + // Instruction Instr = InstrHandle.getInstruction(); + // int opCode = Instr.opcode; + // // if current instruction is a branch instruction, see if it's a backward + // branch. + // // if it is return immediately (can't be trivial) + // if (Instr instanceof InstructionBranch) { + // // InstructionBranch BI = (InstructionBranch) Instr; + // if (Instr.getIndex() < 0) return false; + // } else if (Instr instanceof InvokeInstruction) { + // // if current instruction is an invocation, indicate that it can't be + // trivial + // return false; + // } + // InstrHandle = InstrHandle.getNext(); + // } + // return true; + // } + + public static Attribute bcelAttribute(AjAttribute a, ConstantPool pool) { + int nameIndex = pool.addUtf8(a.getNameString()); + byte[] bytes = a.getBytes(new BcelConstantPoolWriter(pool)); + int length = bytes.length; + + return new Unknown(nameIndex, length, bytes, pool); + } +} \ No newline at end of file diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/asm/AsmDetector.java b/weaver/src/main/java/org/aspectj/weaver/bcel/asm/AsmDetector.java new file mode 100644 index 000000000..5d5bb6990 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/asm/AsmDetector.java @@ -0,0 +1,36 @@ +/* ******************************************************************* + * Copyright (c) 2008 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement + * ******************************************************************/ +package org.aspectj.weaver.bcel.asm; + +import java.lang.reflect.Method; + +/** + * Determines if a version of asm is around that will enable us to add stack map attributes to classes that we produce. + * + * @author Andy Clement + */ +public class AsmDetector { + + public static boolean isAsmAround; + + static { + try { + Class reader = Class.forName("aj.org.objectweb.asm.ClassReader"); + Class visitor = Class.forName("aj.org.objectweb.asm.ClassVisitor"); + Method m = reader.getMethod("accept", new Class[] { visitor, Integer.TYPE }); + isAsmAround = m != null; + } catch (Exception e) { + isAsmAround = false; + } + // System.out.println(isAsmAround?"ASM detected":"No ASM found"); + } +} diff --git a/weaver/src/main/java/org/aspectj/weaver/bcel/asm/StackMapAdder.java b/weaver/src/main/java/org/aspectj/weaver/bcel/asm/StackMapAdder.java new file mode 100644 index 000000000..dd3965b61 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/bcel/asm/StackMapAdder.java @@ -0,0 +1,121 @@ +/* ******************************************************************* + * Copyright (c) 2008, 2018 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement + * ******************************************************************/ +package org.aspectj.weaver.bcel.asm; + +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.World; + +import aj.org.objectweb.asm.ClassReader; +import aj.org.objectweb.asm.ClassVisitor; +import aj.org.objectweb.asm.ClassWriter; +import aj.org.objectweb.asm.MethodVisitor; +import aj.org.objectweb.asm.Opcodes; + +/** + * Uses asm to add the stack map attribute to methods in a class. The class is passed in as pure byte data and then a reader/writer + * process it. The writer is wired into the world so that types can be resolved and getCommonSuperClass() can be implemented without + * class loading using the context class loader. + * + * It is important that the constant pool is preserved here and asm does not try to remove unused entries. That is because some + * entries are refered to from classfile attributes. Asm cannot see into these attributes so does not realise the constant pool + * entries are in use. In order to ensure the copying of cp occurs, we use the variant super constructor call in AspectJConnectClassWriter + * that passes in the classreader. However, ordinarily that change causes a further optimization: that if a classreader sees + * a methodvisitor that has been created by a ClassWriter then it just copies the data across without changing it (and so it + * fails to attach the stackmapattribute). In order to avoid this further optimization we use our own minimal MethodVisitor. + * + * @author Andy Clement + */ +public class StackMapAdder { + + public static byte[] addStackMaps(World world, byte[] data) { + try { + ClassReader cr = new ClassReader(data); + ClassWriter cw = new AspectJConnectClassWriter(cr, world); + ClassVisitor cv = new AspectJClassVisitor(cw); + cr.accept(cv, 0); + return cw.toByteArray(); + } catch (Throwable t) { + System.err.println("AspectJ Internal Error: unable to add stackmap attributes. " + t.getMessage()); + AsmDetector.isAsmAround = false; + return data; + } + } + + private static class AspectJClassVisitor extends ClassVisitor { + + public AspectJClassVisitor(ClassVisitor classwriter) { + super(Opcodes.ASM7, classwriter); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); + return new AJMethodVisitor(mv); + } + + // Minimal pass through MethodVisitor just so that the ClassReader doesn't see one that has been directly + // created by a ClassWriter (see top level class comment) + static class AJMethodVisitor extends MethodVisitor { + public AJMethodVisitor(MethodVisitor mv) { + super(Opcodes.ASM7,mv); + } + } + + } + + private static class AspectJConnectClassWriter extends ClassWriter { + private final World world; + + public AspectJConnectClassWriter(ClassReader cr, World w) { + super(cr, ClassWriter.COMPUTE_FRAMES); // passing in cr is necessary so cpool isnt modified (see 2.2.4 of asm doc) + this.world = w; + } + + + // Implementation of getCommonSuperClass() that avoids Class.forName() + @Override + protected String getCommonSuperClass(final String type1, final String type2) { + + ResolvedType resolvedType1 = world.resolve(UnresolvedType.forName(type1.replace('/', '.'))); + ResolvedType resolvedType2 = world.resolve(UnresolvedType.forName(type2.replace('/', '.'))); + + if (resolvedType1.isAssignableFrom(resolvedType2)) { + return type1; + } + + if (resolvedType2.isAssignableFrom(resolvedType1)) { + return type2; + } + + if (resolvedType1.isInterface() || resolvedType2.isInterface()) { + return "java/lang/Object"; + } else { + do { + resolvedType1 = resolvedType1.getSuperclass(); + if (resolvedType1 == null) { + // This happens if some types are missing, the getSuperclass() call on + // MissingResolvedTypeWithKnownSignature will return the Missing type which + // in turn returns a superclass of null. By returning Object here it + // should surface the cantFindType message raised in the first problematic + // getSuperclass call + return "java/lang/Object"; + } + if (resolvedType1.isParameterizedOrGenericType()) { + resolvedType1 = resolvedType1.getRawType(); + } + } while (!resolvedType1.isAssignableFrom(resolvedType2)); + return resolvedType1.getRawName().replace('.', '/'); + } + } + } +} diff --git a/weaver/src/main/java/org/aspectj/weaver/loadtime/IWeavingContext.java b/weaver/src/main/java/org/aspectj/weaver/loadtime/IWeavingContext.java new file mode 100644 index 000000000..51b781e86 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/loadtime/IWeavingContext.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * David Knibb initial implementation + *******************************************************************************/ +package org.aspectj.weaver.loadtime; + +import java.io.IOException; +import java.net.URL; +import java.util.Enumeration; +import java.util.List; + +import org.aspectj.weaver.loadtime.definition.Definition; +import org.aspectj.weaver.tools.WeavingAdaptor; + +/** + * This class adds support to AspectJ for an OSGi environment + * + * @author David Knibb + */ +public interface IWeavingContext { + + /** + * Allows the standard ClassLoader.getResources() mechanisms to be + * replaced with a different implementation. + * In an OSGi environment, this will allow for filtering to take + * place on the results of ClassLoader.getResources(). In a non-OSGi + * environment, ClassLoader.getResources should be returned. + * @param name the name of the resource to search for + * @return an enumeration containing all of the matching resources found + * @throws IOException + */ + public Enumeration getResources(String name) throws IOException; + + /** + * In an OSGi environment, determin which bundle a URL originated from. + * In a non-OSGi environment, implementors should return null. + * @param url + * @return + * @deprecated use getFile() or getClassLoaderName() + */ + public String getBundleIdFromURL(URL url); + + /** + * In an environment with multiple class loaders allows each to be + * identified using something safer and possibly shorter than toString + * @return name of the associated class loader + */ + public String getClassLoaderName (); + + public ClassLoader getClassLoader(); + + /** + * Format a URL + * @return filename + */ + public String getFile(URL url); + + /** + * In an environment with multiple class loaders allows messages + * to identified according to the weaving context + * @return short name + */ + public String getId (); + + /** + * Return true if the classloader associated with this weaving context + * is the one that will define the class with the specified name. + * In a delegating classloader hierarchy this might check the parent won't + * define it and the child will - in OSGi it will do something else. + * @param classname name of the class, eg. "java.lang.String" + * @return true if the associated classloader will define the class + */ + public boolean isLocallyDefined(String classname); + + /** + * Allow custom parsing of aop.xml or alternative mechanism for providing + * Definitions + * + * @param loader + * @param adaptor + * @return List containing 0 or more Definition instances + */ + public List getDefinitions(final ClassLoader loader, WeavingAdaptor adaptor); + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/loadtime/definition/Definition.java b/weaver/src/main/java/org/aspectj/weaver/loadtime/definition/Definition.java new file mode 100644 index 000000000..de780156e --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/loadtime/definition/Definition.java @@ -0,0 +1,222 @@ +/******************************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alexandre Vasseur initial implementation + *******************************************************************************/ +package org.aspectj.weaver.loadtime.definition; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * A POJO that contains raw strings from the XML (sort of XMLBean for our simple LTW DTD) + * + * @author
Alexandre Vasseur + */ +public class Definition { + + private final StringBuffer weaverOptions; + private final List dumpPatterns; + private boolean dumpBefore; + private boolean perClassloaderDumpDir; + private final List includePatterns; + private final List excludePatterns; + private final List aspectClassNames; + private final List aspectExcludePatterns; + private final List aspectIncludePatterns; + private final List concreteAspects; + + /** + * When aspects are defined, they can specify a scope type pattern and then will only apply to types matching that pattern. + */ + private final Map scopedAspects; + + /** + * Some aspects (from aspect libraries) will describe a type that must be around for them to function properly + */ + private final Map requiredTypesForAspects; + + public Definition() { + weaverOptions = new StringBuffer(); + dumpBefore = false; + perClassloaderDumpDir = false; + dumpPatterns = new ArrayList(); + includePatterns = new ArrayList(); + excludePatterns = new ArrayList(); + aspectClassNames = new ArrayList(); + aspectExcludePatterns = new ArrayList(); + aspectIncludePatterns = new ArrayList(); + concreteAspects = new ArrayList(); + scopedAspects = new HashMap(); + requiredTypesForAspects = new HashMap(); + } + + public String getWeaverOptions() { + return weaverOptions.toString(); + } + + public List getDumpPatterns() { + return dumpPatterns; + } + + public void setDumpBefore(boolean b) { + dumpBefore = b; + } + + public boolean shouldDumpBefore() { + return dumpBefore; + } + + public void setCreateDumpDirPerClassloader(boolean b) { + perClassloaderDumpDir = b; + } + + public boolean createDumpDirPerClassloader() { + return perClassloaderDumpDir; + } + + public List getIncludePatterns() { + return includePatterns; + } + + public List getExcludePatterns() { + return excludePatterns; + } + + public List getAspectClassNames() { + return aspectClassNames; + } + + public List getAspectExcludePatterns() { + return aspectExcludePatterns; + } + + public List getAspectIncludePatterns() { + return aspectIncludePatterns; + } + + public List getConcreteAspects() { + return concreteAspects; + } + + public static class ConcreteAspect { + public final String name; + public final String extend; + public final String precedence; + public final List pointcuts; + public final List declareAnnotations; + public final List pointcutsAndAdvice; + public final String perclause; + public List deows; + + public ConcreteAspect(String name, String extend) { + this(name, extend, null, null); + } + + public ConcreteAspect(String name, String extend, String precedence, String perclause) { + this.name = name; + // make sure extend set to null if "" + if (extend == null || extend.length() == 0) { + this.extend = null; + if (precedence == null || precedence.length() == 0) { + // if (pointcutsAndAdvice.size() == 0) { + // throw new RuntimeException("Not allowed"); + // } + } + } else { + this.extend = extend; + } + this.precedence = precedence; + this.pointcuts = new ArrayList(); + this.declareAnnotations = new ArrayList(); + this.pointcutsAndAdvice = new ArrayList(); + this.deows = new ArrayList(); + this.perclause = perclause; + } + } + + public static class Pointcut { + public final String name; + public final String expression; + + public Pointcut(String name, String expression) { + this.name = name; + this.expression = expression; + } + } + + public enum AdviceKind { + Before, After, AfterReturning, AfterThrowing, Around; + } + + public enum DeclareAnnotationKind { + Method, Field, Type; + } + + public static class DeclareAnnotation { + public final DeclareAnnotationKind declareAnnotationKind; + public final String pattern; + public final String annotation; + + public DeclareAnnotation(DeclareAnnotationKind kind, String pattern, String annotation) { + this.declareAnnotationKind = kind; + this.pattern = pattern; + this.annotation = annotation; + } + } + + public static class PointcutAndAdvice { + public final AdviceKind adviceKind; + public final String pointcut; + public final String adviceClass; // com.foo.Bar + public final String adviceMethod; // foo(java.lang.String,org.aspectj.lang.JoinPoint) + + public PointcutAndAdvice(AdviceKind adviceKind, String pointcut, String adviceClass, String adviceMethod) { + this.adviceKind = adviceKind; + this.pointcut = pointcut; + this.adviceClass = adviceClass; + this.adviceMethod = adviceMethod; + } + } + + public static class DeclareErrorOrWarning { + public final boolean isError; + public final String pointcut; + public final String message; + + public DeclareErrorOrWarning(boolean isError, String pointcut, String message) { + this.isError = isError; + this.pointcut = pointcut; + this.message = message; + } + } + + public void appendWeaverOptions(String option) { + weaverOptions.append(option.trim()).append(' '); + } + + public void addScopedAspect(String name, String scopePattern) { + scopedAspects.put(name, scopePattern); + } + + public String getScopeForAspect(String name) { + return scopedAspects.get(name); + } + + public void setAspectRequires(String name, String requiredType) { + requiredTypesForAspects.put(name, requiredType); + } + + public String getAspectRequires(String name) { + return requiredTypesForAspects.get(name); + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/loadtime/definition/DocumentParser.java b/weaver/src/main/java/org/aspectj/weaver/loadtime/definition/DocumentParser.java new file mode 100644 index 000000000..7ff275fd7 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/loadtime/definition/DocumentParser.java @@ -0,0 +1,382 @@ +/******************************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alexandre Vasseur initial implementation + * Abraham Nevado - Lucierna simple caching strategy + *******************************************************************************/ +package org.aspectj.weaver.loadtime.definition; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Hashtable; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParserFactory; + +import org.aspectj.util.LangUtil; +import org.aspectj.weaver.loadtime.definition.Definition.AdviceKind; +import org.aspectj.weaver.loadtime.definition.Definition.DeclareAnnotation; +import org.aspectj.weaver.loadtime.definition.Definition.DeclareAnnotationKind; +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.DefaultHandler; +import org.xml.sax.helpers.XMLReaderFactory; + +/** + * + * @author Alexandre Vasseur + * @author A. Nevado + * @author Andy Clement + */ +public class DocumentParser extends DefaultHandler { + /** + * The current DTD public id. The matching dtd will be searched as a resource. + */ + private final static String DTD_PUBLIC_ID = "-//AspectJ//DTD 1.5.0//EN"; + + /** + * The DTD alias, for better user experience. + */ + private final static String DTD_PUBLIC_ID_ALIAS = "-//AspectJ//DTD//EN"; + + private final static String ASPECTJ_ELEMENT = "aspectj"; + private final static String WEAVER_ELEMENT = "weaver"; + private final static String DUMP_ELEMENT = "dump"; + private final static String DUMP_BEFOREANDAFTER_ATTRIBUTE = "beforeandafter"; + private final static String DUMP_PERCLASSLOADERDIR_ATTRIBUTE = "perclassloaderdumpdir"; + private final static String INCLUDE_ELEMENT = "include"; + private final static String EXCLUDE_ELEMENT = "exclude"; + private final static String OPTIONS_ATTRIBUTE = "options"; + private final static String ASPECTS_ELEMENT = "aspects"; + private final static String ASPECT_ELEMENT = "aspect"; + private final static String CONCRETE_ASPECT_ELEMENT = "concrete-aspect"; + private final static String NAME_ATTRIBUTE = "name"; + private final static String SCOPE_ATTRIBUTE = "scope"; + private final static String REQUIRES_ATTRIBUTE = "requires"; + private final static String EXTEND_ATTRIBUTE = "extends"; + private final static String PRECEDENCE_ATTRIBUTE = "precedence"; + private final static String PERCLAUSE_ATTRIBUTE = "perclause"; + private final static String POINTCUT_ELEMENT = "pointcut"; + private final static String BEFORE_ELEMENT = "before"; + private final static String AFTER_ELEMENT = "after"; + private final static String AFTER_RETURNING_ELEMENT = "after-returning"; + private final static String AFTER_THROWING_ELEMENT = "after-throwing"; + private final static String AROUND_ELEMENT = "around"; + private final static String WITHIN_ATTRIBUTE = "within"; + private final static String EXPRESSION_ATTRIBUTE = "expression"; + private final static String DECLARE_ANNOTATION_ELEMENT = "declare-annotation"; + + private final Definition definition; + + private boolean inAspectJ; + private boolean inWeaver; + private boolean inAspects; + + private Definition.ConcreteAspect activeConcreteAspectDefinition; + + private static Hashtable parsedFiles = new Hashtable(); + private static boolean CACHE; + private static final boolean LIGHTPARSER; + + static { + boolean value = false; + try { + value = System.getProperty("org.aspectj.weaver.loadtime.configuration.cache", "true").equalsIgnoreCase("true"); + } catch (Throwable t) { + t.printStackTrace(); + } + CACHE = value; + + value = false; + try { + value = System.getProperty("org.aspectj.weaver.loadtime.configuration.lightxmlparser", "false") + .equalsIgnoreCase("true"); + } catch (Throwable t) { + t.printStackTrace(); + } + LIGHTPARSER = value; + } + + private DocumentParser() { + definition = new Definition(); + } + + public static Definition parse(final URL url) throws Exception { + if (CACHE && parsedFiles.containsKey(url.toString())) { + return parsedFiles.get(url.toString()); + } + Definition def = null; + + if (LIGHTPARSER) { + def = SimpleAOPParser.parse(url); + } else { + def = saxParsing(url); + } + + if (CACHE && def.getAspectClassNames().size() > 0) { + parsedFiles.put(url.toString(), def); + } + + return def; + } + + private static Definition saxParsing(URL url) throws SAXException, ParserConfigurationException, IOException { + DocumentParser parser = new DocumentParser(); + + XMLReader xmlReader = getXMLReader(); + xmlReader.setContentHandler(parser); + xmlReader.setErrorHandler(parser); + + try { + xmlReader.setFeature("http://xml.org/sax/features/validation", false); + } catch (SAXException e) { + // fine, the parser don't do validation + } + try { + xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", false); + } catch (SAXException e) { + // fine, the parser don't do validation + } + try { + xmlReader.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + } catch (SAXException e) { + // fine, the parser don't do validation + } + + xmlReader.setEntityResolver(parser); + InputStream in = url.openStream(); + xmlReader.parse(new InputSource(in)); + return parser.definition; + } + + private static XMLReader getXMLReader() throws SAXException, ParserConfigurationException { + XMLReader xmlReader = null; + /* Try this first for Java 5 */ + try { + xmlReader = XMLReaderFactory.createXMLReader(); + } + + /* .. and ignore "System property ... not set" and then try this instead */ + catch (SAXException ex) { + xmlReader = SAXParserFactory.newInstance().newSAXParser().getXMLReader(); + } + return xmlReader; + } + + public InputSource resolveEntity(String publicId, String systemId) throws SAXException { + if (publicId.equals(DTD_PUBLIC_ID) || publicId.equals(DTD_PUBLIC_ID_ALIAS)) { + InputStream in = DocumentParser.class.getResourceAsStream("/aspectj_1_5_0.dtd"); + if (in == null) { + System.err.println("AspectJ - WARN - could not read DTD " + publicId); + return null; + } else { + return new InputSource(in); + } + } else { + System.err.println("AspectJ - WARN - unknown DTD " + publicId + " - consider using " + DTD_PUBLIC_ID); + return null; + } + } + + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + if (ASPECT_ELEMENT.equals(qName)) { + String name = attributes.getValue(NAME_ATTRIBUTE); + String scopePattern = replaceXmlAnd(attributes.getValue(SCOPE_ATTRIBUTE)); + String requiredType = attributes.getValue(REQUIRES_ATTRIBUTE); + if (!isNull(name)) { + definition.getAspectClassNames().add(name); + if (scopePattern != null) { + definition.addScopedAspect(name, scopePattern); + } + if (requiredType != null) { + definition.setAspectRequires(name, requiredType); + } + } + } else if (WEAVER_ELEMENT.equals(qName)) { + String options = attributes.getValue(OPTIONS_ATTRIBUTE); + if (!isNull(options)) { + definition.appendWeaverOptions(options); + } + inWeaver = true; + } else if (CONCRETE_ASPECT_ELEMENT.equals(qName)) { + String name = attributes.getValue(NAME_ATTRIBUTE); + String extend = attributes.getValue(EXTEND_ATTRIBUTE); + String precedence = attributes.getValue(PRECEDENCE_ATTRIBUTE); + String perclause = attributes.getValue(PERCLAUSE_ATTRIBUTE); + if (!isNull(name)) { + activeConcreteAspectDefinition = new Definition.ConcreteAspect(name, extend, precedence, perclause); + // if (isNull(precedence) && !isNull(extend)) {// if no precedence, then extends must be there + // m_lastConcreteAspect = new Definition.ConcreteAspect(name, extend); + // } else if (!isNull(precedence)) { + // // wether a pure precedence def, or an extendsANDprecedence def. + // m_lastConcreteAspect = new Definition.ConcreteAspect(name, extend, precedence, perclause); + // } + definition.getConcreteAspects().add(activeConcreteAspectDefinition); + } + } else if (POINTCUT_ELEMENT.equals(qName) && activeConcreteAspectDefinition != null) { + String name = attributes.getValue(NAME_ATTRIBUTE); + String expression = attributes.getValue(EXPRESSION_ATTRIBUTE); + if (!isNull(name) && !isNull(expression)) { + activeConcreteAspectDefinition.pointcuts.add(new Definition.Pointcut(name, replaceXmlAnd(expression))); + } + } else if (DECLARE_ANNOTATION_ELEMENT.equals(qName) && activeConcreteAspectDefinition!=null) { + String methodSig = attributes.getValue("method"); + String fieldSig = attributes.getValue("field"); + String typePat = attributes.getValue("type"); + String anno = attributes.getValue("annotation"); + if (isNull(anno)) { + throw new SAXException("Badly formed element, 'annotation' value is missing"); + } + if (isNull(methodSig) && isNull(fieldSig) && isNull(typePat)) { + throw new SAXException("Badly formed element, need one of 'method'/'field'/'type' specified"); + } + if (!isNull(methodSig)) { + // declare @method + activeConcreteAspectDefinition.declareAnnotations.add(new Definition.DeclareAnnotation(DeclareAnnotationKind.Method, + methodSig, anno)); + } else if (!isNull(fieldSig)) { + // declare @field + activeConcreteAspectDefinition.declareAnnotations.add(new Definition.DeclareAnnotation(DeclareAnnotationKind.Field, + fieldSig, anno)); + } else if (!isNull(typePat)) { + // declare @type + activeConcreteAspectDefinition.declareAnnotations.add(new Definition.DeclareAnnotation(DeclareAnnotationKind.Type, + typePat, anno)); + } + } else if (BEFORE_ELEMENT.equals(qName) && activeConcreteAspectDefinition != null) { + String pointcut = attributes.getValue(POINTCUT_ELEMENT); + String adviceClass = attributes.getValue("invokeClass"); + String adviceMethod = attributes.getValue("invokeMethod"); + if (!isNull(pointcut) && !isNull(adviceClass) && !isNull(adviceMethod)) { + activeConcreteAspectDefinition.pointcutsAndAdvice.add(new Definition.PointcutAndAdvice(AdviceKind.Before, + replaceXmlAnd(pointcut), adviceClass, adviceMethod)); + } else { + throw new SAXException("Badly formed element"); + } + } else if (AFTER_ELEMENT.equals(qName) && activeConcreteAspectDefinition != null) { + String pointcut = attributes.getValue(POINTCUT_ELEMENT); + String adviceClass = attributes.getValue("invokeClass"); + String adviceMethod = attributes.getValue("invokeMethod"); + if (!isNull(pointcut) && !isNull(adviceClass) && !isNull(adviceMethod)) { + activeConcreteAspectDefinition.pointcutsAndAdvice.add(new Definition.PointcutAndAdvice(AdviceKind.After, + replaceXmlAnd(pointcut), adviceClass, adviceMethod)); + } else { + throw new SAXException("Badly formed element"); + } + } else if (AROUND_ELEMENT.equals(qName) && activeConcreteAspectDefinition != null) { + String pointcut = attributes.getValue(POINTCUT_ELEMENT); + String adviceClass = attributes.getValue("invokeClass"); + String adviceMethod = attributes.getValue("invokeMethod"); + if (!isNull(pointcut) && !isNull(adviceClass) && !isNull(adviceMethod)) { + activeConcreteAspectDefinition.pointcutsAndAdvice.add(new Definition.PointcutAndAdvice(AdviceKind.Around, + replaceXmlAnd(pointcut), adviceClass, adviceMethod)); + } else { + throw new SAXException("Badly formed element"); + } + } else if (ASPECTJ_ELEMENT.equals(qName)) { + if (inAspectJ) { + throw new SAXException("Found nested element"); + } + inAspectJ = true; + } else if (ASPECTS_ELEMENT.equals(qName)) { + inAspects = true; + } else if (INCLUDE_ELEMENT.equals(qName) && inWeaver) { + String typePattern = getWithinAttribute(attributes); + if (!isNull(typePattern)) { + definition.getIncludePatterns().add(typePattern); + } + } else if (EXCLUDE_ELEMENT.equals(qName) && inWeaver) { + String typePattern = getWithinAttribute(attributes); + if (!isNull(typePattern)) { + definition.getExcludePatterns().add(typePattern); + } + } else if (DUMP_ELEMENT.equals(qName) && inWeaver) { + String typePattern = getWithinAttribute(attributes); + if (!isNull(typePattern)) { + definition.getDumpPatterns().add(typePattern); + } + String beforeAndAfter = attributes.getValue(DUMP_BEFOREANDAFTER_ATTRIBUTE); + if (isTrue(beforeAndAfter)) { + definition.setDumpBefore(true); + } + String perWeaverDumpDir = attributes.getValue(DUMP_PERCLASSLOADERDIR_ATTRIBUTE); + if (isTrue(perWeaverDumpDir)) { + definition.setCreateDumpDirPerClassloader(true); + } + } else if (EXCLUDE_ELEMENT.equals(qName) && inAspects) { + String typePattern = getWithinAttribute(attributes); + if (!isNull(typePattern)) { + definition.getAspectExcludePatterns().add(typePattern); + } + } else if (INCLUDE_ELEMENT.equals(qName) && inAspects) { + String typePattern = getWithinAttribute(attributes); + if (!isNull(typePattern)) { + definition.getAspectIncludePatterns().add(typePattern); + } + } else { + throw new SAXException("Unknown element while parsing element: " + qName); + } + super.startElement(uri, localName, qName, attributes); + } + + private String getWithinAttribute(Attributes attributes) { + return replaceXmlAnd(attributes.getValue(WITHIN_ATTRIBUTE)); + } + + public void endElement(String uri, String localName, String qName) throws SAXException { + if (CONCRETE_ASPECT_ELEMENT.equals(qName)) { + activeConcreteAspectDefinition = null; + } else if (ASPECTJ_ELEMENT.equals(qName)) { + inAspectJ = false; + } else if (WEAVER_ELEMENT.equals(qName)) { + inWeaver = false; + } else if (ASPECTS_ELEMENT.equals(qName)) { + inAspects = false; + } + super.endElement(uri, localName, qName); + } + + // TODO AV - define what we want for XML parser error - for now stderr + public void warning(SAXParseException e) throws SAXException { + super.warning(e); + } + + public void error(SAXParseException e) throws SAXException { + super.error(e); + } + + public void fatalError(SAXParseException e) throws SAXException { + super.fatalError(e); + } + + private static String replaceXmlAnd(String expression) { + // TODO AV do we need to handle "..)AND" or "AND(.." ? + return LangUtil.replace(expression, " AND ", " && "); + } + + private boolean isNull(String s) { + return (s == null || s.length() <= 0); + } + + private boolean isTrue(String s) { + return (s != null && s.equals("true")); + } + + /** + * Turn off caching + */ + public static void deactivateCaching() { + CACHE = false; + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/loadtime/definition/LightXMLParser.java b/weaver/src/main/java/org/aspectj/weaver/loadtime/definition/LightXMLParser.java new file mode 100644 index 000000000..09a9df96f --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/loadtime/definition/LightXMLParser.java @@ -0,0 +1,471 @@ +/******************************************************************************* + * Copyright (c) 2011 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Abraham Nevado - Lucierna initial implementation + *******************************************************************************/ +package org.aspectj.weaver.loadtime.definition; + +import java.io.Reader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +public class LightXMLParser { + + private final static char NULL_CHAR = '\0'; + private Map attributes; + private ArrayList children; + private String name; + private char pushedBackChar; + private Reader reader; + + private static Map entities = new HashMap(); + + static { + entities.put("amp", new char[] { '&' }); + entities.put("quot", new char[] { '"' }); + entities.put("apos", new char[] { '\'' }); + entities.put("lt", new char[] { '<' }); + entities.put("gt", new char[] { '>' }); + } + + public LightXMLParser() { + this.name = null; + this.attributes = new HashMap(); + this.children = new ArrayList(); + } + + public ArrayList getChildrens() { + return this.children; + } + + public String getName() { + return this.name; + } + + public void parseFromReader(Reader reader) throws Exception { + this.pushedBackChar = NULL_CHAR; + this.attributes = new HashMap(); + this.name = null; + this.children = new ArrayList(); + this.reader = reader; + + while (true) { + // Skips whiteSpaces, blanks, \r\n.. + char c = this.skipBlanks(); + + // All xml should start by 'z')) && ((c > 'Z') || (c < 'A')) && ((c > '9') || (c < '0')) && (c != '_') && (c != '-') + && (c != '.') && (c != ':')) { + this.pushBackChar(c); + return; + } + result.append(c); + } + } + + private void getString(StringBuffer string) throws Exception { + char delimiter = this.getNextChar(); + if ((delimiter != '\'') && (delimiter != '"')) { + throw new Exception("Parsing error. Expected ' or \" but got: " + delimiter); + + } + + while (true) { + char c = this.getNextChar(); + if (c == delimiter) { + return; + } else if (c == '&') { + this.mapEntity(string); + } else { + string.append(c); + } + } + } + + private void getPCData(StringBuffer data) throws Exception { + while (true) { + char c = this.getNextChar(); + if (c == '<') { + c = this.getNextChar(); + if (c == '!') { + this.checkCDATA(data); + } else { + this.pushBackChar(c); + return; + } + } else { + data.append(c); + } + } + } + + private boolean checkCDATA(StringBuffer buf) throws Exception { + char c = this.getNextChar(); + if (c != '[') { + this.pushBackChar(c); + this.skipCommentOrXmlTag(0); + return false; + } else if (!this.checkLiteral("CDATA[")) { + this.skipCommentOrXmlTag(1); // one [ has already been read + return false; + } else { + int delimiterCharsSkipped = 0; + while (delimiterCharsSkipped < 3) { + c = this.getNextChar(); + switch (c) { + case ']': + if (delimiterCharsSkipped < 2) { + delimiterCharsSkipped++; + } else { + buf.append(']'); + buf.append(']'); + delimiterCharsSkipped = 0; + } + break; + case '>': + if (delimiterCharsSkipped < 2) { + for (int i = 0; i < delimiterCharsSkipped; i++) { + buf.append(']'); + } + delimiterCharsSkipped = 0; + buf.append('>'); + } else { + delimiterCharsSkipped = 3; + } + break; + default: + for (int i = 0; i < delimiterCharsSkipped; i++) { + buf.append(']'); + } + buf.append(c); + delimiterCharsSkipped = 0; + } + } + return true; + } + } + + private void skipCommentOrXmlTag(int bracketLevel) throws Exception { + char delim = NULL_CHAR; + int level = 1; + char c; + if (bracketLevel == 0) { + c = this.getNextChar(); + if (c == '-') { + c = this.getNextChar(); + if (c == ']') { + bracketLevel--; + } else if (c == '[') { + bracketLevel++; + } else if (c == '-') { + this.skipComment(); + return; + } + } else if (c == '[') { + bracketLevel++; + } + } + while (level > 0) { + c = this.getNextChar(); + if (delim == NULL_CHAR) { + if ((c == '"') || (c == '\'')) { + delim = c; + } else if (bracketLevel <= 0) { + if (c == '<') { + level++; + } else if (c == '>') { + level--; + } + } + if (c == '[') { + bracketLevel++; + } else if (c == ']') { + bracketLevel--; + } + } else { + if (c == delim) { + delim = NULL_CHAR; + } + } + } + } + + private void parseNode(LightXMLParser elt) throws Exception { + // Now we are in a new node element. Get its name + StringBuffer buf = new StringBuffer(); + this.getNodeName(buf); + String name = buf.toString(); + elt.setName(name); + + char c = this.skipBlanks(); + while ((c != '>') && (c != '/')) { + // Get attributes + emptyBuf(buf); + this.pushBackChar(c); + this.getNodeName(buf); + String key = buf.toString(); + c = this.skipBlanks(); + if (c != '=') { + throw new Exception("Parsing error. Expected = but got: " + c); + } + // Go up to " character and push it back + this.pushBackChar(this.skipBlanks()); + + emptyBuf(buf); + this.getString(buf); + + elt.setAttribute(key, buf); + + // Skip blanks + c = this.skipBlanks(); + } + if (c == '/') { + c = this.getNextChar(); + if (c != '>') { + throw new Exception("Parsing error. Expected > but got: " + c); + } + return; + } + + // Now see if we got content, or CDATA, if content get it: it is free... + emptyBuf(buf); + c = this.getWhitespaces(buf); + if (c != '<') { + // It is PCDATA + this.pushBackChar(c); + this.getPCData(buf); + } else { + // It is content: get it, or CDATA. + while (true) { + c = this.getNextChar(); + if (c == '!') { + if (this.checkCDATA(buf)) { + this.getPCData(buf); + break; + } else { + c = this.getWhitespaces(buf); + if (c != '<') { + this.pushBackChar(c); + this.getPCData(buf); + break; + } + } + } else { + if (c != '/') { + emptyBuf(buf); + } + if (c == '/') { + this.pushBackChar(c); + } + break; + } + } + } + if (buf.length() == 0) { + // It is a comment + while (c != '/') { + if (c == '!') { + for (int i = 0; i < 2; i++) { + c = this.getNextChar(); + if (c != '-') { + throw new Exception("Parsing error. Expected element or comment"); + } + } + this.skipComment(); + } else { + // it is a new node + this.pushBackChar(c); + LightXMLParser child = this.createAnotherElement(); + this.parseNode(child); + elt.addChild(child); + } + c = this.skipBlanks(); + if (c != '<') { + throw new Exception("Parsing error. Expected <, but got: " + c); + } + c = this.getNextChar(); + } + this.pushBackChar(c); + } // Here content could be grabbed + + c = this.getNextChar(); + if (c != '/') { + throw new Exception("Parsing error. Expected /, but got: " + c); + } + this.pushBackChar(this.skipBlanks()); + if (!this.checkLiteral(name)) { + throw new Exception("Parsing error. Expected " + name); + } + if (this.skipBlanks() != '>') { + throw new Exception("Parsing error. Expected >, but got: " + c); + } + } + + private void skipComment() throws Exception { + int dashes = 2; + while (dashes > 0) { + char ch = this.getNextChar(); + if (ch == '-') { + dashes -= 1; + } else { + dashes = 2; + } + } + + char nextChar = this.getNextChar(); + if (nextChar != '>') { + throw new Exception("Parsing error. Expected > but got: " + nextChar); + } + } + + private boolean checkLiteral(String literal) throws Exception { + int length = literal.length(); + for (int i = 0; i < length; i++) { + if (this.getNextChar() != literal.charAt(i)) { + return false; + } + } + return true; + } + + private char getNextChar() throws Exception { + if (this.pushedBackChar != NULL_CHAR) { + char c = this.pushedBackChar; + this.pushedBackChar = NULL_CHAR; + return c; + } else { + int i = this.reader.read(); + if (i < 0) { + throw new Exception("Parsing error. Unexpected end of data"); + } else { + return (char) i; + } + } + } + + private void mapEntity(StringBuffer buf) throws Exception { + char c = this.NULL_CHAR; + StringBuffer keyBuf = new StringBuffer(); + while (true) { + c = this.getNextChar(); + if (c == ';') { + break; + } + keyBuf.append(c); + } + String key = keyBuf.toString(); + if (key.charAt(0) == '#') { + try { + if (key.charAt(1) == 'x') { + c = (char) Integer.parseInt(key.substring(2), 16); + } else { + c = (char) Integer.parseInt(key.substring(1), 10); + } + } catch (NumberFormatException e) { + throw new Exception("Unknown entity: " + key); + } + buf.append(c); + } else { + char[] value = (char[]) entities.get(key); + if (value == null) { + throw new Exception("Unknown entity: " + key); + } + buf.append(value); + } + } + + private void pushBackChar(char c) { + this.pushedBackChar = c; + } + + private void addChild(LightXMLParser child) { + this.children.add(child); + } + + private void setAttribute(String name, Object value) { + this.attributes.put(name, value.toString()); + } + + public Map getAttributes() { + return this.attributes; + } + + private LightXMLParser createAnotherElement() { + return new LightXMLParser(); + } + + private void setName(String name) { + this.name = name; + } + + private void emptyBuf(StringBuffer buf) { + buf.setLength(0); + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/loadtime/definition/SimpleAOPParser.java b/weaver/src/main/java/org/aspectj/weaver/loadtime/definition/SimpleAOPParser.java new file mode 100644 index 000000000..bcd6ddcd0 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/loadtime/definition/SimpleAOPParser.java @@ -0,0 +1,265 @@ +/******************************************************************************* + * Copyright (c) 2011 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Abraham Nevado - Lucierna initial implementation + * Just a slight variation of current DocumentParser.java from Alexandre Vasseur. + *******************************************************************************/ +package org.aspectj.weaver.loadtime.definition; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.ArrayList; +import java.util.Map; +import java.util.Set; + +import org.aspectj.util.LangUtil; +import org.aspectj.weaver.loadtime.definition.Definition.AdviceKind; +import org.aspectj.weaver.loadtime.definition.Definition.DeclareAnnotationKind; +import org.xml.sax.SAXException; + +/** + * This class has been created to avoid deadlocks when instrumenting SAXParser. + * So it is used as a wrapper for the ligthweigh XML parser LightXMLParser. + * + * @author A. Nevado + */ +public class SimpleAOPParser { + + private final static String ASPECTJ_ELEMENT = "aspectj"; + private final static String WEAVER_ELEMENT = "weaver"; + private final static String DUMP_ELEMENT = "dump"; + private final static String DUMP_BEFOREANDAFTER_ATTRIBUTE = "beforeandafter"; + private final static String DUMP_PERCLASSLOADERDIR_ATTRIBUTE = "perclassloaderdumpdir"; + private final static String INCLUDE_ELEMENT = "include"; + private final static String EXCLUDE_ELEMENT = "exclude"; + private final static String OPTIONS_ATTRIBUTE = "options"; + private final static String ASPECTS_ELEMENT = "aspects"; + private final static String ASPECT_ELEMENT = "aspect"; + private final static String CONCRETE_ASPECT_ELEMENT = "concrete-aspect"; + private final static String NAME_ATTRIBUTE = "name"; + private final static String SCOPE_ATTRIBUTE = "scope"; + private final static String REQUIRES_ATTRIBUTE = "requires"; + private final static String EXTEND_ATTRIBUTE = "extends"; + private final static String PRECEDENCE_ATTRIBUTE = "precedence"; + private final static String PERCLAUSE_ATTRIBUTE = "perclause"; + private final static String POINTCUT_ELEMENT = "pointcut"; + private final static String WITHIN_ATTRIBUTE = "within"; + private final static String EXPRESSION_ATTRIBUTE = "expression"; + private static final String DECLARE_ANNOTATION = "declare-annotation"; + private static final String ANNONATION_TAG = "annotation"; + private static final String ANNO_KIND_TYPE = "type"; + private static final String ANNO_KIND_METHOD = "method"; + private static final String ANNO_KIND_FIELD = "field"; + private final static String BEFORE_ELEMENT = "before"; + private final static String AFTER_ELEMENT = "after"; + private final static String AROUND_ELEMENT = "around"; + private final Definition m_definition; + private boolean m_inAspectJ; + private boolean m_inWeaver; + private boolean m_inAspects; + + private Definition.ConcreteAspect m_lastConcreteAspect; + + private SimpleAOPParser() { + m_definition = new Definition(); + } + + public static Definition parse(final URL url) throws Exception { + // FileReader freader = new FileReader("/tmp/aop.xml"); + InputStream in = url.openStream(); + LightXMLParser xml = new LightXMLParser(); + xml.parseFromReader(new InputStreamReader(in)); + SimpleAOPParser sap = new SimpleAOPParser(); + traverse(sap, xml); + return sap.m_definition; + } + + private void startElement(String qName, Map attrMap) throws Exception { + if (ASPECT_ELEMENT.equals(qName)) { + String name = (String) attrMap.get(NAME_ATTRIBUTE); + String scopePattern = replaceXmlAnd((String) attrMap + .get(SCOPE_ATTRIBUTE)); + String requiredType = (String) attrMap.get(REQUIRES_ATTRIBUTE); + if (!isNull(name)) { + m_definition.getAspectClassNames().add(name); + if (scopePattern != null) { + m_definition.addScopedAspect(name, scopePattern); + } + if (requiredType != null) { + m_definition.setAspectRequires(name, requiredType); + } + } + } else if (WEAVER_ELEMENT.equals(qName)) { + String options = (String) attrMap.get(OPTIONS_ATTRIBUTE); + if (!isNull(options)) { + m_definition.appendWeaverOptions(options); + } + m_inWeaver = true; + } else if (CONCRETE_ASPECT_ELEMENT.equals(qName)) { + String name = (String) attrMap.get(NAME_ATTRIBUTE); + String extend = (String) attrMap.get(EXTEND_ATTRIBUTE); + String precedence = (String) attrMap.get(PRECEDENCE_ATTRIBUTE); + String perclause = (String) attrMap.get(PERCLAUSE_ATTRIBUTE); + if (!isNull(name)) { + m_lastConcreteAspect = new Definition.ConcreteAspect(name, + extend, precedence, perclause); + m_definition.getConcreteAspects().add(m_lastConcreteAspect); + } + } else if (POINTCUT_ELEMENT.equals(qName) + && m_lastConcreteAspect != null) { + String name = (String) attrMap.get(NAME_ATTRIBUTE); + String expression = (String) attrMap.get(EXPRESSION_ATTRIBUTE); + if (!isNull(name) && !isNull(expression)) { + m_lastConcreteAspect.pointcuts.add(new Definition.Pointcut( + name, replaceXmlAnd(expression))); + } + } else if (ASPECTJ_ELEMENT.equals(qName)) { + if (m_inAspectJ) { + throw new Exception("Found nested element"); + } + m_inAspectJ = true; + } else if (ASPECTS_ELEMENT.equals(qName)) { + m_inAspects = true; + } else if (INCLUDE_ELEMENT.equals(qName) && m_inWeaver) { + String typePattern = getWithinAttribute(attrMap); + if (!isNull(typePattern)) { + m_definition.getIncludePatterns().add(typePattern); + } + } else if (EXCLUDE_ELEMENT.equals(qName) && m_inWeaver) { + String typePattern = getWithinAttribute(attrMap); + if (!isNull(typePattern)) { + m_definition.getExcludePatterns().add(typePattern); + } + } else if (DUMP_ELEMENT.equals(qName) && m_inWeaver) { + String typePattern = getWithinAttribute(attrMap); + if (!isNull(typePattern)) { + m_definition.getDumpPatterns().add(typePattern); + } + String beforeAndAfter = (String) attrMap + .get(DUMP_BEFOREANDAFTER_ATTRIBUTE); + if (isTrue(beforeAndAfter)) { + m_definition.setDumpBefore(true); + } + String perWeaverDumpDir = (String) attrMap + .get(DUMP_PERCLASSLOADERDIR_ATTRIBUTE); + if (isTrue(perWeaverDumpDir)) { + m_definition.setCreateDumpDirPerClassloader(true); + } + } else if (EXCLUDE_ELEMENT.equals(qName) && m_inAspects) { + String typePattern = getWithinAttribute(attrMap); + if (!isNull(typePattern)) { + m_definition.getAspectExcludePatterns().add(typePattern); + } + } else if (INCLUDE_ELEMENT.equals(qName) && m_inAspects) { + String typePattern = getWithinAttribute(attrMap); + if (!isNull(typePattern)) { + m_definition.getAspectIncludePatterns().add(typePattern); + } + }else if (DECLARE_ANNOTATION.equals(qName) && m_inAspects) { + String anno = (String) attrMap.get(ANNONATION_TAG); + if (!isNull(anno)){ + String pattern = (String) attrMap.get(ANNO_KIND_FIELD); + if (pattern != null){ + m_lastConcreteAspect.declareAnnotations.add(new Definition.DeclareAnnotation( + DeclareAnnotationKind.Field, pattern, anno)); + } + else{ + pattern = (String) attrMap.get(ANNO_KIND_METHOD); + if (pattern != null){ + m_lastConcreteAspect.declareAnnotations.add(new Definition.DeclareAnnotation( + DeclareAnnotationKind.Method, pattern, anno)); + } + else{ + pattern = (String) attrMap.get(ANNO_KIND_TYPE); + if (pattern != null){ + m_lastConcreteAspect.declareAnnotations.add(new Definition.DeclareAnnotation( + DeclareAnnotationKind.Type, pattern, anno)); + } + } + } + + } + } + else if (BEFORE_ELEMENT.equals(qName) && m_inAspects ) { + String pointcut = (String) attrMap.get(POINTCUT_ELEMENT); + String adviceClass = (String) attrMap.get("invokeClass"); + String adviceMethod = (String) attrMap.get("invokeMethod"); + if (!isNull(pointcut) && !isNull(adviceClass) && !isNull(adviceMethod)) { + m_lastConcreteAspect.pointcutsAndAdvice.add(new Definition.PointcutAndAdvice(AdviceKind.Before, + replaceXmlAnd(pointcut), adviceClass, adviceMethod)); + } else { + throw new SAXException("Badly formed element"); + } + } else if (AFTER_ELEMENT.equals(qName) && m_inAspects) { + String pointcut = (String) attrMap.get(POINTCUT_ELEMENT); + String adviceClass = (String) attrMap.get("invokeClass"); + String adviceMethod = (String) attrMap.get("invokeMethod"); + if (!isNull(pointcut) && !isNull(adviceClass) && !isNull(adviceMethod)) { + m_lastConcreteAspect.pointcutsAndAdvice.add(new Definition.PointcutAndAdvice(AdviceKind.After, + replaceXmlAnd(pointcut), adviceClass, adviceMethod)); + } else { + throw new SAXException("Badly formed element"); + } + } else if (AROUND_ELEMENT.equals(qName) && m_inAspects) { + String pointcut = (String) attrMap.get(POINTCUT_ELEMENT); + String adviceClass = (String) attrMap.get("invokeClass"); + String adviceMethod = (String) attrMap.get("invokeMethod"); + if (!isNull(pointcut) && !isNull(adviceClass) && !isNull(adviceMethod)) { + m_lastConcreteAspect.pointcutsAndAdvice.add(new Definition.PointcutAndAdvice(AdviceKind.Around, + replaceXmlAnd(pointcut), adviceClass, adviceMethod)); + } + } + else { + throw new Exception( + "Unknown element while parsing element: " + qName); + } + } + + private void endElement(String qName) throws Exception { + if (CONCRETE_ASPECT_ELEMENT.equals(qName)) { + m_lastConcreteAspect = null; + } else if (ASPECTJ_ELEMENT.equals(qName)) { + m_inAspectJ = false; + } else if (WEAVER_ELEMENT.equals(qName)) { + m_inWeaver = false; + } else if (ASPECTS_ELEMENT.equals(qName)) { + m_inAspects = false; + } + } + + private String getWithinAttribute(Map attributes) { + return replaceXmlAnd((String) attributes.get(WITHIN_ATTRIBUTE)); + } + + private static String replaceXmlAnd(String expression) { + // TODO AV do we need to handle "..)AND" or "AND(.." ? + return LangUtil.replace(expression, " AND ", " && "); + } + + private boolean isNull(String s) { + return (s == null || s.length() <= 0); + } + + private boolean isTrue(String s) { + return (s != null && s.equals("true")); + } + + private static void traverse(SimpleAOPParser sap, LightXMLParser xml) + throws Exception { + sap.startElement(xml.getName(), xml.getAttributes()); + ArrayList childrens = xml.getChildrens(); + for (int i = 0; i < childrens.size(); i++) { + LightXMLParser child = (LightXMLParser) childrens.get(i); + traverse(sap, child); + } + sap.endElement(xml.getName()); + + } +} diff --git a/weaver/src/main/java/org/aspectj/weaver/ltw/LTWWorld.java b/weaver/src/main/java/org/aspectj/weaver/ltw/LTWWorld.java new file mode 100644 index 000000000..de5a4d854 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/ltw/LTWWorld.java @@ -0,0 +1,294 @@ +/* ******************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ron Bodkin Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.ltw; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.bridge.IMessageHandler; +import org.aspectj.util.LangUtil; +import org.aspectj.weaver.Dump.IVisitor; +import org.aspectj.weaver.ICrossReferenceHandler; +import org.aspectj.weaver.ReferenceType; +import org.aspectj.weaver.ReferenceTypeDelegate; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.bcel.BcelWorld; +import org.aspectj.weaver.loadtime.IWeavingContext; +import org.aspectj.weaver.reflect.AnnotationFinder; +import org.aspectj.weaver.reflect.IReflectionWorld; +import org.aspectj.weaver.reflect.ReflectionBasedReferenceTypeDelegateFactory; +import org.aspectj.weaver.reflect.ReflectionWorld; + +/** + * @author adrian + * @author Ron Bodkin + * + * For use in LT weaving + * + * Backed by both a BcelWorld and a ReflectionWorld + * + * Needs a callback when a woven class is defined This is the trigger for us to ditch the class from Bcel and cache it in + * the reflective world instead. + * + * Create by passing in a classloader, message handler + */ +public class LTWWorld extends BcelWorld implements IReflectionWorld { + + private AnnotationFinder annotationFinder; + private IWeavingContext weavingContext; + private String classLoaderString; + + private String classLoaderParentString; + + protected final static Class concurrentMapClass; + + private static final boolean ShareBootstrapTypes = false; + protected static Map/* > */bootstrapTypes; + + static { + if (ShareBootstrapTypes) { + concurrentMapClass = makeConcurrentMapClass(); + bootstrapTypes = makeConcurrentMap(); + } else { + concurrentMapClass = null; + } + } + + /** + * Build a World from a ClassLoader, for LTW support + */ + public LTWWorld(ClassLoader loader, IWeavingContext weavingContext, IMessageHandler handler, ICrossReferenceHandler xrefHandler) { + super(loader, handler, xrefHandler); + this.weavingContext = weavingContext; + try { + classLoaderString = loader.toString(); + } catch (Throwable t) { + // Possibly some state in the loader isn't initialized but is used in the toString() + classLoaderString = loader.getClass().getName()+":"+Integer.toString(System.identityHashCode(loader)); + } + classLoaderParentString = (loader.getParent() == null ? "" : loader.getParent().toString()); + setBehaveInJava5Way(LangUtil.is15VMOrGreater()); + annotationFinder = ReflectionWorld.makeAnnotationFinderIfAny(loader, this); + } + + public ClassLoader getClassLoader() { + return weavingContext.getClassLoader(); + } + + // TEST + // this is probably easier: just mark anything loaded while loading aspects as not + // expendible... it also fixes a possible bug whereby non-rewoven aspects are deemed expendible + // + // protected boolean isExpendable(ResolvedType type) { + // return ((type != null) && !loadingAspects && !type.isAspect() && (!type + // .isPrimitiveType())); + // } + + /** + * @Override + */ + @Override + protected ReferenceTypeDelegate resolveDelegate(ReferenceType ty) { + + // use reflection delegates for all bootstrap types + ReferenceTypeDelegate bootstrapLoaderDelegate = resolveIfBootstrapDelegate(ty); + if (bootstrapLoaderDelegate != null) { + return bootstrapLoaderDelegate; + } + + return super.resolveDelegate(ty); + } + + protected ReferenceTypeDelegate resolveIfBootstrapDelegate(ReferenceType ty) { + // first check for anything available in the bootstrap loader: these types are just defined from that without allowing + // nondelegation + // if (!ShareBootstrapTypes) return null; + // String name = ty.getName(); + // Reference bootRef = (Reference) bootstrapTypes.get(name); + // if (bootRef != null) { + // ReferenceTypeDelegate rtd = (ReferenceTypeDelegate) bootRef.get(); + // if (rtd != null) { + // return rtd; + // } + // } + // + // char fc = name.charAt(0); + // if (fc == 'j' || fc == 'c' || fc == 'o' || fc == 's') { // cheaper than imminent string startsWith tests + // if (name.startsWith("java") || name.startsWith("com.sun.") || name.startsWith("org.w3c") || + // name.startsWith("sun.") || name.startsWith("org.omg")) { + // ReferenceTypeDelegate bootstrapLoaderDelegate = resolveReflectionTypeDelegate(ty, null); + // if (bootstrapLoaderDelegate != null) { + // // it's always fine to load these bytes: there's no weaving into them + // // and since the class isn't initialized, all we are doing at this point is loading the bytes + // // processedRefTypes.put(ty, this); // has no effect - and probably too aggressive if we did store + // // these in the type map + // + // // should we share these, like we do the BCEL delegates? + // bootstrapTypes.put(ty.getName(), new WeakReference(bootstrapLoaderDelegate)); + // } + // return bootstrapLoaderDelegate; + // } + // } + return null; + } + + /** + * Helper method to resolve the delegate from the reflection delegate factory. + */ + private ReferenceTypeDelegate resolveReflectionTypeDelegate(ReferenceType ty, ClassLoader resolutionLoader) { + ReferenceTypeDelegate res = ReflectionBasedReferenceTypeDelegateFactory.createDelegate(ty, this, resolutionLoader); + return res; + } + + /** + * Remove this class from the typeMap. Call back to be made from a publishing class loader The class loader should, ideally, + * make this call on each not yet working + * + * @param clazz + */ + public void loadedClass(Class clazz) { + } + + private static final long serialVersionUID = 1; + + public AnnotationFinder getAnnotationFinder() { + return this.annotationFinder; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.reflect.IReflectionWorld#resolve(java.lang.Class) + */ + public ResolvedType resolve(Class aClass) { + return ReflectionWorld.resolve(this, aClass); + } + + private static Map makeConcurrentMap() { + if (concurrentMapClass != null) { + try { + return (Map) concurrentMapClass.newInstance(); + } catch (InstantiationException ie) { + } catch (IllegalAccessException iae) { + } + // fall through if exceptions + } + return Collections.synchronizedMap(new HashMap()); + } + + private static Class makeConcurrentMapClass() { + String betterChoices[] = { "java.util.concurrent.ConcurrentHashMap", + "edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap", + "EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap" }; + for (int i = 0; i < betterChoices.length; i++) { + try { + return Class.forName(betterChoices[i]); + } catch (ClassNotFoundException cnfe) { + // try the next one + } catch (SecurityException se) { + // you get one of these if you dare to try to load an undefined class in a + // package starting with java like java.util.concurrent + } + } + return null; + } + + @Override + public boolean isRunMinimalMemory() { + if (isRunMinimalMemorySet()) { + return super.isRunMinimalMemory(); + } + return false; + } + + // One type is completed at a time, if multiple need doing then they + // are queued up + private boolean typeCompletionInProgress = false; + private List/* ResolvedType */typesForCompletion = new ArrayList(); + + @Override + protected void completeBinaryType(ResolvedType ret) { + if (isLocallyDefined(ret.getName())) { + if (typeCompletionInProgress) { + typesForCompletion.add(ret); + } else { + try { + typeCompletionInProgress = true; + completeHierarchyForType(ret); + } finally { + typeCompletionInProgress = false; + } + while (typesForCompletion.size() != 0) { + ResolvedType rt = (ResolvedType) typesForCompletion.get(0); + completeHierarchyForType(rt); + typesForCompletion.remove(0); + } + } + } else { + if (!ret.needsModifiableDelegate()) { + ret = completeNonLocalType(ret); + } + } + } + + private void completeHierarchyForType(ResolvedType ret) { + getLint().typeNotExposedToWeaver.setSuppressed(true); + weaveInterTypeDeclarations(ret); + getLint().typeNotExposedToWeaver.setSuppressed(false); + } + + protected boolean needsCompletion() { + return true; + } + + @Override + public boolean isLocallyDefined(String classname) { + return weavingContext.isLocallyDefined(classname); + } + + protected ResolvedType completeNonLocalType(ResolvedType ret) { + if (ret.isMissing()) { + return ret; // who knows ?!? + } + ResolvedType toResolve = ret; + if (ret.isParameterizedType() || ret.isGenericType()) { + toResolve = toResolve.getGenericType(); + } + ReferenceTypeDelegate rtd = resolveReflectionTypeDelegate((ReferenceType) toResolve, getClassLoader()); + ((ReferenceType) ret).setDelegate(rtd); + return ret; + } + + @Override + public void storeClass(JavaClass clazz) { + ensureRepositorySetup(); + delegate.storeClass(clazz); + } + + @Override + public void accept(IVisitor visitor) { + visitor.visitObject("Class loader:"); + visitor.visitObject(classLoaderString); + visitor.visitObject("Class loader parent:"); + visitor.visitObject(classLoaderParentString); + super.accept(visitor); + } + + public boolean isLoadtimeWeaving() { + return true; + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/model/AsmRelationshipProvider.java b/weaver/src/main/java/org/aspectj/weaver/model/AsmRelationshipProvider.java new file mode 100644 index 000000000..5801397a8 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/model/AsmRelationshipProvider.java @@ -0,0 +1,1133 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.model; + +import java.io.File; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.aspectj.asm.AsmManager; +import org.aspectj.asm.IHierarchy; +import org.aspectj.asm.IProgramElement; +import org.aspectj.asm.IRelationship; +import org.aspectj.asm.IRelationshipMap; +import org.aspectj.asm.internal.HandleProviderDelimiter; +import org.aspectj.asm.internal.ProgramElement; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.SourceLocation; +import org.aspectj.weaver.Advice; +import org.aspectj.weaver.AdviceKind; +import org.aspectj.weaver.Checker; +import org.aspectj.weaver.Lint; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.NewParentTypeMunger; +import org.aspectj.weaver.ReferenceType; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedPointcutDefinition; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.ResolvedTypeMunger; +import org.aspectj.weaver.ResolvedTypeMunger.Kind; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.ShadowMunger; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.World; +import org.aspectj.weaver.bcel.BcelShadow; +import org.aspectj.weaver.bcel.BcelTypeMunger; +import org.aspectj.weaver.patterns.DeclareErrorOrWarning; +import org.aspectj.weaver.patterns.DeclareParents; +import org.aspectj.weaver.patterns.Pointcut; +import org.aspectj.weaver.patterns.TypePatternList; + +public class AsmRelationshipProvider { + + public static final String ADVISES = "advises"; + public static final String ADVISED_BY = "advised by"; + public static final String DECLARES_ON = "declares on"; + public static final String DECLAREDY_BY = "declared by"; + public static final String SOFTENS = "softens"; + public static final String SOFTENED_BY = "softened by"; + public static final String MATCHED_BY = "matched by"; + public static final String MATCHES_DECLARE = "matches declare"; + public static final String INTER_TYPE_DECLARES = "declared on"; + public static final String INTER_TYPE_DECLARED_BY = "aspect declarations"; + + public static final String ANNOTATES = "annotates"; + public static final String ANNOTATED_BY = "annotated by"; + + // public static final String REMOVES_ANNOTATION = "removes annotation"; + // public static final String ANNOTATION_REMOVED_BY = "annotated removed by"; + + /** + * Add a relationship for a declare error or declare warning + */ + public static void addDeclareErrorOrWarningRelationship(AsmManager model, Shadow affectedShadow, Checker deow) { + if (model == null) { + return; + } + if (affectedShadow.getSourceLocation() == null || deow.getSourceLocation() == null) { + return; + } + + if (World.createInjarHierarchy) { + createHierarchyForBinaryAspect(model, deow); + } + + IProgramElement targetNode = getNode(model, affectedShadow); + if (targetNode == null) { + return; + } + String targetHandle = targetNode.getHandleIdentifier(); + if (targetHandle == null) { + return; + } + + IProgramElement sourceNode = model.getHierarchy().findElementForSourceLine(deow.getSourceLocation()); + String sourceHandle = sourceNode.getHandleIdentifier(); + if (sourceHandle == null) { + return; + } + + IRelationshipMap relmap = model.getRelationshipMap(); + IRelationship foreward = relmap.get(sourceHandle, IRelationship.Kind.DECLARE, MATCHED_BY, false, true); + foreward.addTarget(targetHandle); + + IRelationship back = relmap.get(targetHandle, IRelationship.Kind.DECLARE, MATCHES_DECLARE, false, true); + if (back != null && back.getTargets() != null) { + back.addTarget(sourceHandle); + } + if (sourceNode.getSourceLocation() != null) { + model.addAspectInEffectThisBuild(sourceNode.getSourceLocation().getSourceFile()); + } + } + + private static boolean isMixinRelated(ResolvedTypeMunger typeTransformer) { + Kind kind = typeTransformer.getKind(); + return kind == ResolvedTypeMunger.MethodDelegate2 || kind == ResolvedTypeMunger.FieldHost + || (kind == ResolvedTypeMunger.Parent && ((NewParentTypeMunger) typeTransformer).isMixin()); + } + + /** + * Add a relationship for a type transformation (declare parents, intertype method declaration, declare annotation on type). + */ + public static void addRelationship(AsmManager model, ResolvedType onType, ResolvedTypeMunger typeTransformer, + ResolvedType originatingAspect) { + if (model == null) { + return; + } + + if (World.createInjarHierarchy && isBinaryAspect(originatingAspect)) { + createHierarchy(model, typeTransformer, originatingAspect); + } + + if (originatingAspect.getSourceLocation() != null) { + String sourceHandle = ""; + IProgramElement sourceNode = null; + if (typeTransformer.getSourceLocation() != null && typeTransformer.getSourceLocation().getOffset() != -1 + && !isMixinRelated(typeTransformer)) { + sourceNode = model.getHierarchy().findElementForType(originatingAspect.getPackageName(), + originatingAspect.getClassName()); + IProgramElement closer = model.getHierarchy().findCloserMatchForLineNumber(sourceNode, + typeTransformer.getSourceLocation().getLine()); + if (closer != null) { + sourceNode = closer; + } + if (sourceNode == null) { + // This can be caused by the aspect defining the type munger actually being on the classpath and not the + // inpath or aspectpath. Rather than NPE at the next line, let's have another go at faulting it in. + // This inner loop is a small duplicate of the outer loop that attempts to find something closer than + // the type declaration + if (World.createInjarHierarchy) { + createHierarchy(model, typeTransformer, originatingAspect); + if (typeTransformer.getSourceLocation() != null && typeTransformer.getSourceLocation().getOffset() != -1 + && !isMixinRelated(typeTransformer)) { + sourceNode = model.getHierarchy().findElementForType(originatingAspect.getPackageName(), + originatingAspect.getClassName()); + IProgramElement closer2 = model.getHierarchy().findCloserMatchForLineNumber(sourceNode, + typeTransformer.getSourceLocation().getLine()); + if (closer2 != null) { + sourceNode = closer2; + } + } else { + sourceNode = model.getHierarchy().findElementForType(originatingAspect.getPackageName(), + originatingAspect.getClassName()); + } + } + } + sourceHandle = sourceNode.getHandleIdentifier(); + } else { + sourceNode = model.getHierarchy().findElementForType(originatingAspect.getPackageName(), + originatingAspect.getClassName()); + // sourceNode = + // asm.getHierarchy().findElementForSourceLine(originatingAspect + // .getSourceLocation()); + sourceHandle = sourceNode.getHandleIdentifier(); + } + // sourceNode = + // asm.getHierarchy().findElementForType(originatingAspect + // .getPackageName(), + // originatingAspect.getClassName()); + // // sourceNode = + // asm.getHierarchy().findElementForSourceLine(munger + // .getSourceLocation()); + // sourceHandle = + // asm.getHandleProvider().createHandleIdentifier(sourceNode); + if (sourceHandle == null) { + return; + } + String targetHandle = findOrFakeUpNode(model, onType); + if (targetHandle == null) { + return; + } + IRelationshipMap mapper = model.getRelationshipMap(); + IRelationship foreward = mapper.get(sourceHandle, IRelationship.Kind.DECLARE_INTER_TYPE, INTER_TYPE_DECLARES, false, + true); + foreward.addTarget(targetHandle); + + IRelationship back = mapper.get(targetHandle, IRelationship.Kind.DECLARE_INTER_TYPE, INTER_TYPE_DECLARED_BY, false, + true); + back.addTarget(sourceHandle); + if (sourceNode != null && sourceNode.getSourceLocation() != null) { + // May have been a bug in the compiled aspect - so it didn't get put in the model + model.addAspectInEffectThisBuild(sourceNode.getSourceLocation().getSourceFile()); + } + } + } + + private static String findOrFakeUpNode(AsmManager model, ResolvedType onType) { + IHierarchy hierarchy = model.getHierarchy(); + ISourceLocation sourceLocation = onType.getSourceLocation(); + String canonicalFilePath = model.getCanonicalFilePath(sourceLocation.getSourceFile()); + int lineNumber = sourceLocation.getLine(); + // Find the relevant source file node first + IProgramElement node = hierarchy.findNodeForSourceFile(hierarchy.getRoot(), canonicalFilePath); + if (node == null) { + // Does not exist in the model - probably an inpath + String bpath = onType.getBinaryPath(); + if (bpath == null) { + return model.getHandleProvider().createHandleIdentifier(createFileStructureNode(model, canonicalFilePath)); + } else { + IProgramElement programElement = model.getHierarchy().getRoot(); + // =Foo/, 0 && ((ch = bpath.charAt(startPosition)) != '/' && ch != '\\' && ch != '!')) { + startPosition--; + } + String classFile = bpath.substring(startPosition + 1, dotClassPosition + 6); + phantomHandle.append(HandleProviderDelimiter.CLASSFILE.getDelimiter()).append(classFile); + } + + // [G + phantomHandle.append(HandleProviderDelimiter.TYPE.getDelimiter()).append(onType.getClassName()); + + return phantomHandle.toString(); + } + } else { + // Check if there is a more accurate child node of that source file node: + IProgramElement closernode = hierarchy.findCloserMatchForLineNumber(node, lineNumber); + if (closernode == null) { + return node.getHandleIdentifier(); + } else { + return closernode.getHandleIdentifier(); + } + } + + } + + public static IProgramElement createFileStructureNode(AsmManager asm, String sourceFilePath) { + // SourceFilePath might have originated on windows on linux... + int lastSlash = sourceFilePath.lastIndexOf('\\'); + if (lastSlash == -1) { + lastSlash = sourceFilePath.lastIndexOf('/'); + } + // '!' is used like in URLs "c:/blahblah/X.jar!a/b.class" + int i = sourceFilePath.lastIndexOf('!'); + int j = sourceFilePath.indexOf(".class"); + if (i > lastSlash && i != -1 && j != -1) { + // we are a binary aspect in the default package + lastSlash = i; + } + String fileName = sourceFilePath.substring(lastSlash + 1); + IProgramElement fileNode = new ProgramElement(asm, fileName, IProgramElement.Kind.FILE_JAVA, new SourceLocation(new File( + sourceFilePath), 1, 1), 0, null, null); + // fileNode.setSourceLocation(); + fileNode.addChild(IHierarchy.NO_STRUCTURE); + return fileNode; + } + + private static boolean isBinaryAspect(ResolvedType aspect) { + return aspect.getBinaryPath() != null; + } + + /** + * Returns the binarySourceLocation for the given sourcelocation. This isn't cached because it's used when faulting in the + * binary nodes and is called with ISourceLocations for all advice, pointcuts and deows contained within the + * resolvedDeclaringAspect. + */ + private static ISourceLocation getBinarySourceLocation(ResolvedType aspect, ISourceLocation sl) { + if (sl == null) { + return null; + } + String sourceFileName = null; + if (aspect instanceof ReferenceType) { + String s = ((ReferenceType) aspect).getDelegate().getSourcefilename(); + int i = s.lastIndexOf('/'); + if (i != -1) { + sourceFileName = s.substring(i + 1); + } else { + sourceFileName = s; + } + } + ISourceLocation sLoc = new SourceLocation(getBinaryFile(aspect), sl.getLine(), sl.getEndLine(), + ((sl.getColumn() == 0) ? ISourceLocation.NO_COLUMN : sl.getColumn()), sl.getContext(), sourceFileName); + return sLoc; + } + + private static ISourceLocation createSourceLocation(String sourcefilename, ResolvedType aspect, ISourceLocation sl) { + ISourceLocation sLoc = new SourceLocation(getBinaryFile(aspect), sl.getLine(), sl.getEndLine(), + ((sl.getColumn() == 0) ? ISourceLocation.NO_COLUMN : sl.getColumn()), sl.getContext(), sourcefilename); + return sLoc; + } + + private static String getSourceFileName(ResolvedType aspect) { + String sourceFileName = null; + if (aspect instanceof ReferenceType) { + String s = ((ReferenceType) aspect).getDelegate().getSourcefilename(); + int i = s.lastIndexOf('/'); + if (i != -1) { + sourceFileName = s.substring(i + 1); + } else { + sourceFileName = s; + } + } + return sourceFileName; + } + + /** + * Returns the File with pathname to the class file, for example either C:\temp + * \ajcSandbox\workspace\ajcTest16957.tmp\simple.jar!pkg\BinaryAspect.class if the class file is in a jar file, or + * C:\temp\ajcSandbox\workspace\ajcTest16957.tmp!pkg\BinaryAspect.class if the class file is in a directory + */ + private static File getBinaryFile(ResolvedType aspect) { + String s = aspect.getBinaryPath(); + File f = aspect.getSourceLocation().getSourceFile(); + // Replace the source file suffix with .class + int i = f.getPath().lastIndexOf('.'); + String path = null; + if (i != -1) { + path = f.getPath().substring(0, i) + ".class"; + } else { + path = f.getPath() + ".class"; + } + return new File(s + "!" + path); + } + + /** + * Create a basic hierarchy to represent an aspect only available in binary (from the aspectpath). + */ + private static void createHierarchy(AsmManager model, ResolvedTypeMunger typeTransformer, ResolvedType aspect) { + // assert aspect != null; + + // Check if already defined in the model + // IProgramElement filenode = + // model.getHierarchy().findElementForType(aspect.getPackageName(), + // aspect.getClassName()); + // SourceLine(typeTransformer.getSourceLocation()); + IProgramElement filenode = model.getHierarchy().findElementForSourceLine(typeTransformer.getSourceLocation()); + if (filenode == null) { + if (typeTransformer.getKind() == ResolvedTypeMunger.MethodDelegate2 + || typeTransformer.getKind() == ResolvedTypeMunger.FieldHost) { + // not yet faulting these in + return; + } + } + // the call to findElementForSourceLine(ISourceLocation) returns a file + // node + // if it can't find a node in the hierarchy for the given + // sourcelocation. + // Therefore, if this is returned, we know we can't find one and have to + // // continue to fault in the model. + // if (filenode != null) { // + if (!filenode.getKind().equals(IProgramElement.Kind.FILE_JAVA)) { + return; + } + + // create the class file node + ISourceLocation binLocation = getBinarySourceLocation(aspect, aspect.getSourceLocation()); + String f = getBinaryFile(aspect).getName(); + IProgramElement classFileNode = new ProgramElement(model, f, IProgramElement.Kind.FILE, binLocation, 0, null, null); + + // create package ipe if one exists.... + IProgramElement root = model.getHierarchy().getRoot(); + IProgramElement binaries = model.getHierarchy().findElementForLabel(root, IProgramElement.Kind.SOURCE_FOLDER, "binaries"); + if (binaries == null) { + binaries = new ProgramElement(model, "binaries", IProgramElement.Kind.SOURCE_FOLDER, new ArrayList()); + root.addChild(binaries); + } + // if (aspect.getPackageName() != null) { + String packagename = aspect.getPackageName() == null ? "" : aspect.getPackageName(); + // check that there doesn't already exist a node with this name + IProgramElement pkgNode = model.getHierarchy().findElementForLabel(binaries, IProgramElement.Kind.PACKAGE, packagename); + // note packages themselves have no source location + if (pkgNode == null) { + pkgNode = new ProgramElement(model, packagename, IProgramElement.Kind.PACKAGE, new ArrayList()); + binaries.addChild(pkgNode); + pkgNode.addChild(classFileNode); + } else { + // need to add it first otherwise the handle for classFileNode + // may not be generated correctly if it uses information from + // it's parent node + pkgNode.addChild(classFileNode); + for (IProgramElement element: pkgNode.getChildren()) { + if (!element.equals(classFileNode) && element.getHandleIdentifier().equals(classFileNode.getHandleIdentifier())) { + // already added the classfile so have already + // added the structure for this aspect + pkgNode.removeChild(classFileNode); + return; + } + } + } + // } else { + // // need to add it first otherwise the handle for classFileNode + // // may not be generated correctly if it uses information from + // // it's parent node + // root.addChild(classFileNode); + // for (Iterator iter = root.getChildren().iterator(); iter.hasNext();) + // { + // IProgramElement element = (IProgramElement) iter.next(); + // if (!element.equals(classFileNode) && + // element.getHandleIdentifier().equals + // (classFileNode.getHandleIdentifier())) { + // // already added the sourcefile so have already + // // added the structure for this aspect + // root.removeChild(classFileNode); + // return; + // } + // } + // } + + // add and create empty import declaration ipe + // no import container for binary type - 265693 + // classFileNode.addChild(new ProgramElement(model, "import declarations", IProgramElement.Kind.IMPORT_REFERENCE, null, 0, + // null, null)); + + // add and create aspect ipe + IProgramElement aspectNode = new ProgramElement(model, aspect.getSimpleName(), IProgramElement.Kind.ASPECT, + getBinarySourceLocation(aspect, aspect.getSourceLocation()), aspect.getModifiers(), null, null); + classFileNode.addChild(aspectNode); + + addChildNodes(model, aspect, aspectNode, aspect.getDeclaredPointcuts()); + + addChildNodes(model, aspect, aspectNode, aspect.getDeclaredAdvice()); + addChildNodes(model, aspect, aspectNode, aspect.getDeclares()); + addChildNodes(model, aspect, aspectNode, aspect.getTypeMungers()); + } + + /** + * Adds a declare annotation relationship, sometimes entities don't have source locs (methods/fields) so use other variants of + * this method if that is the case as they will look the entities up in the structure model. + */ + public static void addDeclareAnnotationRelationship(AsmManager model, ISourceLocation declareAnnotationLocation, + ISourceLocation annotatedLocation, boolean isRemove) { + if (model == null) { + return; + } + + IProgramElement sourceNode = model.getHierarchy().findElementForSourceLine(declareAnnotationLocation); + String sourceHandle = sourceNode.getHandleIdentifier(); + if (sourceHandle == null) { + return; + } + + IProgramElement targetNode = model.getHierarchy().findElementForSourceLine(annotatedLocation); + String targetHandle = targetNode.getHandleIdentifier(); + if (targetHandle == null) { + return; + } + + IRelationshipMap mapper = model.getRelationshipMap(); + // if (isRemove) { + // IRelationship foreward = mapper.get(sourceHandle, IRelationship.Kind.DECLARE_INTER_TYPE, REMOVES_ANNOTATION, false, + // true); + // foreward.addTarget(targetHandle); + // + // IRelationship back = mapper + // .get(targetHandle, IRelationship.Kind.DECLARE_INTER_TYPE, ANNOTATION_REMOVED_BY, false, true); + // back.addTarget(sourceHandle); + // if (sourceNode.getSourceLocation() != null) { + // model.addAspectInEffectThisBuild(sourceNode.getSourceLocation().getSourceFile()); + // } + // } else { + IRelationship foreward = mapper.get(sourceHandle, IRelationship.Kind.DECLARE_INTER_TYPE, ANNOTATES, false, true); + foreward.addTarget(targetHandle); + + IRelationship back = mapper.get(targetHandle, IRelationship.Kind.DECLARE_INTER_TYPE, ANNOTATED_BY, false, true); + back.addTarget(sourceHandle); + if (sourceNode.getSourceLocation() != null) { + model.addAspectInEffectThisBuild(sourceNode.getSourceLocation().getSourceFile()); + } + // } + } + + /** + * Creates the hierarchy for binary aspects + */ + public static void createHierarchyForBinaryAspect(AsmManager asm, ShadowMunger munger) { + if (!munger.isBinary()) { + return; + } + + IProgramElement sourceFileNode = asm.getHierarchy().findElementForSourceLine(munger.getSourceLocation()); + // the call to findElementForSourceLine(ISourceLocation) returns a file + // node if it can't find a node in the hierarchy for the given sourcelocation. + // Therefore, if this is returned, we know we can't find one and have to + // continue to fault in the model. + if (!sourceFileNode.getKind().equals(IProgramElement.Kind.FILE_JAVA)) { + return; + } + + ResolvedType aspect = munger.getDeclaringType(); + + // create the class file node + IProgramElement classFileNode = new ProgramElement(asm, sourceFileNode.getName(), IProgramElement.Kind.FILE, + munger.getBinarySourceLocation(aspect.getSourceLocation()), 0, null, null); + + // create package ipe if one exists.... + IProgramElement root = asm.getHierarchy().getRoot(); + IProgramElement binaries = asm.getHierarchy().findElementForLabel(root, IProgramElement.Kind.SOURCE_FOLDER, "binaries"); + if (binaries == null) { + binaries = new ProgramElement(asm, "binaries", IProgramElement.Kind.SOURCE_FOLDER, new ArrayList()); + root.addChild(binaries); + } + // if (aspect.getPackageName() != null) { + String packagename = aspect.getPackageName() == null ? "" : aspect.getPackageName(); + // check that there doesn't already exist a node with this name + IProgramElement pkgNode = asm.getHierarchy().findElementForLabel(binaries, IProgramElement.Kind.PACKAGE, packagename); + // note packages themselves have no source location + if (pkgNode == null) { + pkgNode = new ProgramElement(asm, packagename, IProgramElement.Kind.PACKAGE, new ArrayList()); + binaries.addChild(pkgNode); + pkgNode.addChild(classFileNode); + } else { + // need to add it first otherwise the handle for classFileNode + // may not be generated correctly if it uses information from + // it's parent node + pkgNode.addChild(classFileNode); + for (IProgramElement element: pkgNode.getChildren()) { + if (!element.equals(classFileNode) && element.getHandleIdentifier().equals(classFileNode.getHandleIdentifier())) { + // already added the classfile so have already + // added the structure for this aspect + pkgNode.removeChild(classFileNode); + return; + } + } + } + // } else { + // // need to add it first otherwise the handle for classFileNode + // // may not be generated correctly if it uses information from + // // it's parent node + // root.addChild(classFileNode); + // for (Iterator iter = root.getChildren().iterator(); iter.hasNext();) + // { + // IProgramElement element = (IProgramElement) iter.next(); + // if (!element.equals(classFileNode) && + // element.getHandleIdentifier().equals + // (classFileNode.getHandleIdentifier())) { + // // already added the sourcefile so have already + // // added the structure for this aspect + // root.removeChild(classFileNode); + // return; + // } + // } + // } + + // add and create empty import declaration ipe + // classFileNode.addChild(new ProgramElement(asm, "import declarations", IProgramElement.Kind.IMPORT_REFERENCE, null, 0, + // null, + // null)); + + // add and create aspect ipe + IProgramElement aspectNode = new ProgramElement(asm, aspect.getSimpleName(), IProgramElement.Kind.ASPECT, + munger.getBinarySourceLocation(aspect.getSourceLocation()), aspect.getModifiers(), null, null); + classFileNode.addChild(aspectNode); + + String sourcefilename = getSourceFileName(aspect); + addPointcuts(asm, sourcefilename, aspect, aspectNode, aspect.getDeclaredPointcuts()); + addChildNodes(asm, aspect, aspectNode, aspect.getDeclaredAdvice()); + addChildNodes(asm, aspect, aspectNode, aspect.getDeclares()); + addChildNodes(asm, aspect, aspectNode, aspect.getTypeMungers()); + + } + + private static void addPointcuts(AsmManager model, String sourcefilename, ResolvedType aspect, + IProgramElement containingAspect, ResolvedMember[] pointcuts) { + for (int i = 0; i < pointcuts.length; i++) { + ResolvedMember pointcut = pointcuts[i]; + if (pointcut instanceof ResolvedPointcutDefinition) { + ResolvedPointcutDefinition rpcd = (ResolvedPointcutDefinition) pointcut; + Pointcut p = rpcd.getPointcut(); + ISourceLocation sLoc = (p == null ? null : p.getSourceLocation()); + if (sLoc == null) { + sLoc = rpcd.getSourceLocation(); + } + ISourceLocation pointcutLocation = (sLoc == null ? null : createSourceLocation(sourcefilename, aspect, sLoc)); + ProgramElement pointcutElement = new ProgramElement(model, pointcut.getName(), IProgramElement.Kind.POINTCUT, + pointcutLocation, pointcut.getModifiers(), NO_COMMENT, Collections.emptyList()); + containingAspect.addChild(pointcutElement); + } + } + } + + private static final String NO_COMMENT = null; + + private static void addChildNodes(AsmManager asm, ResolvedType aspect, IProgramElement parent, ResolvedMember[] children) { + for (int i = 0; i < children.length; i++) { + ResolvedMember pcd = children[i]; + if (pcd instanceof ResolvedPointcutDefinition) { + ResolvedPointcutDefinition rpcd = (ResolvedPointcutDefinition) pcd; + Pointcut p = rpcd.getPointcut(); + ISourceLocation sLoc = (p == null ? null : p.getSourceLocation()); + if (sLoc == null) { + sLoc = rpcd.getSourceLocation(); + } + parent.addChild(new ProgramElement(asm, pcd.getName(), IProgramElement.Kind.POINTCUT, getBinarySourceLocation( + aspect, sLoc), pcd.getModifiers(), null, Collections.emptyList())); + } + } + } + + private static void addChildNodes(AsmManager asm, ResolvedType aspect, IProgramElement parent, Collection children) { + int deCtr = 1; + int dwCtr = 1; + for (Object element: children) { + if (element instanceof DeclareErrorOrWarning) { + DeclareErrorOrWarning decl = (DeclareErrorOrWarning) element; + int counter = 0; + if (decl.isError()) { + counter = deCtr++; + } else { + counter = dwCtr++; + } + parent.addChild(createDeclareErrorOrWarningChild(asm, aspect, decl, counter)); + } else if (element instanceof Advice) { + Advice advice = (Advice) element; + parent.addChild(createAdviceChild(asm, advice)); + } else if (element instanceof DeclareParents) { + parent.addChild(createDeclareParentsChild(asm, (DeclareParents) element)); + } else if (element instanceof BcelTypeMunger) { + IProgramElement newChild = createIntertypeDeclaredChild(asm, aspect, (BcelTypeMunger) element); + // newChild==null means it is something that could not be handled by createIntertypeDeclaredChild() + if (newChild != null) { + parent.addChild(newChild); + } + } + } + } + + // private static IProgramElement + // createDeclareErrorOrWarningChild(AsmManager asm, ShadowMunger munger, + // DeclareErrorOrWarning decl, int count) { + // IProgramElement deowNode = new ProgramElement(asm, decl.getName(), + // decl.isError() ? IProgramElement.Kind.DECLARE_ERROR + // : IProgramElement.Kind.DECLARE_WARNING, + // munger.getBinarySourceLocation(decl.getSourceLocation()), decl + // .getDeclaringType().getModifiers(), null, null); + // deowNode.setDetails("\"" + + // AsmRelationshipUtils.genDeclareMessage(decl.getMessage()) + "\""); + // if (count != -1) { + // deowNode.setBytecodeName(decl.getName() + "_" + count); + // } + // return deowNode; + // } + + private static IProgramElement createDeclareErrorOrWarningChild(AsmManager model, ResolvedType aspect, + DeclareErrorOrWarning decl, int count) { + IProgramElement deowNode = new ProgramElement(model, decl.getName(), decl.isError() ? IProgramElement.Kind.DECLARE_ERROR + : IProgramElement.Kind.DECLARE_WARNING, getBinarySourceLocation(aspect, decl.getSourceLocation()), decl + .getDeclaringType().getModifiers(), null, null); + deowNode.setDetails("\"" + AsmRelationshipUtils.genDeclareMessage(decl.getMessage()) + "\""); + if (count != -1) { + deowNode.setBytecodeName(decl.getName() + "_" + count); + } + return deowNode; + } + + private static IProgramElement createAdviceChild(AsmManager model, Advice advice) { + IProgramElement adviceNode = new ProgramElement(model, advice.getKind().getName(), IProgramElement.Kind.ADVICE, + advice.getBinarySourceLocation(advice.getSourceLocation()), advice.getSignature().getModifiers(), null, + Collections.emptyList()); + adviceNode.setDetails(AsmRelationshipUtils.genPointcutDetails(advice.getPointcut())); + adviceNode.setBytecodeName(advice.getSignature().getName()); + return adviceNode; + } + + /** + * Half baked implementation - will need completing if we go down this route rather than replacing it all for binary aspects. + * Doesn't attempt to get parameter names correct - they may have been lost during (de)serialization of the munger, but the + * member could still be located so they might be retrievable. + */ + private static IProgramElement createIntertypeDeclaredChild(AsmManager model, ResolvedType aspect, BcelTypeMunger itd) { + ResolvedTypeMunger rtMunger = itd.getMunger(); + + ResolvedMember sig = rtMunger.getSignature(); + Kind kind = rtMunger.getKind(); + if (kind == ResolvedTypeMunger.Field) { // ITD FIELD + // String name = rtMunger.getSignature().toString(); + String name = sig.getDeclaringType().getClassName() + "." + sig.getName(); + if (name.indexOf("$") != -1) { + name = name.substring(name.indexOf("$") + 1); + } + IProgramElement pe = new ProgramElement(model, name, IProgramElement.Kind.INTER_TYPE_FIELD, getBinarySourceLocation( + aspect, itd.getSourceLocation()), rtMunger.getSignature().getModifiers(), null, Collections.emptyList()); + pe.setCorrespondingType(sig.getReturnType().getName()); + return pe; + } else if (kind == ResolvedTypeMunger.Method) { // ITD + // METHOD + String name = sig.getDeclaringType().getClassName() + "." + sig.getName(); + if (name.indexOf("$") != -1) { + name = name.substring(name.indexOf("$") + 1); + } + IProgramElement pe = new ProgramElement(model, name, IProgramElement.Kind.INTER_TYPE_METHOD, getBinarySourceLocation( + aspect, itd.getSourceLocation()), rtMunger.getSignature().getModifiers(), null, Collections.emptyList()); + setParams(pe, sig); + return pe; + } else if (kind == ResolvedTypeMunger.Constructor) { + String name = sig.getDeclaringType().getClassName() + "." + sig.getDeclaringType().getClassName(); + if (name.indexOf("$") != -1) { + name = name.substring(name.indexOf("$") + 1); + } + IProgramElement pe = new ProgramElement(model, name, IProgramElement.Kind.INTER_TYPE_CONSTRUCTOR, + getBinarySourceLocation(aspect, itd.getSourceLocation()), rtMunger.getSignature().getModifiers(), null, + Collections.emptyList()); + setParams(pe, sig); + return pe; + // } else if (kind == ResolvedTypeMunger.MethodDelegate2) { + // String name = sig.getDeclaringType().getClassName() + "." + sig.getName(); + // if (name.indexOf("$") != -1) { + // name = name.substring(name.indexOf("$") + 1); + // } + // IProgramElement pe = new ProgramElement(model, name, IProgramElement.Kind.INTER_TYPE_METHOD, getBinarySourceLocation( + // aspect, itd.getSourceLocation()), rtMunger.getSignature().getModifiers(), null, Collections.EMPTY_LIST); + // setParams(pe, sig); + // return pe; + } + // other cases ignored for now + return null; + } + + private static void setParams(IProgramElement pe, ResolvedMember sig) { + // do it for itds too + UnresolvedType[] ts = sig.getParameterTypes(); + pe.setParameterNames(Collections.emptyList()); + // TODO should be doing param names? + if (ts == null) { + pe.setParameterSignatures(Collections.emptyList(), Collections.emptyList()); + } else { + List paramSigs = new ArrayList(); + for (int i = 0; i < ts.length; i++) { + paramSigs.add(ts[i].getSignature().toCharArray()); + } + pe.setParameterSignatures(paramSigs, Collections.emptyList()); + } + pe.setCorrespondingType(sig.getReturnType().getName()); + } + + private static IProgramElement createDeclareParentsChild(AsmManager model, DeclareParents decp) { + IProgramElement decpElement = new ProgramElement(model, "declare parents", IProgramElement.Kind.DECLARE_PARENTS, + getBinarySourceLocation(decp.getDeclaringType(), decp.getSourceLocation()), Modifier.PUBLIC, null, + Collections.emptyList()); + setParentTypesOnDeclareParentsNode(decp, decpElement); + return decpElement; + } + + private static void setParentTypesOnDeclareParentsNode(DeclareParents decp, IProgramElement decpElement) { + TypePatternList tpl = decp.getParents(); + List parents = new ArrayList(); + for (int i = 0; i < tpl.size(); i++) { + parents.add(tpl.get(i).getExactType().getName().replaceAll("\\$", ".")); + } + decpElement.setParentTypes(parents); + } + + public static String getHandle(AsmManager asm, Advice advice) { + if (null == advice.handle) { + ISourceLocation sl = advice.getSourceLocation(); + if (sl != null) { + IProgramElement ipe = asm.getHierarchy().findElementForSourceLine(sl); + advice.handle = ipe.getHandleIdentifier(); + } + } + return advice.handle; + } + + public static void addAdvisedRelationship(AsmManager model, Shadow matchedShadow, ShadowMunger munger) { + if (model == null) { + return; + } + + if (munger instanceof Advice) { + Advice advice = (Advice) munger; + + if (advice.getKind().isPerEntry() || advice.getKind().isCflow()) { + // TODO: might want to show these in the future + return; + } + + if (World.createInjarHierarchy) { + createHierarchyForBinaryAspect(model, advice); + } + + IRelationshipMap mapper = model.getRelationshipMap(); + IProgramElement targetNode = getNode(model, matchedShadow); + if (targetNode == null) { + return; + } + boolean runtimeTest = advice.hasDynamicTests(); + + IProgramElement.ExtraInformation extra = new IProgramElement.ExtraInformation(); + + String adviceHandle = getHandle(model, advice); + if (adviceHandle == null) { + return; + } + + extra.setExtraAdviceInformation(advice.getKind().getName()); + IProgramElement adviceElement = model.getHierarchy().findElementForHandle(adviceHandle); + if (adviceElement != null) { + adviceElement.setExtraInfo(extra); + } + String targetHandle = targetNode.getHandleIdentifier(); + if (advice.getKind().equals(AdviceKind.Softener)) { + IRelationship foreward = mapper.get(adviceHandle, IRelationship.Kind.DECLARE_SOFT, SOFTENS, runtimeTest, true); + if (foreward != null) { + foreward.addTarget(targetHandle); + } + + IRelationship back = mapper.get(targetHandle, IRelationship.Kind.DECLARE, SOFTENED_BY, runtimeTest, true); + if (back != null) { + back.addTarget(adviceHandle); + } + } else { + IRelationship foreward = mapper.get(adviceHandle, IRelationship.Kind.ADVICE, ADVISES, runtimeTest, true); + if (foreward != null) { + foreward.addTarget(targetHandle); + } + + IRelationship back = mapper.get(targetHandle, IRelationship.Kind.ADVICE, ADVISED_BY, runtimeTest, true); + if (back != null) { + back.addTarget(adviceHandle); + } + } + if (adviceElement.getSourceLocation() != null) { + model.addAspectInEffectThisBuild(adviceElement.getSourceLocation().getSourceFile()); + } + } + } + + protected static IProgramElement getNode(AsmManager model, Shadow shadow) { + Member enclosingMember = shadow.getEnclosingCodeSignature(); + // This variant will not be tricked by ITDs that would report they are + // in the target type already. + // This enables us to discover the ITD declaration (in the aspect) and + // advise it appropriately. + + // Have to be smart here, for a code node within an ITD we want to + // lookup the declaration of the + // ITD in the aspect in order to add the code node at the right place - + // and not lookup the + // ITD as it applies in some target type. Due to the use of + // effectiveSignature we will find + // that shadow.getEnclosingCodeSignature() will return a member + // representing the ITD as it will + // appear in the target type. So here, we do an extra bit of analysis to + // make sure we + // do the right thing in the ITD case. + IProgramElement enclosingNode = null; + if (shadow instanceof BcelShadow) { + Member actualEnclosingMember = ((BcelShadow) shadow).getRealEnclosingCodeSignature(); + + if (actualEnclosingMember == null) { + enclosingNode = lookupMember(model.getHierarchy(), shadow.getEnclosingType(), enclosingMember); + } else { + UnresolvedType type = enclosingMember.getDeclaringType(); + UnresolvedType actualType = actualEnclosingMember.getDeclaringType(); + + // if these are not the same, it is an ITD and we need to use + // the latter to lookup + if (type.equals(actualType)) { + enclosingNode = lookupMember(model.getHierarchy(), shadow.getEnclosingType(), enclosingMember); + } else { + enclosingNode = lookupMember(model.getHierarchy(), shadow.getEnclosingType(), actualEnclosingMember); + } + } + } else { + enclosingNode = lookupMember(model.getHierarchy(), shadow.getEnclosingType(), enclosingMember); + } + + if (enclosingNode == null) { + Lint.Kind err = shadow.getIWorld().getLint().shadowNotInStructure; + if (err.isEnabled()) { + err.signal(shadow.toString(), shadow.getSourceLocation()); + } + return null; + } + + Member shadowSig = shadow.getSignature(); + // pr235204 + if (shadow.getKind() == Shadow.MethodCall || shadow.getKind() == Shadow.ConstructorCall + || !shadowSig.equals(enclosingMember)) { + IProgramElement bodyNode = findOrCreateCodeNode(model, enclosingNode, shadowSig, shadow); + return bodyNode; + } else { + return enclosingNode; + } + } + + private static boolean sourceLinesMatch(ISourceLocation location1, ISourceLocation location2) { + return (location1.getLine() == location2.getLine()); + } + + /** + * Finds or creates a code IProgramElement for the given shadow. + * + * The byteCodeName of the created node is set to 'shadowSig.getName() + "!" + counter', eg "println!3". The counter is the + * occurence count of children within the enclosingNode which have the same name. So, for example, if a method contains two + * System.out.println statements, the first one will have byteCodeName 'println!1' and the second will have byteCodeName + * 'println!2'. This is to ensure the two nodes have unique handles when the handles do not depend on sourcelocations. + * + * Currently the shadows are examined in the sequence they appear in the source file. This means that the counters are + * consistent over incremental builds. All aspects are compiled up front and any new aspect created will force a full build. + * Moreover, if the body of the enclosingShadow is changed, then the model for this is rebuilt from scratch. + */ + private static IProgramElement findOrCreateCodeNode(AsmManager asm, IProgramElement enclosingNode, Member shadowSig, + Shadow shadow) { + for (Iterator it = enclosingNode.getChildren().iterator(); it.hasNext();) { + IProgramElement node = (IProgramElement) it.next(); + int excl = node.getBytecodeName().lastIndexOf('!'); + if (((excl != -1 && shadowSig.getName().equals(node.getBytecodeName().substring(0, excl))) || shadowSig.getName() + .equals(node.getBytecodeName())) + && shadowSig.getSignature().equals(node.getBytecodeSignature()) + && sourceLinesMatch(node.getSourceLocation(), shadow.getSourceLocation())) { + return node; + } + } + + ISourceLocation sl = shadow.getSourceLocation(); + + // XXX why not use shadow file? new SourceLocation(sl.getSourceFile(), + // sl.getLine()), + SourceLocation peLoc = new SourceLocation(enclosingNode.getSourceLocation().getSourceFile(), sl.getLine()); + peLoc.setOffset(sl.getOffset()); + IProgramElement peNode = new ProgramElement(asm, shadow.toString(), IProgramElement.Kind.CODE, peLoc, 0, null, null); + + // check to see if the enclosing shadow already has children with the + // same name. If so we want to add a counter to the byteCodeName + // otherwise + // we wont get unique handles + int numberOfChildrenWithThisName = 0; + for (IProgramElement child: enclosingNode.getChildren()) { + if (child.getName().equals(shadow.toString())) { + numberOfChildrenWithThisName++; + } + } + peNode.setBytecodeName(shadowSig.getName() + "!" + String.valueOf(numberOfChildrenWithThisName + 1)); + peNode.setBytecodeSignature(shadowSig.getSignature()); + enclosingNode.addChild(peNode); + return peNode; + } + + private static IProgramElement lookupMember(IHierarchy model, UnresolvedType declaringType, Member member) { + IProgramElement typeElement = model.findElementForType(declaringType.getPackageName(), declaringType.getClassName()); + if (typeElement == null) { + return null; + } + for (Iterator it = typeElement.getChildren().iterator(); it.hasNext();) { + IProgramElement element = (IProgramElement) it.next(); + if (member.getName().equals(element.getBytecodeName()) && member.getSignature().equals(element.getBytecodeSignature())) { + return element; + } + } + // if we can't find the member, we'll just put it in the class + return typeElement; + } + + /** + * Add a relationship for a matching declare annotation method or declare annotation constructor. Locating the method is a messy + * (for messy read 'fragile') bit of code that could break at any moment but it's working for my simple testcase. + */ + public static void addDeclareAnnotationMethodRelationship(ISourceLocation sourceLocation, String affectedTypeName, + ResolvedMember affectedMethod, AsmManager model) { + if (model == null) { + return; + } + + String pkg = null; + String type = affectedTypeName; + int packageSeparator = affectedTypeName.lastIndexOf("."); + if (packageSeparator != -1) { + pkg = affectedTypeName.substring(0, packageSeparator); + type = affectedTypeName.substring(packageSeparator + 1); + } + + IHierarchy hierarchy = model.getHierarchy(); + + IProgramElement typeElem = hierarchy.findElementForType(pkg, type); + if (typeElem == null) { + return; + } + if (!typeElem.getKind().isType()) { + throw new IllegalStateException("Did not find a type element, found a "+typeElem.getKind()+" element"); + } + + StringBuilder parmString = new StringBuilder("("); + UnresolvedType[] args = affectedMethod.getParameterTypes(); + for (int i = 0; i < args.length; i++) { + parmString.append(args[i].getName()); + if ((i + 1) < args.length) { + parmString.append(","); + } + } + parmString.append(")"); + IProgramElement methodElem = null; + + if (affectedMethod.getName().startsWith("")) { + // its a ctor + methodElem = hierarchy.findElementForSignature(typeElem, IProgramElement.Kind.CONSTRUCTOR, type + parmString); + if (methodElem == null && args.length == 0) { + methodElem = typeElem; // assume default ctor + } + } else { + // its a method + methodElem = hierarchy.findElementForSignature(typeElem, IProgramElement.Kind.METHOD, affectedMethod.getName() + + parmString); + } + + if (methodElem == null) { + return; + } + + try { + String targetHandle = methodElem.getHandleIdentifier(); + if (targetHandle == null) { + return; + } + + IProgramElement sourceNode = hierarchy.findElementForSourceLine(sourceLocation); + String sourceHandle = sourceNode.getHandleIdentifier(); + if (sourceHandle == null) { + return; + } + + IRelationshipMap mapper = model.getRelationshipMap(); + IRelationship foreward = mapper.get(sourceHandle, IRelationship.Kind.DECLARE_INTER_TYPE, ANNOTATES, false, true); + foreward.addTarget(targetHandle); + + IRelationship back = mapper.get(targetHandle, IRelationship.Kind.DECLARE_INTER_TYPE, ANNOTATED_BY, false, true); + back.addTarget(sourceHandle); + } catch (Throwable t) { // I'm worried about that code above, this will + // make sure we don't explode if it plays up + t.printStackTrace(); // I know I know .. but I don't want to lose + // it! + } + } + + /** + * Add a relationship for a matching declare ATfield. Locating the field is trickier than it might seem since we have no line + * number info for it, we have to dig through the structure model under the fields' type in order to locate it. + */ + public static void addDeclareAnnotationFieldRelationship(AsmManager model, ISourceLocation declareLocation, + String affectedTypeName, ResolvedMember affectedFieldName, boolean isRemove) { + if (model == null) { + return; + } + + String pkg = null; + String type = affectedTypeName; + int packageSeparator = affectedTypeName.lastIndexOf("."); + if (packageSeparator != -1) { + pkg = affectedTypeName.substring(0, packageSeparator); + type = affectedTypeName.substring(packageSeparator + 1); + } + IHierarchy hierarchy = model.getHierarchy(); + IProgramElement typeElem = hierarchy.findElementForType(pkg, type); + if (typeElem == null) { + return; + } + + IProgramElement fieldElem = hierarchy.findElementForSignature(typeElem, IProgramElement.Kind.FIELD, + affectedFieldName.getName()); + if (fieldElem == null) { + return; + } + + String targetHandle = fieldElem.getHandleIdentifier(); + if (targetHandle == null) { + return; + } + + IProgramElement sourceNode = hierarchy.findElementForSourceLine(declareLocation); + String sourceHandle = sourceNode.getHandleIdentifier(); + if (sourceHandle == null) { + return; + } + + IRelationshipMap relmap = model.getRelationshipMap(); + // if (isRemove) { + // IRelationship foreward = relmap.get(sourceHandle, IRelationship.Kind.DECLARE_INTER_TYPE, REMOVES_ANNOTATION, false, + // true); + // foreward.addTarget(targetHandle); + // IRelationship back = relmap + // .get(targetHandle, IRelationship.Kind.DECLARE_INTER_TYPE, ANNOTATION_REMOVED_BY, false, true); + // back.addTarget(sourceHandle); + // } else { + IRelationship foreward = relmap.get(sourceHandle, IRelationship.Kind.DECLARE_INTER_TYPE, ANNOTATES, false, true); + foreward.addTarget(targetHandle); + IRelationship back = relmap.get(targetHandle, IRelationship.Kind.DECLARE_INTER_TYPE, ANNOTATED_BY, false, true); + back.addTarget(sourceHandle); + // } + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/model/AsmRelationshipUtils.java b/weaver/src/main/java/org/aspectj/weaver/model/AsmRelationshipUtils.java new file mode 100644 index 000000000..f9c9f6ae2 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/model/AsmRelationshipUtils.java @@ -0,0 +1,79 @@ +/******************************************************************** + * Copyright (c) 2006 Contributors. All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: IBM Corporation - initial API and implementation + * Helen Hawkins - initial version + *******************************************************************/ +package org.aspectj.weaver.model; + +import org.aspectj.weaver.patterns.AndPointcut; +import org.aspectj.weaver.patterns.OrPointcut; +import org.aspectj.weaver.patterns.Pointcut; +import org.aspectj.weaver.patterns.ReferencePointcut; + +/** + * Provides utility methods for generating details for IProgramElements used when creating the model both from source (via + * AsmElementFormatter.visit(..)) and when filling in the model for binary aspects (via AsmRelationshipProvider bug 145963) + */ +public class AsmRelationshipUtils { + + // public static final String UNDEFINED=""; + public static final String DECLARE_PRECEDENCE = "precedence"; + public static final String DECLARE_SOFT = "soft"; + public static final String DECLARE_PARENTS = "parents"; + public static final String DECLARE_WARNING = "warning"; + public static final String DECLARE_ERROR = "error"; + public static final String DECLARE_UNKNONWN = ""; + public static final String POINTCUT_ABSTRACT = ""; + public static final String POINTCUT_ANONYMOUS = ""; + public static final String DOUBLE_DOTS = ".."; + public static final int MAX_MESSAGE_LENGTH = 18; + public static final String DEC_LABEL = "declare"; + + /** + * Generates the declare message used in the details, for example if the declare warning statement has message + * "There should be no printlns" will return 'declare warning: "There should be n.."' + */ + public static String genDeclareMessage(String message) { + int length = message.length(); + if (length < MAX_MESSAGE_LENGTH) { + return message; + } else { + return message.substring(0, MAX_MESSAGE_LENGTH - 1) + DOUBLE_DOTS; + } + } + + /** + * Generates the pointcut details for the given pointcut, for example an anonymous pointcut will return '' + * and a named pointcut called p() will return 'p()..' + */ + public static String genPointcutDetails(Pointcut pcd) { + StringBuffer details = new StringBuffer(); + if (pcd instanceof ReferencePointcut) { + ReferencePointcut rp = (ReferencePointcut) pcd; + details.append(rp.name).append(DOUBLE_DOTS); + } else if (pcd instanceof AndPointcut) { + AndPointcut ap = (AndPointcut) pcd; + if (ap.getLeft() instanceof ReferencePointcut) { + details.append(ap.getLeft().toString()).append(DOUBLE_DOTS); + } else { + details.append(POINTCUT_ANONYMOUS).append(DOUBLE_DOTS); + } + } else if (pcd instanceof OrPointcut) { + OrPointcut op = (OrPointcut) pcd; + if (op.getLeft() instanceof ReferencePointcut) { + details.append(op.getLeft().toString()).append(DOUBLE_DOTS); + } else { + details.append(POINTCUT_ANONYMOUS).append(DOUBLE_DOTS); + } + } else { + details.append(POINTCUT_ANONYMOUS); + } + return details.toString(); + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/reflect/ArgNameFinder.java b/weaver/src/main/java/org/aspectj/weaver/reflect/ArgNameFinder.java new file mode 100644 index 000000000..25945a90a --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/reflect/ArgNameFinder.java @@ -0,0 +1,26 @@ +/* ******************************************************************* + * Copyright (c) 2005-2017 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * ******************************************************************/ +package org.aspectj.weaver.reflect; + +import java.lang.reflect.Member; + +/** + * @author Adrian Colyer + * @author Andy Clement + */ +public interface ArgNameFinder { + + /** + * Attempt to discover the parameter names for a reflectively obtained member. + * @param forMember the member for which parameter names are being looked up + * @return parameter names or null if names can't be determined + */ + String[] getParameterNames(Member forMember); + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/reflect/DeferredResolvedPointcutDefinition.java b/weaver/src/main/java/org/aspectj/weaver/reflect/DeferredResolvedPointcutDefinition.java new file mode 100644 index 000000000..b0af93f16 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/reflect/DeferredResolvedPointcutDefinition.java @@ -0,0 +1,35 @@ +/* ******************************************************************* + * Copyright (c) 2006 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.reflect; + +import org.aspectj.weaver.ResolvedPointcutDefinition; +import org.aspectj.weaver.UnresolvedType; + +/** + * When a Java15ReflectionBasedDelegate gets the pointcuts for a given class it tries to resolve them before returning. This can + * cause problems if the resolution of one pointcut in the type depends on another pointcut in the same type. Therefore the + * algorithm proceeds in two phases, first we create and store instances of this class in the pointcuts array, and once that is + * done, we come back round and resolve the actual pointcut expression. This means that if we recurse doing resolution, we will find + * the named pointcut we are looking for! + * + * @author adrian colyer + * + */ +public class DeferredResolvedPointcutDefinition extends ResolvedPointcutDefinition { + + public DeferredResolvedPointcutDefinition(UnresolvedType declaringType, int modifiers, String name, + UnresolvedType[] parameterTypes) { + super(declaringType, modifiers, name, parameterTypes, UnresolvedType.VOID, null); + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/reflect/InternalUseOnlyPointcutParser.java b/weaver/src/main/java/org/aspectj/weaver/reflect/InternalUseOnlyPointcutParser.java new file mode 100644 index 000000000..8d81d7b08 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/reflect/InternalUseOnlyPointcutParser.java @@ -0,0 +1,43 @@ +/* ******************************************************************* + * Copyright (c) 2006 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.reflect; + +import org.aspectj.weaver.patterns.Pointcut; +import org.aspectj.weaver.tools.PointcutParameter; +import org.aspectj.weaver.tools.PointcutParser; + +public class InternalUseOnlyPointcutParser extends PointcutParser { + + public InternalUseOnlyPointcutParser(ClassLoader classLoader, ReflectionWorld world) { + super(); + setClassLoader(classLoader); + setWorld(world); + } + + public InternalUseOnlyPointcutParser(ClassLoader classLoader) { + super(); + setClassLoader(classLoader); + } + + public Pointcut resolvePointcutExpression( + String expression, + Class inScope, + PointcutParameter[] formalParameters) { + return super.resolvePointcutExpression(expression, inScope, formalParameters); + } + + public Pointcut concretizePointcutExpression(Pointcut pc, Class inScope, PointcutParameter[] formalParameters) { + return super.concretizePointcutExpression(pc, inScope, formalParameters); + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/reflect/Java15AnnotationFinder.java b/weaver/src/main/java/org/aspectj/weaver/reflect/Java15AnnotationFinder.java new file mode 100644 index 000000000..016becf87 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/reflect/Java15AnnotationFinder.java @@ -0,0 +1,386 @@ +/* ******************************************************************* + * Copyright (c) 2005, 2017 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * ******************************************************************/ +package org.aspectj.weaver.reflect; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; + +import org.aspectj.apache.bcel.classfile.AnnotationDefault; +import org.aspectj.apache.bcel.classfile.Attribute; +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.classfile.LocalVariable; +import org.aspectj.apache.bcel.classfile.LocalVariableTable; +import org.aspectj.apache.bcel.util.ClassLoaderRepository; +import org.aspectj.apache.bcel.util.NonCachingClassLoaderRepository; +import org.aspectj.apache.bcel.util.Repository; +import org.aspectj.weaver.AnnotationAJ; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.World; +import org.aspectj.weaver.bcel.BcelAnnotation; +import org.aspectj.weaver.bcel.BcelWeakClassLoaderReference; + +/** + * + * @author Adrian Colyer + * @author Andy Clement + */ +public class Java15AnnotationFinder implements AnnotationFinder, ArgNameFinder { + + public static final ResolvedType[][] NO_PARAMETER_ANNOTATIONS = new ResolvedType[][] {}; + + private Repository bcelRepository; + private BcelWeakClassLoaderReference classLoaderRef; + private World world; + private static boolean useCachingClassLoaderRepository; + + static { + try { + useCachingClassLoaderRepository = System.getProperty("Xset:bcelRepositoryCaching","true").equalsIgnoreCase("true"); + } catch (Throwable t) { + useCachingClassLoaderRepository = false; + } + } + + // must have no-arg constructor for reflective construction + public Java15AnnotationFinder() { + } + + public void setClassLoader(ClassLoader aLoader) { + this.classLoaderRef = new BcelWeakClassLoaderReference(aLoader); + if (useCachingClassLoaderRepository) { + this.bcelRepository = new ClassLoaderRepository(classLoaderRef); + } else { + this.bcelRepository = new NonCachingClassLoaderRepository(classLoaderRef); + } + } + + public void setWorld(World aWorld) { + this.world = aWorld; + } + + public Object getAnnotation(ResolvedType annotationType, Object onObject) { + try { + Class annotationClass = (Class) Class.forName(annotationType.getName(), + false, getClassLoader()); + if (onObject.getClass().isAnnotationPresent(annotationClass)) { + return onObject.getClass().getAnnotation(annotationClass); + } + } catch (ClassNotFoundException ex) { + // just return null + } + return null; + } + + public Object getAnnotationFromClass(ResolvedType annotationType, Class aClass) { + try { + Class annotationClass = (Class) Class.forName(annotationType.getName(), + false, getClassLoader()); + if (aClass.isAnnotationPresent(annotationClass)) { + return aClass.getAnnotation(annotationClass); + } + } catch (ClassNotFoundException ex) { + // just return null + } + return null; + } + + public Object getAnnotationFromMember(ResolvedType annotationType, Member aMember) { + if (!(aMember instanceof AccessibleObject)) + return null; + AccessibleObject ao = (AccessibleObject) aMember; + try { + Class annotationClass = Class.forName(annotationType.getName(), false, getClassLoader()); + if (ao.isAnnotationPresent(annotationClass)) { + return ao.getAnnotation(annotationClass); + } + } catch (ClassNotFoundException ex) { + // just return null + } + return null; + } + + private ClassLoader getClassLoader() { + return classLoaderRef.getClassLoader(); + } + + public AnnotationAJ getAnnotationOfType(UnresolvedType ofType, Member onMember) { + if (!(onMember instanceof AccessibleObject)) + return null; + // here we really want both the runtime visible AND the class visible + // annotations + // so we bail out to Bcel and then chuck away the JavaClass so that we + // don't hog + // memory. + try { + JavaClass jc = bcelRepository.loadClass(onMember.getDeclaringClass()); + org.aspectj.apache.bcel.classfile.annotation.AnnotationGen[] anns = new org.aspectj.apache.bcel.classfile.annotation.AnnotationGen[0]; + if (onMember instanceof Method) { + org.aspectj.apache.bcel.classfile.Method bcelMethod = jc.getMethod((Method) onMember); + if (bcelMethod == null) { + // pr220430 + // System.err.println( + // "Unexpected problem in Java15AnnotationFinder: cannot retrieve annotations on method '" + // + + // onMember.getName()+"' in class '"+jc.getClassName()+"'"); + } else { + anns = bcelMethod.getAnnotations(); + } + } else if (onMember instanceof Constructor) { + org.aspectj.apache.bcel.classfile.Method bcelCons = jc.getMethod((Constructor) onMember); + anns = bcelCons.getAnnotations(); + } else if (onMember instanceof Field) { + org.aspectj.apache.bcel.classfile.Field bcelField = jc.getField((Field) onMember); + anns = bcelField.getAnnotations(); + } + // the answer is cached and we don't want to hold on to memory + bcelRepository.clear(); + // OPTIMIZE make constant 0 size array for sharing + if (anns == null) + anns = new org.aspectj.apache.bcel.classfile.annotation.AnnotationGen[0]; + // convert to our Annotation type + for (int i = 0; i < anns.length; i++) { + if (anns[i].getTypeSignature().equals(ofType.getSignature())) { + return new BcelAnnotation(anns[i], world); + } + } + return null; + } catch (ClassNotFoundException cnfEx) { + // just use reflection then + } + return null; + } + + public String getAnnotationDefaultValue(Member onMember) { + try { + JavaClass jc = bcelRepository.loadClass(onMember.getDeclaringClass()); + if (onMember instanceof Method) { + org.aspectj.apache.bcel.classfile.Method bcelMethod = jc.getMethod((Method) onMember); + + if (bcelMethod == null) { + // pr220430 + // System.err.println( + // "Unexpected problem in Java15AnnotationFinder: cannot retrieve annotations on method '" + // + + // onMember.getName()+"' in class '"+jc.getClassName()+"'"); + } else { + Attribute[] attrs = bcelMethod.getAttributes(); + for (int i = 0; i < attrs.length; i++) { + Attribute attribute = attrs[i]; + if (attribute.getName().equals("AnnotationDefault")) { + AnnotationDefault def = (AnnotationDefault) attribute; + return def.getElementValue().stringifyValue(); + } + } + return null; + } + } + } catch (ClassNotFoundException cnfEx) { + // just use reflection then + } + return null; + } + + public ResolvedType[] getAnnotations(Member onMember, boolean areRuntimeAnnotationsSufficient) { + if (!(onMember instanceof AccessibleObject)) { + return ResolvedType.NONE; + } + // If annotations with class level retention are required then we need to open + // open the class file. If only runtime retention annotations are required + // we can just use reflection. + if (!areRuntimeAnnotationsSufficient) { + try { + JavaClass jc = bcelRepository.loadClass(onMember.getDeclaringClass()); + org.aspectj.apache.bcel.classfile.annotation.AnnotationGen[] anns = null; + if (onMember instanceof Method) { + org.aspectj.apache.bcel.classfile.Method bcelMethod = jc.getMethod((Method) onMember); + if (bcelMethod != null) { + anns = bcelMethod.getAnnotations(); + } + } else if (onMember instanceof Constructor) { + org.aspectj.apache.bcel.classfile.Method bcelCons = jc.getMethod((Constructor) onMember); + anns = bcelCons.getAnnotations(); + } else if (onMember instanceof Field) { + org.aspectj.apache.bcel.classfile.Field bcelField = jc.getField((Field) onMember); + anns = bcelField.getAnnotations(); + } + // the answer is cached and we don't want to hold on to memory + bcelRepository.clear(); + if (anns == null || anns.length == 0) { + return ResolvedType.NONE; + } + ResolvedType[] annotationTypes = new ResolvedType[anns.length]; + for (int i = 0; i < anns.length; i++) { + annotationTypes[i] = world.resolve(UnresolvedType.forSignature(anns[i].getTypeSignature())); + } + return annotationTypes; + } catch (ClassNotFoundException cnfEx) { + // just use reflection then + } + } + + AccessibleObject ao = (AccessibleObject) onMember; + Annotation[] anns = ao.getDeclaredAnnotations(); + if (anns.length == 0) { + return ResolvedType.NONE; + } + ResolvedType[] annotationTypes = new ResolvedType[anns.length]; + for (int i = 0; i < anns.length; i++) { + annotationTypes[i] = UnresolvedType.forName(anns[i].annotationType().getName()).resolve(world); + } + return annotationTypes; + } + + public ResolvedType[] getAnnotations(Class forClass, World inWorld) { + // here we really want both the runtime visible AND the class visible + // annotations so we bail out to Bcel and then chuck away the JavaClass so that we + // don't hog memory. + try { + JavaClass jc = bcelRepository.loadClass(forClass); + org.aspectj.apache.bcel.classfile.annotation.AnnotationGen[] anns = jc.getAnnotations(); + bcelRepository.clear(); + if (anns == null) { + return ResolvedType.NONE; + } else { + ResolvedType[] ret = new ResolvedType[anns.length]; + for (int i = 0; i < ret.length; i++) { + ret[i] = inWorld.resolve(UnresolvedType.forSignature(anns[i].getTypeSignature())); + } + return ret; + } + } catch (ClassNotFoundException cnfEx) { + // just use reflection then + } + + Annotation[] classAnnotations = forClass.getAnnotations(); + ResolvedType[] ret = new ResolvedType[classAnnotations.length]; + for (int i = 0; i < classAnnotations.length; i++) { + ret[i] = inWorld.resolve(classAnnotations[i].annotationType().getName()); + } + + return ret; + } + + public String[] getParameterNames(Member forMember) { + if (!(forMember instanceof AccessibleObject)) + return null; + + try { + JavaClass jc = bcelRepository.loadClass(forMember.getDeclaringClass()); + LocalVariableTable lvt = null; + int numVars = 0; + if (forMember instanceof Method) { + org.aspectj.apache.bcel.classfile.Method bcelMethod = jc.getMethod((Method) forMember); + lvt = bcelMethod.getLocalVariableTable(); + numVars = bcelMethod.getArgumentTypes().length; + } else if (forMember instanceof Constructor) { + org.aspectj.apache.bcel.classfile.Method bcelCons = jc.getMethod((Constructor) forMember); + lvt = bcelCons.getLocalVariableTable(); + numVars = bcelCons.getArgumentTypes().length; + } + return getParameterNamesFromLVT(lvt, numVars); + } catch (ClassNotFoundException cnfEx) { + ; // no luck + } + + return null; + } + + private String[] getParameterNamesFromLVT(LocalVariableTable lvt, int numVars) { + if (lvt == null) + return null;// pr222987 - prevent NPE + LocalVariable[] vars = lvt.getLocalVariableTable(); + if (vars.length < numVars) { + // basic error, we can't get the names... + return null; + } + String[] ret = new String[numVars]; + for (int i = 0; i < numVars; i++) { + ret[i] = vars[i + 1].getName(); + } + return ret; + } + + public ResolvedType[][] getParameterAnnotationTypes(Member onMember) { + if (!(onMember instanceof AccessibleObject)) + return NO_PARAMETER_ANNOTATIONS; + // here we really want both the runtime visible AND the class visible + // annotations + // so we bail out to Bcel and then chuck away the JavaClass so that we + // don't hog + // memory. + try { + JavaClass jc = bcelRepository.loadClass(onMember.getDeclaringClass()); + org.aspectj.apache.bcel.classfile.annotation.AnnotationGen[][] anns = null; + if (onMember instanceof Method) { + org.aspectj.apache.bcel.classfile.Method bcelMethod = jc.getMethod((Method) onMember); + if (bcelMethod == null) { + // pr220430 + // System.err.println( + // "Unexpected problem in Java15AnnotationFinder: cannot retrieve annotations on method '" + // + + // onMember.getName()+"' in class '"+jc.getClassName()+"'"); + } else { + anns = bcelMethod.getParameterAnnotations(); + } + } else if (onMember instanceof Constructor) { + org.aspectj.apache.bcel.classfile.Method bcelCons = jc.getMethod((Constructor) onMember); + anns = bcelCons.getParameterAnnotations(); + } else if (onMember instanceof Field) { + // anns = null; + } + // the answer is cached and we don't want to hold on to memory + bcelRepository.clear(); + if (anns == null) + return NO_PARAMETER_ANNOTATIONS; + ResolvedType[][] result = new ResolvedType[anns.length][]; + // CACHING?? + for (int i = 0; i < anns.length; i++) { + if (anns[i] != null) { + result[i] = new ResolvedType[anns[i].length]; + for (int j = 0; j < anns[i].length; j++) { + result[i][j] = world.resolve(UnresolvedType.forSignature(anns[i][j].getTypeSignature())); + } + } + } + return result; + } catch (ClassNotFoundException cnfEx) { + // just use reflection then + } + + // reflection... + AccessibleObject ao = (AccessibleObject) onMember; + Annotation[][] anns = null; + if (onMember instanceof Method) { + anns = ((Method) ao).getParameterAnnotations(); + } else if (onMember instanceof Constructor) { + anns = ((Constructor) ao).getParameterAnnotations(); + } else if (onMember instanceof Field) { + // anns = null; + } + if (anns == null) + return NO_PARAMETER_ANNOTATIONS; + ResolvedType[][] result = new ResolvedType[anns.length][]; + // CACHING?? + for (int i = 0; i < anns.length; i++) { + if (anns[i] != null) { + result[i] = new ResolvedType[anns[i].length]; + for (int j = 0; j < anns[i].length; j++) { + result[i][j] = UnresolvedType.forName(anns[i][j].annotationType().getName()).resolve(world); + } + } + } + return result; + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/reflect/Java15GenericSignatureInformationProvider.java b/weaver/src/main/java/org/aspectj/weaver/reflect/Java15GenericSignatureInformationProvider.java new file mode 100644 index 000000000..c9de9517e --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/reflect/Java15GenericSignatureInformationProvider.java @@ -0,0 +1,103 @@ +/* ******************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.reflect; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Type; + +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.World; + +/** + * Uses Java 1.5 reflection APIs to determine generic signatures + */ +public class Java15GenericSignatureInformationProvider implements + GenericSignatureInformationProvider { + + private final World world; + + public Java15GenericSignatureInformationProvider(World forWorld) { + this.world = forWorld; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.reflect.GenericSignatureInformationProvider#getGenericParameterTypes(org.aspectj.weaver.reflect.ReflectionBasedResolvedMemberImpl) + */ + public UnresolvedType[] getGenericParameterTypes( + ReflectionBasedResolvedMemberImpl resolvedMember) { + JavaLangTypeToResolvedTypeConverter typeConverter = new JavaLangTypeToResolvedTypeConverter(world); + Type[] pTypes = new Type[0]; + Member member = resolvedMember.getMember(); + if (member instanceof Method) { + pTypes = ((Method)member).getGenericParameterTypes(); + } else if (member instanceof Constructor) { + pTypes = ((Constructor)member).getGenericParameterTypes(); + } + return typeConverter.fromTypes(pTypes); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.reflect.GenericSignatureInformationProvider#getGenericReturnType(org.aspectj.weaver.reflect.ReflectionBasedResolvedMemberImpl) + */ + public UnresolvedType getGenericReturnType( + ReflectionBasedResolvedMemberImpl resolvedMember) { + JavaLangTypeToResolvedTypeConverter typeConverter = new JavaLangTypeToResolvedTypeConverter(world); + Member member = resolvedMember.getMember(); + if (member instanceof Field) { + return typeConverter.fromType(((Field)member).getGenericType()); + } else if (member instanceof Method) { + return typeConverter.fromType(((Method)member).getGenericReturnType()); + } else if (member instanceof Constructor) { + return typeConverter.fromType(((Constructor)member).getDeclaringClass()); + } else { + throw new IllegalStateException("unexpected member type: " + member); + } + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.reflect.GenericSignatureInformationProvider#isBridge() + */ + public boolean isBridge(ReflectionBasedResolvedMemberImpl resolvedMember) { + Member member = resolvedMember.getMember(); + if (member instanceof Method) { + return ((Method)member).isBridge(); + } else { + return false; + } + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.reflect.GenericSignatureInformationProvider#isVarArgs() + */ + public boolean isVarArgs(ReflectionBasedResolvedMemberImpl resolvedMember) { + Member member = resolvedMember.getMember(); + if (member instanceof Method) { + return ((Method)member).isVarArgs(); + } else if (member instanceof Constructor) { + return ((Constructor)member).isVarArgs(); + } else { + return false; + } + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.reflect.GenericSignatureInformationProvider#isSynthetic() + */ + public boolean isSynthetic(ReflectionBasedResolvedMemberImpl resolvedMember) { + Member member = resolvedMember.getMember(); + return member.isSynthetic(); + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/reflect/Java15ReflectionBasedReferenceTypeDelegate.java b/weaver/src/main/java/org/aspectj/weaver/reflect/Java15ReflectionBasedReferenceTypeDelegate.java new file mode 100644 index 000000000..6b65ed31e --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/reflect/Java15ReflectionBasedReferenceTypeDelegate.java @@ -0,0 +1,389 @@ +/* ******************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.reflect; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.Iterator; +import java.util.Set; + +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.reflect.AjType; +import org.aspectj.lang.reflect.AjTypeSystem; +import org.aspectj.lang.reflect.Pointcut; +import org.aspectj.weaver.AnnotationAJ; +import org.aspectj.weaver.ReferenceType; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedPointcutDefinition; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.TypeVariable; +import org.aspectj.weaver.TypeVariableReferenceType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.World; +import org.aspectj.weaver.tools.PointcutDesignatorHandler; +import org.aspectj.weaver.tools.PointcutParameter; + +/** + * Provides Java 5 behaviour in reflection based delegates (overriding 1.4 behaviour from superclass where + * appropriate) + * + * @author Adrian Colyer + * @author Andy Clement + */ +public class Java15ReflectionBasedReferenceTypeDelegate extends ReflectionBasedReferenceTypeDelegate { + + private AjType myType; + private ResolvedType[] annotations; + private ResolvedMember[] pointcuts; + private ResolvedMember[] methods; + private ResolvedMember[] fields; + private TypeVariable[] typeVariables; + private ResolvedType superclass; + private ResolvedType[] superInterfaces; + private String genericSignature = null; + private JavaLangTypeToResolvedTypeConverter typeConverter; + private Java15AnnotationFinder annotationFinder = null; + private ArgNameFinder argNameFinder = null; + + public Java15ReflectionBasedReferenceTypeDelegate() { + } + + @Override + public void initialize(ReferenceType aType, Class aClass, ClassLoader classLoader, World aWorld) { + super.initialize(aType, aClass, classLoader, aWorld); + myType = AjTypeSystem.getAjType(aClass); + annotationFinder = new Java15AnnotationFinder(); + argNameFinder = annotationFinder; + annotationFinder.setClassLoader(this.classLoaderReference.getClassLoader()); + annotationFinder.setWorld(aWorld); + this.typeConverter = new JavaLangTypeToResolvedTypeConverter(aWorld); + } + + @Override + public ReferenceType buildGenericType() { + return (ReferenceType) UnresolvedType.forGenericTypeVariables(getResolvedTypeX().getSignature(), getTypeVariables()) + .resolve(getWorld()); + } + + @Override + public AnnotationAJ[] getAnnotations() { + // AMC - we seem not to need to implement this method... + // throw new UnsupportedOperationException( + // "getAnnotations on Java15ReflectionBasedReferenceTypeDelegate is not implemented yet" + // ); + // FIXME is this the right implementation in the reflective case? + return super.getAnnotations(); + } + + @Override + public ResolvedType[] getAnnotationTypes() { + if (annotations == null) { + annotations = annotationFinder.getAnnotations(getBaseClass(), getWorld()); + } + return annotations; + } + + @Override + public boolean hasAnnotations() { + if (annotations == null) { + annotations = annotationFinder.getAnnotations(getBaseClass(), getWorld()); + } + return annotations.length != 0; + } + + @Override + public boolean hasAnnotation(UnresolvedType ofType) { + ResolvedType[] myAnns = getAnnotationTypes(); + ResolvedType toLookFor = ofType.resolve(getWorld()); + for (int i = 0; i < myAnns.length; i++) { + if (myAnns[i] == toLookFor) { + return true; + } + } + return false; + } + + // use the MAP to ensure that any aj-synthetic fields are filtered out + @Override + public ResolvedMember[] getDeclaredFields() { + if (fields == null) { + Field[] reflectFields = this.myType.getDeclaredFields(); + ResolvedMember[] rFields = new ResolvedMember[reflectFields.length]; + for (int i = 0; i < reflectFields.length; i++) { + rFields[i] = createGenericFieldMember(reflectFields[i]); + } + this.fields = rFields; + } + return fields; + } + + @Override + public String getDeclaredGenericSignature() { + if (this.genericSignature == null && isGeneric()) { + // BUG? what the hell is this doing - see testcode in MemberTestCase15.testMemberSignatureCreation() and run it + // off a Reflection World + } + return genericSignature; + } + + @Override + public ResolvedType[] getDeclaredInterfaces() { + if (superInterfaces == null) { + Type[] genericInterfaces = getBaseClass().getGenericInterfaces(); + this.superInterfaces = typeConverter.fromTypes(genericInterfaces); + } + return superInterfaces; + } + + @Override + public ResolvedType getSuperclass() { + // Superclass of object is null + if (superclass == null && getBaseClass() != Object.class) { + Type t = this.getBaseClass().getGenericSuperclass(); + if (t != null) { + superclass = typeConverter.fromType(t); + } + if (t == null) { + // If the superclass is null, return Object - same as bcel does + superclass = getWorld().resolve(UnresolvedType.OBJECT); + } + } + return superclass; + } + + @Override + public TypeVariable[] getTypeVariables() { + TypeVariable[] workInProgressSetOfVariables = getResolvedTypeX().getWorld().getTypeVariablesCurrentlyBeingProcessed( + getBaseClass()); + if (workInProgressSetOfVariables != null) { + return workInProgressSetOfVariables; + } + if (this.typeVariables == null) { + java.lang.reflect.TypeVariable[] tVars = this.getBaseClass().getTypeParameters(); + TypeVariable[] rTypeVariables = new TypeVariable[tVars.length]; + // basic initialization + for (int i = 0; i < tVars.length; i++) { + rTypeVariables[i] = new TypeVariable(tVars[i].getName()); + } + // stash it + this.getResolvedTypeX().getWorld().recordTypeVariablesCurrentlyBeingProcessed(getBaseClass(), rTypeVariables); + // now fill in the details... + for (int i = 0; i < tVars.length; i++) { + TypeVariableReferenceType tvrt = ((TypeVariableReferenceType) typeConverter.fromType(tVars[i])); + TypeVariable tv = tvrt.getTypeVariable(); + rTypeVariables[i].setSuperclass(tv.getSuperclass()); + rTypeVariables[i].setAdditionalInterfaceBounds(tv.getSuperInterfaces()); + rTypeVariables[i].setDeclaringElement(tv.getDeclaringElement()); + rTypeVariables[i].setDeclaringElementKind(tv.getDeclaringElementKind()); + rTypeVariables[i].setRank(tv.getRank()); + } + this.typeVariables = rTypeVariables; + this.getResolvedTypeX().getWorld().forgetTypeVariablesCurrentlyBeingProcessed(getBaseClass()); + } + return this.typeVariables; + } + + // overrides super method since by using the MAP we can filter out advice + // methods that really shouldn't be seen in this list + @Override + public ResolvedMember[] getDeclaredMethods() { + if (methods == null) { + Method[] reflectMethods = this.myType.getDeclaredMethods(); + Constructor[] reflectCons = this.myType.getDeclaredConstructors(); + ResolvedMember[] rMethods = new ResolvedMember[reflectMethods.length + reflectCons.length]; + for (int i = 0; i < reflectMethods.length; i++) { + rMethods[i] = createGenericMethodMember(reflectMethods[i]); + } + for (int i = 0; i < reflectCons.length; i++) { + rMethods[i + reflectMethods.length] = createGenericConstructorMember(reflectCons[i]); + } + this.methods = rMethods; + } + return methods; + } + + /** + * Returns the generic type, regardless of the resolvedType we 'know about' + */ + public ResolvedType getGenericResolvedType() { + ResolvedType rt = getResolvedTypeX(); + if (rt.isParameterizedType() || rt.isRawType()) { + return rt.getGenericType(); + } + return rt; + } + + private ResolvedMember createGenericMethodMember(Method forMethod) { + ReflectionBasedResolvedMemberImpl ret = new ReflectionBasedResolvedMemberImpl(org.aspectj.weaver.Member.METHOD, + getGenericResolvedType(), forMethod.getModifiers(), typeConverter.fromType(forMethod.getReturnType()), + forMethod.getName(), typeConverter.fromTypes(forMethod.getParameterTypes()), typeConverter.fromTypes(forMethod + .getExceptionTypes()), forMethod); + ret.setAnnotationFinder(this.annotationFinder); + ret.setGenericSignatureInformationProvider(new Java15GenericSignatureInformationProvider(this.getWorld())); + return ret; + } + + private ResolvedMember createGenericConstructorMember(Constructor forConstructor) { + ReflectionBasedResolvedMemberImpl ret = new ReflectionBasedResolvedMemberImpl(org.aspectj.weaver.Member.METHOD, + getGenericResolvedType(), forConstructor.getModifiers(), + // to return what BCEL returns the return type is void + UnresolvedType.VOID,// getGenericResolvedType(), + "", typeConverter.fromTypes(forConstructor.getParameterTypes()), typeConverter.fromTypes(forConstructor + .getExceptionTypes()), forConstructor); + ret.setAnnotationFinder(this.annotationFinder); + ret.setGenericSignatureInformationProvider(new Java15GenericSignatureInformationProvider(this.getWorld())); + return ret; + } + + private ResolvedMember createGenericFieldMember(Field forField) { + ReflectionBasedResolvedMemberImpl ret = new ReflectionBasedResolvedMemberImpl(org.aspectj.weaver.Member.FIELD, + getGenericResolvedType(), forField.getModifiers(), typeConverter.fromType(forField.getType()), forField.getName(), + new UnresolvedType[0], forField); + ret.setAnnotationFinder(this.annotationFinder); + ret.setGenericSignatureInformationProvider(new Java15GenericSignatureInformationProvider(this.getWorld())); + return ret; + } + + @Override + public ResolvedMember[] getDeclaredPointcuts() { + if (pointcuts == null) { + Pointcut[] pcs = this.myType.getDeclaredPointcuts(); + pointcuts = new ResolvedMember[pcs.length]; + InternalUseOnlyPointcutParser parser = null; + World world = getWorld(); + if (world instanceof ReflectionWorld) { + parser = new InternalUseOnlyPointcutParser(classLoaderReference.getClassLoader(), (ReflectionWorld) getWorld()); + } else { + parser = new InternalUseOnlyPointcutParser(classLoaderReference.getClassLoader()); + } + Set additionalPointcutHandlers = world.getRegisteredPointcutHandlers(); + for (Iterator handlerIterator = additionalPointcutHandlers.iterator(); handlerIterator.hasNext();) { + PointcutDesignatorHandler handler = (PointcutDesignatorHandler) handlerIterator.next(); + parser.registerPointcutDesignatorHandler(handler); + } + + // phase 1, create legitimate entries in pointcuts[] before we + // attempt to resolve *any* of the pointcuts + // resolution can sometimes cause us to recurse, and this two stage + // process allows us to cope with that + for (int i = 0; i < pcs.length; i++) { + AjType[] ptypes = pcs[i].getParameterTypes(); + UnresolvedType[] weaverPTypes = new UnresolvedType[ptypes.length]; + for (int j = 0; j < weaverPTypes.length; j++) { + weaverPTypes[j] = this.typeConverter.fromType(ptypes[j].getJavaClass()); + } + pointcuts[i] = new DeferredResolvedPointcutDefinition(getResolvedTypeX(), pcs[i].getModifiers(), pcs[i].getName(), + weaverPTypes); + } + // phase 2, now go back round and resolve in-place all of the + // pointcuts + PointcutParameter[][] parameters = new PointcutParameter[pcs.length][]; + for (int i = 0; i < pcs.length; i++) { + AjType[] ptypes = pcs[i].getParameterTypes(); + String[] pnames = pcs[i].getParameterNames(); + if (pnames.length != ptypes.length) { + pnames = tryToDiscoverParameterNames(pcs[i]); + if (pnames == null || (pnames.length != ptypes.length)) { + throw new IllegalStateException("Required parameter names not available when parsing pointcut " + + pcs[i].getName() + " in type " + getResolvedTypeX().getName()); + } + } + parameters[i] = new PointcutParameter[ptypes.length]; + for (int j = 0; j < parameters[i].length; j++) { + parameters[i][j] = parser.createPointcutParameter(pnames[j], ptypes[j].getJavaClass()); + } + String pcExpr = pcs[i].getPointcutExpression().toString(); + org.aspectj.weaver.patterns.Pointcut pc = parser.resolvePointcutExpression(pcExpr, getBaseClass(), parameters[i]); + ((ResolvedPointcutDefinition) pointcuts[i]).setParameterNames(pnames); + ((ResolvedPointcutDefinition) pointcuts[i]).setPointcut(pc); + } + // phase 3, now concretize them all + for (int i = 0; i < pointcuts.length; i++) { + ResolvedPointcutDefinition rpd = (ResolvedPointcutDefinition) pointcuts[i]; + rpd.setPointcut(parser.concretizePointcutExpression(rpd.getPointcut(), getBaseClass(), parameters[i])); + } + } + return pointcuts; + } + + // for @AspectJ pointcuts compiled by javac only... + private String[] tryToDiscoverParameterNames(Pointcut pcut) { + Method[] ms = pcut.getDeclaringType().getJavaClass().getDeclaredMethods(); + for (Method m : ms) { + if (m.getName().equals(pcut.getName())) { + return argNameFinder.getParameterNames(m); + } + } + return null; + } + + @Override + public boolean isAnnotation() { + return getBaseClass().isAnnotation(); + } + + @Override + public boolean isAnnotationStyleAspect() { + return getBaseClass().isAnnotationPresent(Aspect.class); + } + + @Override + public boolean isAnnotationWithRuntimeRetention() { + if (!isAnnotation()) { + return false; + } + if (getBaseClass().isAnnotationPresent(Retention.class)) { + Retention retention = (Retention) getBaseClass().getAnnotation(Retention.class); + RetentionPolicy policy = retention.value(); + return policy == RetentionPolicy.RUNTIME; + } else { + return false; + } + } + + @Override + public boolean isAspect() { + return this.myType.isAspect(); + } + + @Override + public boolean isEnum() { + return getBaseClass().isEnum(); + } + + @Override + public boolean isGeneric() { + // return false; // for now + return getBaseClass().getTypeParameters().length > 0; + } + + @Override + public boolean isAnonymous() { + return this.myClass.isAnonymousClass(); + } + + @Override + public boolean isNested() { + return this.myClass.isMemberClass(); + } + + @Override + public ResolvedType getOuterClass() { + return ReflectionBasedReferenceTypeDelegateFactory.resolveTypeInWorld( + myClass.getEnclosingClass(),world); + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/reflect/JavaLangTypeToResolvedTypeConverter.java b/weaver/src/main/java/org/aspectj/weaver/reflect/JavaLangTypeToResolvedTypeConverter.java new file mode 100644 index 000000000..30983e38f --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/reflect/JavaLangTypeToResolvedTypeConverter.java @@ -0,0 +1,134 @@ +/* ******************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * ******************************************************************/ +package org.aspectj.weaver.reflect; + +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.WildcardType; +import java.util.HashMap; +import java.util.Map; + +import org.aspectj.weaver.BoundedReferenceType; +import org.aspectj.weaver.ReferenceType; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.TypeFactory; +import org.aspectj.weaver.TypeVariable; +import org.aspectj.weaver.TypeVariableReferenceType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.World; + +/** + * Handles the translation of java.lang.reflect.Type objects into AspectJ UnresolvedTypes. + * + * @author Adrian Colyer + */ +public class JavaLangTypeToResolvedTypeConverter { + + // Used to prevent recursion - we record what we are working on and return it if asked again *whilst* working on it + private Map typeVariablesInProgress = new HashMap(); + private final World world; + + public JavaLangTypeToResolvedTypeConverter(World aWorld) { + this.world = aWorld; + } + + private World getWorld() { + return this.world; + } + + public ResolvedType fromType(Type type) { + if (type instanceof Class) { + Class clazz = (Class) type; + String name = clazz.getName(); + /** + * getName() can return: + * + * 1. If this class object represents a reference type that is not an + * array type then the binary name of the class is returned + * 2. If this class object represents a primitive type or void, then + * the name returned is a String equal to the Java language keyword + * corresponding to the primitive type or void. + * 3. If this class object represents a class of arrays, then the internal + * form of the name consists of the name of the element type preceded by + * one or more '[' characters representing the depth of the array nesting. + */ + if (clazz.isArray()) { + UnresolvedType ut = UnresolvedType.forSignature(name.replace('.', '/')); + return getWorld().resolve(ut); + } else { + return getWorld().resolve(name); + } + } else if (type instanceof ParameterizedType) { + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=509327 + // TODO should deal with the ownerType if it set, indicating this is possibly an inner type of a parameterized type + Type ownerType = ((ParameterizedType) type).getOwnerType(); + ParameterizedType parameterizedType = (ParameterizedType) type; + ResolvedType baseType = fromType(parameterizedType.getRawType()); + Type[] typeArguments = parameterizedType.getActualTypeArguments(); + if (baseType.isSimpleType() && typeArguments.length == 0 && ownerType != null) { + // 'type' is an inner type of some outer parameterized type + // For now just return the base type - in future create the parameterized form of the outer + // and use it with the inner. We return the base type to be compatible with what the + // code does that accesses the info from the bytecode (unlike this code which accesses it + // reflectively). + return baseType; + } + ResolvedType[] resolvedTypeArguments = fromTypes(typeArguments); + return TypeFactory.createParameterizedType(baseType, resolvedTypeArguments, getWorld()); + } else if (type instanceof java.lang.reflect.TypeVariable) { + TypeVariableReferenceType inprogressVar = typeVariablesInProgress.get(type); + if (inprogressVar != null) { + return inprogressVar; + } + java.lang.reflect.TypeVariable tv = (java.lang.reflect.TypeVariable) type; + TypeVariable rt_tv = new TypeVariable(tv.getName()); + TypeVariableReferenceType tvrt = new TypeVariableReferenceType(rt_tv, getWorld()); + typeVariablesInProgress.put(type, tvrt); // record what we are working on, for recursion case + Type[] bounds = tv.getBounds(); + ResolvedType[] resBounds = fromTypes(bounds); + ResolvedType upperBound = resBounds[0]; + ResolvedType[] additionalBounds = new ResolvedType[0]; + if (resBounds.length > 1) { + additionalBounds = new ResolvedType[resBounds.length - 1]; + System.arraycopy(resBounds, 1, additionalBounds, 0, additionalBounds.length); + } + rt_tv.setUpperBound(upperBound); + rt_tv.setAdditionalInterfaceBounds(additionalBounds); + typeVariablesInProgress.remove(type); // we have finished working on it + return tvrt; + } else if (type instanceof WildcardType) { + WildcardType wildType = (WildcardType) type; + Type[] lowerBounds = wildType.getLowerBounds(); + Type[] upperBounds = wildType.getUpperBounds(); + ResolvedType bound = null; + boolean isExtends = lowerBounds.length == 0; + if (isExtends) { + bound = fromType(upperBounds[0]); + } else { + bound = fromType(lowerBounds[0]); + } + return new BoundedReferenceType((ReferenceType) bound, isExtends, getWorld()); + } else if (type instanceof GenericArrayType) { + GenericArrayType genericArrayType = (GenericArrayType) type; + Type componentType = genericArrayType.getGenericComponentType(); + return UnresolvedType.makeArray(fromType(componentType), 1).resolve(getWorld()); + } + return ResolvedType.MISSING; + } + + public ResolvedType[] fromTypes(Type[] types) { + ResolvedType[] ret = new ResolvedType[types.length]; + for (int i = 0; i < ret.length; i++) { + ret[i] = fromType(types[i]); + } + return ret; + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/tools/Jdk14Trace.java b/weaver/src/main/java/org/aspectj/weaver/tools/Jdk14Trace.java new file mode 100644 index 000000000..061b0b0a0 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/tools/Jdk14Trace.java @@ -0,0 +1,127 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Matthew Webster - initial implementation + *******************************************************************************/ +package org.aspectj.weaver.tools; + +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.Logger; + +// OPTIMIZE move out for now? check what doc says about using these variants on trace (commons/14) +public class Jdk14Trace extends AbstractTrace { + + private Logger logger; + private String name; + + public Jdk14Trace (Class clazz) { + super(clazz); + this.name = clazz.getName(); + this.logger = Logger.getLogger(name); + } + + public void enter(String methodName, Object thiz, Object[] args) { + if (logger.isLoggable(Level.FINE)) { + logger.entering(name,methodName,formatObj(thiz)); + if (args != null && logger.isLoggable(Level.FINER)) { + logger.entering(name,methodName,formatObjects(args)); + } + } + } + + public void enter(String methodName, Object thiz) { + enter(methodName,thiz,null); + } + + public void exit(String methodName, Object ret) { + if (logger.isLoggable(Level.FINE)) { + logger.exiting(name,methodName,formatObj(ret)); + } + } + + public void exit(String methodName, Throwable th) { + if (logger.isLoggable(Level.FINE)) { + logger.exiting(name,methodName,th); + } + } + + public void exit(String methodName) { + if (logger.isLoggable(Level.FINE)) { + logger.exiting(name,methodName); + } + } + + public void event(String methodName, Object thiz, Object[] args) { + if (logger.isLoggable(Level.FINE)) { + logger.logp(Level.FINER,name,methodName,"EVENT",formatObj(thiz)); + if (args != null && logger.isLoggable(Level.FINER)) { + logger.logp(Level.FINER,name,methodName,"EVENT",formatObjects(args)); + } + } + } + + public void event(String methodName) { + if (logger.isLoggable(Level.FINE)) { + logger.logp(Level.FINER,name,methodName,"EVENT"); + } + } + + public boolean isTraceEnabled() { + return logger.isLoggable(Level.FINER); + } + + public void setTraceEnabled (boolean b) { + if (b) { + logger.setLevel(Level.FINER); + Handler[] handlers = logger.getHandlers(); + if (handlers.length == 0) { + Logger parent = logger.getParent(); + if (parent != null) handlers = parent.getHandlers(); + } + for (int i = 0; i < handlers.length; i++) { + Handler handler = handlers[i]; + handler.setLevel(Level.FINER); + } + } + else { + logger.setLevel(Level.INFO); + } + } + + public void debug (String message) { + if (logger.isLoggable(Level.FINE)) { + logger.fine(message); + } + } + + public void info(String message) { + if (logger.isLoggable(Level.INFO)) { + logger.info(message); + } + } + + public void warn (String message, Throwable th) { + if (logger.isLoggable(Level.WARNING)) { + logger.log(Level.WARNING,message,th); + } + } + + public void error (String message, Throwable th) { + if (logger.isLoggable(Level.SEVERE)) { + logger.log(Level.SEVERE,message,th); + } + } + + public void fatal (String message, Throwable th) { + if (logger.isLoggable(Level.SEVERE)) { + logger.log(Level.SEVERE,message,th); + } + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/tools/Jdk14TraceFactory.java b/weaver/src/main/java/org/aspectj/weaver/tools/Jdk14TraceFactory.java new file mode 100644 index 000000000..4043d1d33 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/tools/Jdk14TraceFactory.java @@ -0,0 +1,20 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Matthew Webster - initial implementation + *******************************************************************************/ +package org.aspectj.weaver.tools; + +public class Jdk14TraceFactory extends TraceFactory { + + @Override + public Trace getTrace(Class clazz) { + return new Jdk14Trace(clazz); + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/tools/WeavingAdaptor.java b/weaver/src/main/java/org/aspectj/weaver/tools/WeavingAdaptor.java new file mode 100644 index 000000000..a02400fb0 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/tools/WeavingAdaptor.java @@ -0,0 +1,951 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM Corporation + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Matthew Webster, Adrian Colyer, John Kew + Lyor Goldstein (caching) + * Martin Lippert initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.tools; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.net.URL; +import java.net.URLClassLoader; +import java.security.ProtectionDomain; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.StringTokenizer; + +import org.aspectj.bridge.AbortException; +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.IMessage.Kind; +import org.aspectj.bridge.IMessageContext; +import org.aspectj.bridge.IMessageHandler; +import org.aspectj.bridge.IMessageHolder; +import org.aspectj.bridge.Message; +import org.aspectj.bridge.MessageHandler; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.bridge.MessageWriter; +import org.aspectj.bridge.Version; +import org.aspectj.bridge.WeaveMessage; +import org.aspectj.util.FileUtil; +import org.aspectj.util.LangUtil; +import org.aspectj.weaver.IClassFileProvider; +import org.aspectj.weaver.IUnwovenClassFile; +import org.aspectj.weaver.IWeaveRequestor; +import org.aspectj.weaver.World; +import org.aspectj.weaver.bcel.BcelObjectType; +import org.aspectj.weaver.bcel.BcelWeaver; +import org.aspectj.weaver.bcel.BcelWorld; +import org.aspectj.weaver.bcel.UnwovenClassFile; +import org.aspectj.weaver.tools.cache.CachedClassEntry; +import org.aspectj.weaver.tools.cache.CachedClassReference; +import org.aspectj.weaver.tools.cache.SimpleCache; +import org.aspectj.weaver.tools.cache.SimpleCacheFactory; +import org.aspectj.weaver.tools.cache.WeavedClassCache; + +// OPTIMIZE add guards for all the debug/info/etc +/** + * This adaptor allows the AspectJ compiler to be embedded in an existing system to facilitate load-time weaving. It provides an + * interface for a weaving class loader to provide a classpath to be woven by a set of aspects. A callback is supplied to allow a + * class loader to define classes generated by the compiler during the weaving process. + *

+ * A weaving class loader should create a WeavingAdaptor before any classes are defined, typically during construction. + * The set of aspects passed to the adaptor is fixed for the lifetime of the adaptor although the classpath can be augmented. A + * system property can be set to allow verbose weaving messages to be written to the console. + * + */ +public class WeavingAdaptor implements IMessageContext { + + /** + * System property used to turn on verbose weaving messages + */ + public static final String WEAVING_ADAPTOR_VERBOSE = "aj.weaving.verbose"; + public static final String SHOW_WEAVE_INFO_PROPERTY = "org.aspectj.weaver.showWeaveInfo"; + public static final String TRACE_MESSAGES_PROPERTY = "org.aspectj.tracing.messages"; + + private final static String ASPECTJ_BASE_PACKAGE = "org.aspectj."; + private final static String PACKAGE_INITIAL_CHARS = ASPECTJ_BASE_PACKAGE.charAt(0) + "sj"; + + private boolean enabled = false; + protected boolean verbose = getVerbose(); + protected BcelWorld bcelWorld; + protected BcelWeaver weaver; + private IMessageHandler messageHandler; + private WeavingAdaptorMessageHolder messageHolder; + private boolean abortOnError = false; + protected GeneratedClassHandler generatedClassHandler; + protected Map generatedClasses = new HashMap(); + public BcelObjectType delegateForCurrentClass; // lazily initialized, should be used to prevent parsing bytecode multiple + // times + protected ProtectionDomain activeProtectionDomain; + + private boolean haveWarnedOnJavax = false; + protected WeavedClassCache cache; + + private int weavingSpecialTypes = 0; + private static final int INITIALIZED = 0x1; + private static final int WEAVE_JAVA_PACKAGE = 0x2; + private static final int WEAVE_JAVAX_PACKAGE = 0x4; + + private static Trace trace = TraceFactory.getTraceFactory().getTrace(WeavingAdaptor.class); + + protected WeavingAdaptor() { + } + + /** + * Construct a WeavingAdaptor with a reference to a weaving class loader. The adaptor will automatically search the class loader + * hierarchy to resolve classes. The adaptor will also search the hierarchy for WeavingClassLoader instances to determine the + * set of aspects to be used for weaving. + * + * @param loader instance of ClassLoader + */ + public WeavingAdaptor(WeavingClassLoader loader) { + // System.err.println("? WeavingAdaptor.(" + loader +"," + aspectURLs.length + ")"); + generatedClassHandler = loader; + init((ClassLoader)loader, getFullClassPath((ClassLoader) loader), getFullAspectPath((ClassLoader) loader/* ,aspectURLs */)); + } + + /** + * Construct a WeavingAdaptor with a reference to a GeneratedClassHandler, a full search path for resolving classes + * and a complete set of aspects. The search path must include classes loaded by the class loader constructing the + * WeavingAdaptor and all its parents in the hierarchy. + * + * @param handler GeneratedClassHandler + * @param classURLs the URLs from which to resolve classes + * @param aspectURLs the aspects used to weave classes defined by this class loader + */ + public WeavingAdaptor(GeneratedClassHandler handler, URL[] classURLs, URL[] aspectURLs) { + // System.err.println("? WeavingAdaptor.()"); + generatedClassHandler = handler; + init(null, FileUtil.makeClasspath(classURLs), FileUtil.makeClasspath(aspectURLs)); + } + + protected List getFullClassPath(ClassLoader loader) { + List list = new LinkedList(); + for (; loader != null; loader = loader.getParent()) { + if (loader instanceof URLClassLoader) { + URL[] urls = ((URLClassLoader) loader).getURLs(); + list.addAll(0, FileUtil.makeClasspath(urls)); + } else { + warn("cannot determine classpath"); + } + } + // On Java9 it is possible to fail to find a URLClassLoader from which to derive a suitable classpath + // For now we can determine it from the java.class.path: + if (LangUtil.is19VMOrGreater()) { + list.add(0, LangUtil.getJrtFsFilePath()); + List javaClassPathEntries = makeClasspath(System.getProperty("java.class.path")); + for (int i=javaClassPathEntries.size()-1;i>=0;i--) { + String javaClassPathEntry = javaClassPathEntries.get(i); + if (!list.contains(javaClassPathEntry)) { + list.add(0,javaClassPathEntry); + } + } + } + // On Java9 the sun.boot.class.path won't be set. System classes accessible through JRT filesystem + list.addAll(0, makeClasspath(System.getProperty("sun.boot.class.path"))); + return list; + } + + private List getFullAspectPath(ClassLoader loader) { + List list = new LinkedList(); + for (; loader != null; loader = loader.getParent()) { + if (loader instanceof WeavingClassLoader) { + URL[] urls = ((WeavingClassLoader) loader).getAspectURLs(); + list.addAll(0, FileUtil.makeClasspath(urls)); + } + } + return list; + } + + private static boolean getVerbose() { + try { + return Boolean.getBoolean(WEAVING_ADAPTOR_VERBOSE); + } catch (Throwable t) { + // security exception + return false; + } + } + + /** + * Initialize the WeavingAdapter + * @param loader ClassLoader used by this adapter; which can be null + * @param classPath classpath of this adapter + * @param aspectPath list of aspect paths + */ + private void init(ClassLoader loader, List classPath, List aspectPath) { + abortOnError = true; + createMessageHandler(); + + info("using classpath: " + classPath); + info("using aspectpath: " + aspectPath); + + bcelWorld = new BcelWorld(classPath, messageHandler, null); + bcelWorld.setXnoInline(false); + bcelWorld.getLint().loadDefaultProperties(); + if (LangUtil.is15VMOrGreater()) { + bcelWorld.setBehaveInJava5Way(true); + } + + weaver = new BcelWeaver(bcelWorld); + registerAspectLibraries(aspectPath); + initializeCache(loader, aspectPath, null, getMessageHandler()); + enabled = true; + } + + /** + * If the cache is enabled, initialize it and swap out the existing classhandler + * for the caching one - + * + * @param loader classloader for this adapter, may be null + * @param aspects List of strings representing aspects managed by the adapter; these could be urls or classnames + * @param existingClassHandler current class handler + * @param myMessageHandler current message handler + */ + protected void initializeCache(ClassLoader loader, List aspects, GeneratedClassHandler existingClassHandler, IMessageHandler myMessageHandler) { + if (WeavedClassCache.isEnabled()) { + cache = WeavedClassCache.createCache(loader, aspects, existingClassHandler, myMessageHandler); + // Wrap the existing class handler so that any generated classes are also cached + if (cache != null) { + this.generatedClassHandler = cache.getCachingClassHandler(); + } + } + } + + + protected void createMessageHandler() { + messageHolder = new WeavingAdaptorMessageHolder(new PrintWriter(System.err)); + messageHandler = messageHolder; + if (verbose) { + messageHandler.dontIgnore(IMessage.INFO); + } + if (Boolean.getBoolean(SHOW_WEAVE_INFO_PROPERTY)) { + messageHandler.dontIgnore(IMessage.WEAVEINFO); + } + info("AspectJ Weaver Version " + Version.text + " built on " + Version.time_text); //$NON-NLS-1$ + } + + protected IMessageHandler getMessageHandler() { + return messageHandler; + } + + public IMessageHolder getMessageHolder() { + return messageHolder; + } + + protected void setMessageHandler(IMessageHandler mh) { + if (mh instanceof ISupportsMessageContext) { + ISupportsMessageContext smc = (ISupportsMessageContext) mh; + smc.setMessageContext(this); + } + if (mh != messageHolder) { + messageHolder.setDelegate(mh); + } + messageHolder.flushMessages(); + } + + protected void disable() { + if (trace.isTraceEnabled()) { + trace.enter("disable", this); + } + + enabled = false; + messageHolder.flushMessages(); + + if (trace.isTraceEnabled()) { + trace.exit("disable"); + } + } + + protected void enable() { + enabled = true; + messageHolder.flushMessages(); + } + + protected boolean isEnabled() { + return enabled; + } + + /** + * Appends URL to path used by the WeavingAdptor to resolve classes + * + * @param url to be appended to search path + */ + public void addURL(URL url) { + File libFile = new File(url.getPath()); + try { + weaver.addLibraryJarFile(libFile); + } catch (IOException ex) { + warn("bad library: '" + libFile + "'"); + } + } + + /** + * Weave a class using aspects previously supplied to the adaptor. + * + * @param name the name of the class + * @param bytes the class bytes + * @return the woven bytes + * @exception IOException weave failed + */ + public byte[] weaveClass(String name, byte[] bytes) throws IOException { + return weaveClass(name, bytes, false); + } + + // Track if the weaver is already running on this thread - don't allow re-entrant calls + private ThreadLocal weaverRunning = new ThreadLocal() { + @Override + protected Boolean initialValue() { + return Boolean.FALSE; + } + }; + + /** + * Weave a class using aspects previously supplied to the adaptor. + * + * @param name the name of the class + * @param bytes the class bytes + * @param mustWeave if true then this class *must* get woven (used for concrete aspects generated from XML) + * @return the woven bytes + * @exception IOException weave failed + */ + public byte[] weaveClass(String name, byte[] bytes, boolean mustWeave) throws IOException { + if (trace == null) { + // Pr231945: we are likely to be under tomcat and ENABLE_CLEAR_REFERENCES hasn't been set + System.err + .println("AspectJ Weaver cannot continue to weave, static state has been cleared. Are you under Tomcat? In order to weave '" + + name + + "' during shutdown, 'org.apache.catalina.loader.WebappClassLoader.ENABLE_CLEAR_REFERENCES=false' must be set (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=231945)."); + return bytes; + } + if (weaverRunning.get()) { + // System.out.println("AJC: avoiding re-entrant call to transform " + name); + return bytes; + } + try { + weaverRunning.set(true); + if (trace.isTraceEnabled()) { + trace.enter("weaveClass", this, new Object[] { name, bytes }); + } + + if (!enabled) { + if (trace.isTraceEnabled()) { + trace.exit("weaveClass", false); + } + return bytes; + } + + boolean debugOn = !messageHandler.isIgnoring(Message.DEBUG); + + try { + delegateForCurrentClass = null; + name = name.replace('/', '.'); + if (couldWeave(name, bytes)) { + if (accept(name, bytes)) { + + // Determine if we have the weaved class cached + CachedClassReference cacheKey = null; + final byte[] original_bytes = bytes; + if (cache != null && !mustWeave) { + cacheKey = cache.createCacheKey(name, original_bytes); + CachedClassEntry entry = cache.get(cacheKey, original_bytes); + if (entry != null) { + // If the entry has been explicitly ignored + // return the original bytes + if (entry.isIgnored()) { + return bytes; + } + return entry.getBytes(); + } + } + + // TODO @AspectJ problem + // Annotation style aspects need to be included regardless in order to get + // a valid aspectOf()/hasAspect() generated in them. However - if they are excluded + // (via include/exclude in aop.xml) they really should only get aspectOf()/hasAspect() + // and not be included in the full set of aspects being applied by 'this' weaver + if (debugOn) { + debug("weaving '" + name + "'"); + } + bytes = getWovenBytes(name, bytes); + // temporarily out - searching for @Aspect annotated types is a slow thing to do - we should + // expect the user to name them if they want them woven - just like code style + // } else if (shouldWeaveAnnotationStyleAspect(name, bytes)) { + // if (mustWeave) { + // if (bcelWorld.getLint().mustWeaveXmlDefinedAspects.isEnabled()) { + // bcelWorld.getLint().mustWeaveXmlDefinedAspects.signal(name, null); + // } + // } + // // an @AspectJ aspect needs to be at least munged by the aspectOf munger + // if (debugOn) { + // debug("weaving '" + name + "'"); + // } + // bytes = getAtAspectJAspectBytes(name, bytes); + + // Add the weaved class to the cache only if there + // has been an actual change + // JVK: Is there a better way to check if the class has + // been transformed without carrying up some value + // from the depths? + if (cacheKey != null) { + // If no transform has been applied, mark the class + // as ignored. + if (Arrays.equals(original_bytes, bytes)) { + cache.ignore(cacheKey, original_bytes); + } else { + cache.put(cacheKey, original_bytes, bytes); + } + } + } else if (debugOn) { + debug("not weaving '" + name + "'"); + } + } else if (debugOn) { + debug("cannot weave '" + name + "'"); + } + } finally { + delegateForCurrentClass = null; + } + + if (trace.isTraceEnabled()) { + trace.exit("weaveClass", bytes); + } + return bytes; + } finally { + weaverRunning.set(false); + } + } + + /** + * @param name + * @return true if even valid to weave: either with an accept check or to munge it for @AspectJ aspectof support + */ + private boolean couldWeave(String name, byte[] bytes) { + return !generatedClasses.containsKey(name) && shouldWeaveName(name); + } + + // ATAJ + protected boolean accept(String name, byte[] bytes) { + return true; + } + + protected boolean shouldDump(String name, boolean before) { + return false; + } + + private boolean shouldWeaveName(String name) { + if (PACKAGE_INITIAL_CHARS.indexOf(name.charAt(0)) != -1) { + if ((weavingSpecialTypes & INITIALIZED) == 0) { + weavingSpecialTypes |= INITIALIZED; + // initialize it + Properties p = weaver.getWorld().getExtraConfiguration(); + if (p != null) { + boolean b = p.getProperty(World.xsetWEAVE_JAVA_PACKAGES, "false").equalsIgnoreCase("true"); + if (b) { + weavingSpecialTypes |= WEAVE_JAVA_PACKAGE; + } + b = p.getProperty(World.xsetWEAVE_JAVAX_PACKAGES, "false").equalsIgnoreCase("true"); + if (b) { + weavingSpecialTypes |= WEAVE_JAVAX_PACKAGE; + } + } + } + if (name.startsWith(ASPECTJ_BASE_PACKAGE)) { + return false; + } + if (name.startsWith("sun.reflect.")) {// JDK reflect + return false; + } + if (name.startsWith("javax.")) { + if ((weavingSpecialTypes & WEAVE_JAVAX_PACKAGE) != 0) { + return true; + } else { + if (!haveWarnedOnJavax) { + haveWarnedOnJavax = true; + warn("javax.* types are not being woven because the weaver option '-Xset:weaveJavaxPackages=true' has not been specified"); + } + return false; + } + } + if (name.startsWith("java.")) { + if ((weavingSpecialTypes & WEAVE_JAVA_PACKAGE) != 0) { + return true; + } else { + return false; + } + } + } + // boolean should = !(name.startsWith("org.aspectj.") + // || (name.startsWith("java.") && (weavingSpecialTypes & WEAVE_JAVA_PACKAGE) == 0) + // || (name.startsWith("javax.") && (weavingSpecialTypes & WEAVE_JAVAX_PACKAGE) == 0) + // // || name.startsWith("$Proxy")//JDK proxies//FIXME AV is that 1.3 proxy ? fe. ataspect.$Proxy0 is a java5 proxy... + // || name.startsWith("sun.reflect.")); + return true; + } + + /** + * We allow @AJ aspect weaving so that we can add aspectOf() as part of the weaving (and not part of the source compilation) + * + * @param name + * @param bytes bytecode (from classloader), allow to NOT lookup stuff on disk again during resolve + * @return true if @Aspect + */ + private boolean shouldWeaveAnnotationStyleAspect(String name, byte[] bytes) { + if (delegateForCurrentClass == null) { + // if (weaver.getWorld().isASMAround()) return asmCheckAnnotationStyleAspect(bytes); + // else + ensureDelegateInitialized(name, bytes); + } + return (delegateForCurrentClass.isAnnotationStyleAspect()); + } + + // private boolean asmCheckAnnotationStyleAspect(byte[] bytes) { + // IsAtAspectAnnotationVisitor detector = new IsAtAspectAnnotationVisitor(); + // + // ClassReader cr = new ClassReader(bytes); + // try { + // cr.accept(detector, true);//, ClassReader.SKIP_DEBUG | ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES); + // } catch (Exception spe) { + // // if anything goes wrong, e.g., an NPE, then assume it's NOT an @AspectJ aspect... + // System.err.println("Unexpected problem parsing bytes to discover @Aspect annotation"); + // spe.printStackTrace(); + // return false; + // } + // + // return detector.isAspect(); + // } + + protected void ensureDelegateInitialized(String name, byte[] bytes) { + if (delegateForCurrentClass == null) { + BcelWorld world = (BcelWorld) weaver.getWorld(); + delegateForCurrentClass = world.addSourceObjectType(name, bytes, false); + } + } + + /** + * Weave a set of bytes defining a class. + * + * @param name the name of the class being woven + * @param bytes the bytes that define the class + * @return byte[] the woven bytes for the class + * @throws IOException + */ + private byte[] getWovenBytes(String name, byte[] bytes) throws IOException { + WeavingClassFileProvider wcp = new WeavingClassFileProvider(name, bytes); + weaver.weave(wcp); + return wcp.getBytes(); + } + + /** + * Weave a set of bytes defining a class for only what is needed to turn @AspectJ aspect in a usefull form ie with aspectOf + * method - see #113587 + * + * @param name the name of the class being woven + * @param bytes the bytes that define the class + * @return byte[] the woven bytes for the class + * @throws IOException + */ + private byte[] getAtAspectJAspectBytes(String name, byte[] bytes) throws IOException { + WeavingClassFileProvider wcp = new WeavingClassFileProvider(name, bytes); + wcp.setApplyAtAspectJMungersOnly(); + weaver.weave(wcp); + return wcp.getBytes(); + } + + private void registerAspectLibraries(List aspectPath) { + // System.err.println("? WeavingAdaptor.registerAspectLibraries(" + aspectPath + ")"); + for (Iterator i = aspectPath.iterator(); i.hasNext();) { + String libName = (String) i.next(); + addAspectLibrary(libName); + } + + weaver.prepareForWeave(); + } + + /* + * Register an aspect library with this classloader for use during weaving. This class loader will also return (unmodified) any + * of the classes in the library in response to a findClass() request. The library is not required to be on the + * weavingClasspath given when this classloader was constructed. + * + * @param aspectLibraryJarFile a jar file representing an aspect library + * + * @throws IOException + */ + private void addAspectLibrary(String aspectLibraryName) { + File aspectLibrary = new File(aspectLibraryName); + if (aspectLibrary.isDirectory() || (FileUtil.isZipFile(aspectLibrary))) { + try { + info("adding aspect library: '" + aspectLibrary + "'"); + weaver.addLibraryJarFile(aspectLibrary); + } catch (IOException ex) { + error("exception adding aspect library: '" + ex + "'"); + } + } else { + error("bad aspect library: '" + aspectLibrary + "'"); + } + } + + private static List makeClasspath(String cp) { + List ret = new ArrayList(); + if (cp != null) { + StringTokenizer tok = new StringTokenizer(cp, File.pathSeparator); + while (tok.hasMoreTokens()) { + ret.add(tok.nextToken()); + } + } + return ret; + } + + protected boolean debug(String message) { + return MessageUtil.debug(messageHandler, message); + } + + protected boolean info(String message) { + return MessageUtil.info(messageHandler, message); + } + + protected boolean warn(String message) { + return MessageUtil.warn(messageHandler, message); + } + + protected boolean warn(String message, Throwable th) { + return messageHandler.handleMessage(new Message(message, IMessage.WARNING, th, null)); + } + + protected boolean error(String message) { + return MessageUtil.error(messageHandler, message); + } + + protected boolean error(String message, Throwable th) { + return messageHandler.handleMessage(new Message(message, IMessage.ERROR, th, null)); + } + + public String getContextId() { + return "WeavingAdaptor"; + } + + /** + * Dump the given bytcode in _dump/... (dev mode) + * + * @param name + * @param b + * @param before whether we are dumping before weaving + * @throws Throwable + */ + protected void dump(String name, byte[] b, boolean before) { + String dirName = getDumpDir(); + + if (before) { + dirName = dirName + File.separator + "_before"; + } + + String className = name.replace('.', '/'); + final File dir; + if (className.indexOf('/') > 0) { + dir = new File(dirName + File.separator + className.substring(0, className.lastIndexOf('/'))); + } else { + dir = new File(dirName); + } + dir.mkdirs(); + String fileName = dirName + File.separator + className + ".class"; + try { + // System.out.println("WeavingAdaptor.dump() fileName=" + new File(fileName).getAbsolutePath()); + FileOutputStream os = new FileOutputStream(fileName); + os.write(b); + os.close(); + } catch (IOException ex) { + warn("unable to dump class " + name + " in directory " + dirName, ex); + } + } + + /** + * @return the directory in which to dump - default is _ajdump but it + */ + protected String getDumpDir() { + return "_ajdump"; + } + + /** + * Processes messages arising from weaver operations. Tell weaver to abort on any message more severe than warning. + */ + protected class WeavingAdaptorMessageHolder extends MessageHandler { + + private IMessageHandler delegate; + private List savedMessages; + + protected boolean traceMessages = Boolean.getBoolean(TRACE_MESSAGES_PROPERTY); + + public WeavingAdaptorMessageHolder(PrintWriter writer) { + + this.delegate = new WeavingAdaptorMessageWriter(writer); + super.dontIgnore(IMessage.WEAVEINFO); + } + + private void traceMessage(IMessage message) { + if (message instanceof WeaveMessage) { + trace.debug(render(message)); + } else if (message.isDebug()) { + trace.debug(render(message)); + } else if (message.isInfo()) { + trace.info(render(message)); + } else if (message.isWarning()) { + trace.warn(render(message), message.getThrown()); + } else if (message.isError()) { + trace.error(render(message), message.getThrown()); + } else if (message.isFailed()) { + trace.fatal(render(message), message.getThrown()); + } else if (message.isAbort()) { + trace.fatal(render(message), message.getThrown()); + } else { + trace.error(render(message), message.getThrown()); + } + } + + protected String render(IMessage message) { + return "[" + getContextId() + "] " + message.toString(); + } + + public void flushMessages() { + if (savedMessages == null) { + savedMessages = new ArrayList(); + savedMessages.addAll(super.getUnmodifiableListView()); + clearMessages(); + for (IMessage message : savedMessages) { + delegate.handleMessage(message); + } + } + // accumulating = false; + // messages.clear(); + } + + public void setDelegate(IMessageHandler messageHandler) { + delegate = messageHandler; + } + + /* + * IMessageHandler + */ + + @Override + public boolean handleMessage(IMessage message) throws AbortException { + if (traceMessages) { + traceMessage(message); + } + + super.handleMessage(message); + + if (abortOnError && 0 <= message.getKind().compareTo(IMessage.ERROR)) { + throw new AbortException(message); + } + // if (accumulating) { + // boolean result = addMessage(message); + // if (abortOnError && 0 <= message.getKind().compareTo(IMessage.ERROR)) { + // throw new AbortException(message); + // } + // return result; + // } + // else return delegate.handleMessage(message); + + if (savedMessages != null) { + delegate.handleMessage(message); + } + return true; + } + + @Override + public boolean isIgnoring(Kind kind) { + return delegate.isIgnoring(kind); + } + + @Override + public void dontIgnore(IMessage.Kind kind) { + if (null != kind && delegate != null) { + delegate.dontIgnore(kind); + } + } + + @Override + public void ignore(Kind kind) { + if (null != kind && delegate != null) { + delegate.ignore(kind); + } + } + + /* + * IMessageHolder + */ + + @Override + public List getUnmodifiableListView() { + // System.err.println("? WeavingAdaptorMessageHolder.getUnmodifiableListView() savedMessages=" + savedMessages); + List allMessages = new ArrayList(); + allMessages.addAll(savedMessages); + allMessages.addAll(super.getUnmodifiableListView()); + return allMessages; + } + } + + protected class WeavingAdaptorMessageWriter extends MessageWriter { + + private final Set ignoring = new HashSet(); + private final IMessage.Kind failKind; + + public WeavingAdaptorMessageWriter(PrintWriter writer) { + super(writer, true); + + ignore(IMessage.WEAVEINFO); + ignore(IMessage.DEBUG); + ignore(IMessage.INFO); + this.failKind = IMessage.ERROR; + } + + @Override + public boolean handleMessage(IMessage message) throws AbortException { + // boolean result = + super.handleMessage(message); + if (abortOnError && 0 <= message.getKind().compareTo(failKind)) { + throw new AbortException(message); + } + return true; + } + + @Override + public boolean isIgnoring(Kind kind) { + return ((null != kind) && (ignoring.contains(kind))); + } + + /** + * Set a message kind to be ignored from now on + */ + @Override + public void ignore(IMessage.Kind kind) { + if ((null != kind) && (!ignoring.contains(kind))) { + ignoring.add(kind); + } + } + + /** + * Remove a message kind from the list of those ignored from now on. + */ + @Override + public void dontIgnore(IMessage.Kind kind) { + if (null != kind) { + ignoring.remove(kind); + } + } + + @Override + protected String render(IMessage message) { + return "[" + getContextId() + "] " + super.render(message); + } + } + + private class WeavingClassFileProvider implements IClassFileProvider { + + private final UnwovenClassFile unwovenClass; + private final List unwovenClasses = new ArrayList(); + private IUnwovenClassFile wovenClass; + private boolean isApplyAtAspectJMungersOnly = false; + + public WeavingClassFileProvider(String name, byte[] bytes) { + ensureDelegateInitialized(name, bytes); + this.unwovenClass = new UnwovenClassFile(name, delegateForCurrentClass.getResolvedTypeX().getName(), bytes); + this.unwovenClasses.add(unwovenClass); + + if (shouldDump(name.replace('/', '.'), true)) { + dump(name, bytes, true); + } + + } + + public void setApplyAtAspectJMungersOnly() { + isApplyAtAspectJMungersOnly = true; + } + + public boolean isApplyAtAspectJMungersOnly() { + return isApplyAtAspectJMungersOnly; + } + + public byte[] getBytes() { + if (wovenClass != null) { + return wovenClass.getBytes(); + } else { + return unwovenClass.getBytes(); + } + } + + public Iterator getClassFileIterator() { + return unwovenClasses.iterator(); + } + + public IWeaveRequestor getRequestor() { + return new IWeaveRequestor() { + + public void acceptResult(IUnwovenClassFile result) { + if (wovenClass == null) { + wovenClass = result; + String name = result.getClassName(); + if (shouldDump(name.replace('/', '.'), false)) { + dump(name, result.getBytes(), false); + } + } else { + // Classes generated by weaver e.g. around closure advice + String className = result.getClassName(); + byte[] resultBytes = result.getBytes(); + + if (SimpleCacheFactory.isEnabled()) { + SimpleCache lacache=SimpleCacheFactory.createSimpleCache(); + lacache.put(result.getClassName(), wovenClass.getBytes(), result.getBytes()); + lacache.addGeneratedClassesNames(wovenClass.getClassName(), wovenClass.getBytes(), result.getClassName()); + } + + generatedClasses.put(className, result); + generatedClasses.put(wovenClass.getClassName(), result); + generatedClassHandler.acceptClass(className, null, resultBytes); + } + } + + public void processingReweavableState() { + } + + public void addingTypeMungers() { + } + + public void weavingAspects() { + } + + public void weavingClasses() { + } + + public void weaveCompleted() { + // ResolvedType.resetPrimitives(); + if (delegateForCurrentClass != null) { + delegateForCurrentClass.weavingCompleted(); + } + // ResolvedType.resetPrimitives(); + // bcelWorld.discardType(typeBeingProcessed.getResolvedTypeX()); // work in progress + } + }; + } + } + + public void setActiveProtectionDomain(ProtectionDomain protectionDomain) { + activeProtectionDomain = protectionDomain; + } +} \ No newline at end of file diff --git a/weaver/src/main/java/org/aspectj/weaver/tools/cache/AbstractCacheBacking.java b/weaver/src/main/java/org/aspectj/weaver/tools/cache/AbstractCacheBacking.java new file mode 100644 index 000000000..649e21d05 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/tools/cache/AbstractCacheBacking.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2012 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * John Kew (vmware) initial implementation + * Lyor Goldstein (vmware) add support for weaved class being re-defined + *******************************************************************************/ +package org.aspectj.weaver.tools.cache; + +import java.util.zip.CRC32; + +import org.aspectj.weaver.tools.Trace; +import org.aspectj.weaver.tools.TraceFactory; + +/** + * Basic "common" {@link CacheBacking} implementation + */ +public abstract class AbstractCacheBacking implements CacheBacking { + protected final Trace logger=TraceFactory.getTraceFactory().getTrace(getClass()); + + protected AbstractCacheBacking () { + super(); + } + + /** + * Calculates CRC32 on the provided bytes + * @param bytes The bytes array - ignored if null/empty + * @return Calculated CRC + * @see {@link CRC32} + */ + public static final long crc (byte[] bytes) { + if ((bytes == null) || (bytes.length <= 0)) { + return 0L; + } + + CRC32 crc32=new CRC32(); + crc32.update(bytes); + return crc32.getValue(); + } +} diff --git a/weaver/src/main/java/org/aspectj/weaver/tools/cache/AbstractFileCacheBacking.java b/weaver/src/main/java/org/aspectj/weaver/tools/cache/AbstractFileCacheBacking.java new file mode 100644 index 000000000..5447c158b --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/tools/cache/AbstractFileCacheBacking.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2012 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * John Kew (vmware) initial implementation + * Lyor Goldstein (vmware) add support for weaved class being re-defined + *******************************************************************************/ +package org.aspectj.weaver.tools.cache; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * Useful "common" functionality for caching to files + */ +public abstract class AbstractFileCacheBacking extends AbstractCacheBacking { + /** + * Default property used to specify a default weaving cache dir location + */ + public static final String WEAVED_CLASS_CACHE_DIR = "aj.weaving.cache.dir"; + private final File cacheDirectory; + + protected AbstractFileCacheBacking (File cacheDirectory) { + if ((this.cacheDirectory=cacheDirectory) == null) { + throw new IllegalStateException("No cache directory specified"); + } + } + + public File getCacheDirectory () { + return cacheDirectory; + } + + protected void writeClassBytes (String key, byte[] bytes) throws Exception { + File dir=getCacheDirectory(), file=new File(dir, key); + FileOutputStream out=new FileOutputStream(file); + try { + out.write(bytes); + } finally { + close(out, file); + } + } + + protected void delete(File file) { + if (file.exists() && (!file.delete())) { + if ((logger != null) && logger.isTraceEnabled()) { + logger.error("Error deleting file " + file.getAbsolutePath()); + } + } + } + + protected void close(OutputStream out, File file) { + if (out != null) { + try { + out.close(); + } catch (IOException e) { + if ((logger != null) && logger.isTraceEnabled()) { + logger.error("Failed (" + e.getClass().getSimpleName() + ")" + + " to close write file " + file.getAbsolutePath() + + ": " + e.getMessage(), e); + } + } + } + } + + protected void close(InputStream in, File file) { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + if ((logger != null) && logger.isTraceEnabled()) { + logger.error("Failed (" + e.getClass().getSimpleName() + ")" + + " to close read file " + file.getAbsolutePath() + + ": " + e.getMessage(), e); + } + } + } + } +} diff --git a/weaver/src/main/java/org/aspectj/weaver/tools/cache/AbstractIndexedFileCacheBacking.java b/weaver/src/main/java/org/aspectj/weaver/tools/cache/AbstractIndexedFileCacheBacking.java new file mode 100644 index 000000000..1580277b1 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/tools/cache/AbstractIndexedFileCacheBacking.java @@ -0,0 +1,262 @@ +/******************************************************************************* + * Copyright (c) 2012 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * John Kew (vmware) initial implementation + * Lyor Goldstein (vmware) add support for weaved class being re-defined + *******************************************************************************/ +package org.aspectj.weaver.tools.cache; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.io.StreamCorruptedException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.Map; +import java.util.TreeMap; + +import org.aspectj.util.LangUtil; + +/** + * Uses an index file to keep track of the cached entries + */ +public abstract class AbstractIndexedFileCacheBacking extends AbstractFileCacheBacking { + /** + * Default name of cache index file - assumed to contain {@link IndexEntry}-s + */ + public static final String INDEX_FILE = "cache.idx"; + protected static final IndexEntry[] EMPTY_INDEX=new IndexEntry[0]; + protected static final String[] EMPTY_KEYS=new String[0]; + + private final File indexFile; + + protected AbstractIndexedFileCacheBacking(File cacheDir) { + super(cacheDir); + + indexFile = new File(cacheDir, INDEX_FILE); + } + + public File getIndexFile () { + return indexFile; + } + + public String[] getKeys(String regex) { + Map index=getIndex(); + if ((index == null) || index.isEmpty()) { + return EMPTY_KEYS; + } + + Collection matches=new LinkedList(); + synchronized(index) { + for (String key : index.keySet()) { + if (key.matches(regex)) { + matches.add(key); + } + } + } + + if (matches.isEmpty()) { + return EMPTY_KEYS; + } else { + return matches.toArray(new String[matches.size()]); + } + } + + protected Map readIndex () { + return readIndex(getCacheDirectory(), getIndexFile()); + } + + protected void writeIndex () { + writeIndex(getIndexFile()); + } + + protected void writeIndex (File file) { + try { + writeIndex(file, getIndex()); + } catch(Exception e) { + if ((logger != null) && logger.isTraceEnabled()) { + logger.warn("writeIndex(" + file + ") " + e.getClass().getSimpleName() + ": " + e.getMessage(), e); + } + } + } + + protected abstract Map getIndex (); + + protected Map readIndex (File cacheDir, File cacheFile) { + Map indexMap=new TreeMap(); + IndexEntry[] idxValues=readIndex(cacheFile); + if (LangUtil.isEmpty(idxValues)) { + if ((logger != null) && logger.isTraceEnabled()) { + logger.debug("readIndex(" + cacheFile + ") no index entries"); + } + return indexMap; + } + + for (IndexEntry ie : idxValues) { + IndexEntry resEntry=resolveIndexMapEntry(cacheDir, ie); + if (resEntry != null) { + indexMap.put(resEntry.key, resEntry); + } else if ((logger != null) && logger.isTraceEnabled()) { + logger.debug("readIndex(" + cacheFile + ") skip " + ie.key); + } + } + + return indexMap; + } + + protected IndexEntry resolveIndexMapEntry (File cacheDir, IndexEntry ie) { + return ie; + } + + public IndexEntry[] readIndex(File indexFile) { + if (!indexFile.canRead()) { + return EMPTY_INDEX; + } + + ObjectInputStream ois = null; + try { + ois = new ObjectInputStream(new FileInputStream(indexFile)); + return (IndexEntry[]) ois.readObject(); + } catch (Exception e) { + if ((logger != null) && logger.isTraceEnabled()) { + logger.error("Failed (" + e.getClass().getSimpleName() + ")" + + " to read index from " + indexFile.getAbsolutePath() + + " : " + e.getMessage(), e); + } + delete(indexFile); + } finally { + close(ois, indexFile); + } + + return EMPTY_INDEX; + } + + protected void writeIndex (File indexFile, Map index) throws IOException { + writeIndex(indexFile, LangUtil.isEmpty(index) ? Collections.emptyList() : index.values()); + } + + protected void writeIndex (File indexFile, IndexEntry ... entries) throws IOException { + writeIndex(indexFile, LangUtil.isEmpty(entries) ? Collections.emptyList() : Arrays.asList(entries)); + } + + protected void writeIndex (File indexFile, Collection entries) throws IOException { + File indexDir=indexFile.getParentFile(); + if ((!indexDir.exists()) && (!indexDir.mkdirs())) { + throw new IOException("Failed to create path to " + indexFile.getAbsolutePath()); + } + + int numEntries=LangUtil.isEmpty(entries) ? 0 : entries.size(); + IndexEntry[] entryValues=(numEntries <= 0) ? null : entries.toArray(new IndexEntry[numEntries]); + // if no entries, simply delete the index file + if (LangUtil.isEmpty(entryValues)) { + if (indexFile.exists() && (!indexFile.delete())) { + throw new StreamCorruptedException("Failed to clean up index file at " + indexFile.getAbsolutePath()); + } + + return; + } + + ObjectOutputStream oos=new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(indexFile), 4096)); + try { + oos.writeObject(entryValues); + } finally { + close(oos, indexFile); + } + } + + public static final IndexEntry createIndexEntry (CachedClassEntry classEntry, byte[] originalBytes) { + if (classEntry == null) { + return null; + } + + IndexEntry indexEntry = new IndexEntry(); + indexEntry.key = classEntry.getKey(); + indexEntry.generated = classEntry.isGenerated(); + indexEntry.ignored = classEntry.isIgnored(); + indexEntry.crcClass = crc(originalBytes); + if (!classEntry.isIgnored()) { + indexEntry.crcWeaved = crc(classEntry.getBytes()); + } + + return indexEntry; + } + + /** + * The default index entry in the index file + */ + public static class IndexEntry implements Serializable, Cloneable { + private static final long serialVersionUID = 756391290557029363L; + + public String key; + public boolean generated; + public boolean ignored; + public long crcClass; + public long crcWeaved; + + public IndexEntry () { + super(); + } + + @Override + public IndexEntry clone () { + try { + return getClass().cast(super.clone()); + } catch(CloneNotSupportedException e) { + throw new RuntimeException("Failed to clone: " + toString() + ": " + e.getMessage(), e); + } + } + + @Override + public int hashCode() { + return (int) (key.hashCode() + + (generated ? 1 : 0) + + (ignored ? 1 : 0) + + crcClass + + crcWeaved); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) + return false; + if (this == obj) + return true; + if (getClass() != obj.getClass()) + return false; + + IndexEntry other=(IndexEntry) obj; + if (this.key.equals(other.key) + && (this.ignored == other.ignored) + && (this.generated == other.generated) + && (this.crcClass == other.crcClass) + && (this.crcWeaved == other.crcWeaved)) { + return true; + } else { + return false; + } + } + + @Override + public String toString() { + return key + + "[" + (generated ? "generated" : "ignored") + "]" + + ";crcClass=0x" + Long.toHexString(crcClass) + + ";crcWeaved=0x" + Long.toHexString(crcWeaved) + ; + } + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/tools/cache/AsynchronousFileCacheBacking.java b/weaver/src/main/java/org/aspectj/weaver/tools/cache/AsynchronousFileCacheBacking.java new file mode 100644 index 000000000..636a82a7a --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/tools/cache/AsynchronousFileCacheBacking.java @@ -0,0 +1,424 @@ +/******************************************************************************* + * Copyright (c) 2012 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Lyor Goldstein (vmware) add support for weaved class being re-defined + *******************************************************************************/ + +package org.aspectj.weaver.tools.cache; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; + +import org.aspectj.util.FileUtil; +import org.aspectj.util.LangUtil; +import org.aspectj.weaver.tools.Trace; +import org.aspectj.weaver.tools.TraceFactory; + +/** + * Uses a background thread to do the actual I/O and for caching "persistence" + * so that the caching works faster on repeated activations of the application. + * The class maintains an in-memory cache, and uses a queue of {@link AsyncCommand}s + * to signal to a background thread various actions required to "synchronize" + * the in-memory cache with the persisted copy. Whenever there is a cache miss + * from the {@link #get(CachedClassReference)} call, the weaver issues a + * {@link #put(CachedClassEntry)} call. This call has 2 side-effects:
+ *

    + *
  • + * The in-memory cache is updated so that subsequent calls to {@link #get(CachedClassReference)} + * will not return the mapped value. + *
  • + * + *
  • + * An "update index" {@link AsyncCommand} is posted to the background + * thread so that the newly mapped value will be persisted (eventually) + *
  • + *
+ * The actual persistence is implemented by the concrete classes + */ +public abstract class AsynchronousFileCacheBacking extends AbstractIndexedFileCacheBacking { + private static final BlockingQueue commandsQ=new LinkedBlockingQueue(); + private static final ExecutorService execService=Executors.newSingleThreadExecutor(); + private static Future commandsRunner; + + protected final Map index, exposedIndex; + protected final Map bytesMap, exposedBytes; + + protected AsynchronousFileCacheBacking (File cacheDir) { + super(cacheDir); + + index = readIndex(cacheDir, getIndexFile()); + exposedIndex = Collections.unmodifiableMap(index); + bytesMap = readClassBytes(index, cacheDir); + exposedBytes = Collections.unmodifiableMap(bytesMap); + } + + @Override + protected Map getIndex() { + return index; + } + + public CachedClassEntry get(CachedClassReference ref, byte[] originalBytes) { + String key=ref.getKey(); + final IndexEntry indexEntry; + synchronized(index) { + if ((indexEntry=index.get(key)) == null) { + return null; + } + } + + if (crc(originalBytes) != indexEntry.crcClass) { + if ((logger != null) && logger.isTraceEnabled()) { + logger.debug("get(" + getCacheDirectory() + ") mismatched original class bytes CRC for " + key); + } + + remove(key); + return null; + } + + if (indexEntry.ignored) { + return new CachedClassEntry(ref, WeavedClassCache.ZERO_BYTES, CachedClassEntry.EntryType.IGNORED); + } + + final byte[] bytes; + synchronized(bytesMap) { + /* + * NOTE: we assume that keys represent classes so if we have their + * bytes they will not be re-created + */ + if ((bytes=bytesMap.remove(key)) == null) { + return null; + } + } + + if (indexEntry.generated) { + return new CachedClassEntry(ref, bytes, CachedClassEntry.EntryType.GENERATED); + } else { + return new CachedClassEntry(ref, bytes, CachedClassEntry.EntryType.WEAVED); + } + } + + public void put(CachedClassEntry entry, byte[] originalBytes) { + String key=entry.getKey(); + byte[] bytes=entry.isIgnored() ? null : entry.getBytes(); + synchronized(index) { + IndexEntry indexEntry=index.get(key); + if (indexEntry != null) { + return; + } + + /* + * Note: we do not cache the class bytes - only send them to + * be saved. The assumption is that the 'put' call was invoked + * because 'get' failed to return any bytes. And since we assume + * that each class bytes are required only once, there is no + * need to cache them + */ + indexEntry = createIndexEntry(entry, originalBytes); + index.put(key, indexEntry); + } + + if (!postCacheCommand(new InsertCommand(this, key, bytes))) { + if ((logger != null) && logger.isTraceEnabled()) { + logger.error("put(" + getCacheDirectory() + ") Failed to post insert command for " + key); + } + } + + if ((logger != null) && logger.isTraceEnabled()) { + logger.debug("put(" + getCacheDirectory() + ")[" + key + "] inserted"); + } + } + + public void remove(CachedClassReference ref) { + remove(ref.getKey()); + } + + protected IndexEntry remove (String key) { + IndexEntry entry; + synchronized(index) { + entry = index.remove(key); + } + + synchronized(bytesMap) { + bytesMap.remove(key); + } + + if (!postCacheCommand(new RemoveCommand(this, key))) { + if ((logger != null) && logger.isTraceEnabled()) { + logger.error("remove(" + getCacheDirectory() + ") Failed to post remove command for " + key); + } + } + + if (entry != null) { + if (!key.equals(entry.key)) { + if ((logger != null) && logger.isTraceEnabled()) { + logger.error("remove(" + getCacheDirectory() + ") Mismatched keys: " + key + " / " + entry.key); + } + } else if ((logger != null) && logger.isTraceEnabled()) { + logger.debug("remove(" + getCacheDirectory() + ")[" + key + "] removed"); + } + } + + return entry; + } + + public List getIndexEntries () { + synchronized(index) { + if (index.isEmpty()) { + return Collections.emptyList(); + } else { + return new ArrayList(index.values()); + } + } + } + + public Map getIndexMap () { + return exposedIndex; + } + + public Map getBytesMap () { + return exposedBytes; + } + + public void clear() { + synchronized(index) { + index.clear(); + } + + if (!postCacheCommand(new ClearCommand(this))) { + if ((logger != null) && logger.isTraceEnabled()) { + logger.error("Failed to post clear command for " + getIndexFile()); + } + } + } + + protected void executeCommand (AsyncCommand cmd) throws Exception { + if (cmd instanceof ClearCommand) { + executeClearCommand(); + } else if (cmd instanceof UpdateIndexCommand) { + executeUpdateIndexCommand(); + } else if (cmd instanceof InsertCommand) { + executeInsertCommand((InsertCommand) cmd); + } else if (cmd instanceof RemoveCommand) { + executeRemoveCommand((RemoveCommand) cmd); + } else { + throw new UnsupportedOperationException("Unknown command: " + cmd); + } + } + + protected void executeClearCommand () throws Exception { + FileUtil.deleteContents(getIndexFile()); + FileUtil.deleteContents(getCacheDirectory()); + } + + protected void executeUpdateIndexCommand () throws Exception { + writeIndex(getIndexFile(), getIndexEntries()); + } + + protected void executeInsertCommand (InsertCommand cmd) throws Exception { + writeIndex(getIndexFile(), getIndexEntries()); + + byte[] bytes=cmd.getClassBytes(); + if (bytes != null) { + writeClassBytes(cmd.getKey(), bytes); + } + } + + protected void executeRemoveCommand (RemoveCommand cmd) throws Exception { + Exception err=null; + try { + removeClassBytes(cmd.getKey()); + } catch(Exception e) { + err = e; + } + + writeIndex(getIndexFile(), getIndexEntries()); + + if (err != null) { + throw err; // check if the class bytes remove had any problems + } + } + + /** + * Helper for {@link #executeRemoveCommand(RemoveCommand)} + * @param key The key representing the class whose bytes are to be removed + * @throws Exception if failed to remove class bytes + */ + protected abstract void removeClassBytes (String key) throws Exception; + + protected abstract Map readClassBytes (Map indexMap, File cacheDir); + + @Override + public String toString() { + return getClass().getSimpleName() + "[" + String.valueOf(getCacheDirectory()) + "]"; + } + + protected static final T createBacking ( + File cacheDir, AsynchronousFileCacheBackingCreator creator) { + final Trace trace=TraceFactory.getTraceFactory().getTrace(AsynchronousFileCacheBacking.class); + if (!cacheDir.exists()) { + if (!cacheDir.mkdirs()) { + if ((trace != null) && trace.isTraceEnabled()) { + trace.error("Unable to create cache directory at " + cacheDir.getAbsolutePath()); + } + return null; + } + } + + if (!cacheDir.canWrite()) { + if ((trace != null) && trace.isTraceEnabled()) { + trace.error("Cache directory is not writable at " + cacheDir.getAbsolutePath()); + } + return null; + } + + // start the service (if needed) only if successfully create the backing instance + T backing=creator.create(cacheDir); + synchronized(execService) { + if (commandsRunner == null) { + commandsRunner = execService.submit(new Runnable() { + @SuppressWarnings("synthetic-access") + public void run() { + for ( ; ; ) { + try { + AsyncCommand cmd=commandsQ.take(); + try { + AsynchronousFileCacheBacking cache=cmd.getCache(); + cache.executeCommand(cmd); + } catch(Exception e) { + if ((trace != null) && trace.isTraceEnabled()) { + trace.error("Failed (" + e.getClass().getSimpleName() + ")" + + " to execute " + cmd + ": " + e.getMessage(), e); + } + } + } catch(InterruptedException e) { + if ((trace != null) && trace.isTraceEnabled()) { + trace.warn("Interrupted"); + } + Thread.currentThread().interrupt(); + break; + } + } + } + }); + } + } + + // fire-up an update-index command in case index was changed by the constructor + if (!postCacheCommand(new UpdateIndexCommand(backing))) { + if ((trace != null) && trace.isTraceEnabled()) { + trace.warn("Failed to offer update index command to " + cacheDir.getAbsolutePath()); + } + } + + return backing; + } + + public static final boolean postCacheCommand (AsyncCommand cmd) { + return commandsQ.offer(cmd); + } + + public static interface AsynchronousFileCacheBackingCreator { + T create (File cacheDir); + } + /** + * Represents an asynchronous command that can be sent to the + * {@link AsynchronousFileCacheBacking} instance to be executed + * on it asynchronously + */ + public static interface AsyncCommand { + /** + * @return The {@link AsynchronousFileCacheBacking} on which + * this command is supposed to be executed + * @see AsynchronousFileCacheBacking#executeCommand(AsyncCommand) + */ + AsynchronousFileCacheBacking getCache (); + } + + public static abstract class AbstractCommand implements AsyncCommand { + private final AsynchronousFileCacheBacking cache; + protected AbstractCommand (AsynchronousFileCacheBacking backing) { + if ((cache=backing) == null) { + throw new IllegalStateException("No backing cache specified"); + } + } + + public final AsynchronousFileCacheBacking getCache () { + return cache; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[" + getCache() + "]"; + } + } + + public static class ClearCommand extends AbstractCommand { + public ClearCommand (AsynchronousFileCacheBacking cache) { + super(cache); + } + } + + public static class UpdateIndexCommand extends AbstractCommand { + public UpdateIndexCommand (AsynchronousFileCacheBacking cache) { + super(cache); + } + } + + /** + * Base class for {@link AbstractCommand}s that refer to a cache key + */ + public static abstract class KeyedCommand extends AbstractCommand { + private final String key; + protected KeyedCommand (AsynchronousFileCacheBacking cache, String keyValue) { + super(cache); + + if (LangUtil.isEmpty(keyValue)) { + throw new IllegalStateException("No key value"); + } + + key = keyValue; + } + + public final String getKey () { + return key; + } + + @Override + public String toString() { + return super.toString() + "[" + getKey() + "]"; + } + } + + public static class RemoveCommand extends KeyedCommand { + public RemoveCommand (AsynchronousFileCacheBacking cache, String keyValue) { + super(cache, keyValue); + } + } + + public static class InsertCommand extends KeyedCommand { + private final byte[] bytes; + + public InsertCommand (AsynchronousFileCacheBacking cache, String keyValue, byte[] classBytes) { + super(cache, keyValue); + bytes = classBytes; + } + + public final byte[] getClassBytes () { + return bytes; + } + } +} diff --git a/weaver/src/main/java/org/aspectj/weaver/tools/cache/CacheBacking.java b/weaver/src/main/java/org/aspectj/weaver/tools/cache/CacheBacking.java new file mode 100644 index 000000000..bcf7c0b60 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/tools/cache/CacheBacking.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2012 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * John Kew (vmware) initial implementation + * Lyor Goldstein (vmware) add support for weaved class being re-defined + *******************************************************************************/ +package org.aspectj.weaver.tools.cache; + +/** + * Interface for the backing to the cache; usually a file, + * but could be an in-memory backing for testing. + *

+ * aspectj and jvmti provide no suitable guarantees + * on locking for class redefinitions, so every implementation + * must have a some locking mechanism to prevent invalid reads. + */ +public interface CacheBacking { + /** + * Return a list of keys which match the given + * regex. + * + * @param regex + * @return + */ + public String[] getKeys(String regex); + + /** + * Remove an entry from the cache + * + * @param ref + */ + public void remove(CachedClassReference ref); + + /** + * Clear the entire cache + */ + public void clear(); + + /** + * Get a cache entry + * + * @param ref entry to retrieve + * @param originalBytes Pre-weaving class bytes - required in order to + * ensure that the cached entry refers to the same original class + * @return the cached bytes or null, if the entry does not exist + */ + public CachedClassEntry get(CachedClassReference ref, byte[] originalBytes); + + /** + * Put an entry in the cache + * + * @param entry key of the entry + * @param originalBytes Pre-weaving class bytes - required in order to + * ensure that the cached entry refers to the same original class + */ + public void put(CachedClassEntry entry, byte[] originalBytes); +} diff --git a/weaver/src/main/java/org/aspectj/weaver/tools/cache/CacheFactory.java b/weaver/src/main/java/org/aspectj/weaver/tools/cache/CacheFactory.java new file mode 100644 index 000000000..042ef61bd --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/tools/cache/CacheFactory.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2012 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * John Kew (vmware) initial implementation + *******************************************************************************/ +package org.aspectj.weaver.tools.cache; + +/** + * Facility for overriding the default CacheKeyResolver + * and CacheBacking; an implementing factory must be set + * on the {@link WeavedClassCache} before the + * {@link org.aspectj.weaver.tools.WeavingAdaptor} is + * configured. + */ +public interface CacheFactory { + CacheKeyResolver createResolver(); + + CacheBacking createBacking(String scope); +} diff --git a/weaver/src/main/java/org/aspectj/weaver/tools/cache/CacheKeyResolver.java b/weaver/src/main/java/org/aspectj/weaver/tools/cache/CacheKeyResolver.java new file mode 100644 index 000000000..8c76ad878 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/tools/cache/CacheKeyResolver.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (c) 2012 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * John Kew (vmware) initial implementation + *******************************************************************************/ + +package org.aspectj.weaver.tools.cache; + +import java.util.List; + +/** + * Interface to allow alternate hashing schemes for weaved and + * generated classes. While the DefaultCacheKeyResolver may be + * a reasonable naive implementation, the management and invalidation + * of the cache may be more usefully accomplished at the Application + * or Container level. + *

+ * The key is not a one-way hash; it must be convertible back to a + * className and must match the regex for the type of key it is + * (generated or weaved). + */ +public interface CacheKeyResolver { + /** + * Create a key for the given className from a class generated by + * the weaver such that: + *

+	 *    className == keyToClass(generatedKey(className)) holds
+	 * and
+	 *    generatedKey(className).matches(getGeneratedRegex()) == true
+	 * 
+ * + * @param className class to create a key for + * @return key for the class, or null if no caching should be performed + */ + CachedClassReference generatedKey(String className); + + /** + * Create a key for the given class name and byte array from the pre-weaved + * class such that + *
+	 *    className == keyToClass(weavedKey(className, various_bytes)) holds
+	 * and
+	 *    weavedKey(className, various_bytes).matches(getWeavedRegex()) == true
+	 * 
+ * + * @param className class to create a key for + * @param original_bytes bytes of the pre-weaved class + * @return key for the class, or null if no caching should be performed + */ + CachedClassReference weavedKey(String className, byte[] original_bytes); + + /** + * Convert a key back to a className + * + * @param key cache key + * @return className + */ + String keyToClass(String key); + + /** + * Create a unique string for the given classpath and aspect list + * + * @param loader Classloader for this adapter + * @param aspects list of aspects; either urls or class names handled by this adapter + * @return scope, or null, if no caching should be performed for this classloader + */ + String createClassLoaderScope(ClassLoader loader, List aspects); + + /** + * Return a regex which matches all generated keys + * + * @return string regex + */ + String getGeneratedRegex(); + + /** + * Return a regex which matches all weaved keys; + * + * @return string regex + */ + String getWeavedRegex(); +} diff --git a/weaver/src/main/java/org/aspectj/weaver/tools/cache/CacheStatistics.java b/weaver/src/main/java/org/aspectj/weaver/tools/cache/CacheStatistics.java new file mode 100644 index 000000000..1ccacfc18 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/tools/cache/CacheStatistics.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2012 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * John Kew (vmware) initial implementation + *******************************************************************************/ + +package org.aspectj.weaver.tools.cache; + +/** + * Maintains some basic statistics on the class cache. + */ +public class CacheStatistics { + private volatile int hits; + private volatile int misses; + private volatile int weaved; + private volatile int generated; + private volatile int ignored; + private volatile int puts; + private volatile int puts_ignored; + + public void hit() { + hits++; + } + + public void miss() { + misses++; + } + + public void weaved() { + weaved++; + } + + public void generated() { + generated++; + } + + public void ignored() { + ignored++; + } + + public void put() { + puts++; + } + + public void putIgnored() { + puts_ignored++; + } + + + public int getHits() { + return hits; + } + + public int getMisses() { + return misses; + } + + public int getWeaved() { + return weaved; + } + + public int getGenerated() { + return generated; + } + + public int getIgnored() { + return ignored; + } + + public int getPuts() { + return puts; + } + + public int getPutsIgnored() { + return puts_ignored; + } + + + public void reset() { + hits = 0; + misses = 0; + weaved = 0; + generated = 0; + ignored = 0; + puts = 0; + puts_ignored = 0; + } + + @Override + public String toString() { + return "CacheStatistics{" + + "hits=" + hits + + ", misses=" + misses + + ", weaved=" + weaved + + ", generated=" + generated + + ", ignored=" + ignored + + ", puts=" + puts + + ", puts_ignored=" + puts_ignored + + '}'; + } +} diff --git a/weaver/src/main/java/org/aspectj/weaver/tools/cache/CachedClassEntry.java b/weaver/src/main/java/org/aspectj/weaver/tools/cache/CachedClassEntry.java new file mode 100644 index 000000000..db74e7f92 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/tools/cache/CachedClassEntry.java @@ -0,0 +1,90 @@ +/******************************************************************************* + * Copyright (c) 2012 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * John Kew (vmware) initial implementation + * Lyor Goldstein (vmware) add support for weaved class being re-defined + *******************************************************************************/ +package org.aspectj.weaver.tools.cache; + +/** + * Represents a class which has been cached + */ +public class CachedClassEntry { + static enum EntryType { + GENERATED, + WEAVED, + IGNORED, + } + + private final CachedClassReference ref; + private final byte[] weavedBytes; + private final EntryType type; + + public CachedClassEntry(CachedClassReference ref, byte[] weavedBytes, EntryType type) { + this.weavedBytes = weavedBytes; + this.ref = ref; + this.type = type; + } + + public String getClassName() { + return ref.getClassName(); + } + + public byte[] getBytes() { + return weavedBytes; + } + + public String getKey() { + return ref.getKey(); + } + + public boolean isGenerated() { + return type == EntryType.GENERATED; + } + + public boolean isWeaved() { + return type == EntryType.WEAVED; + } + + public boolean isIgnored() { + return type == EntryType.IGNORED; + } + + @Override + public int hashCode() { + return getClassName().hashCode() + + getKey().hashCode() + + type.hashCode() + ; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) + return false; + if (this == obj) + return true; + if (getClass() != obj.getClass()) + return false; + + CachedClassEntry other=(CachedClassEntry) obj; + if (getClassName().equals(other.getClassName()) + && getKey().equals(other.getKey()) + && (type == other.type)) { + return true; + } else { + return false; + } + } + + @Override + public String toString() { + return getClassName() + "[" + type + "]"; + } +} diff --git a/weaver/src/main/java/org/aspectj/weaver/tools/cache/CachedClassReference.java b/weaver/src/main/java/org/aspectj/weaver/tools/cache/CachedClassReference.java new file mode 100644 index 000000000..7d48ff171 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/tools/cache/CachedClassReference.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2012 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * John Kew (vmware) initial implementation + * Lyor Goldstein (vmware) add support for weaved class being re-defined + *******************************************************************************/ + +package org.aspectj.weaver.tools.cache; + +/** + * A typed reference to a cached class entry. The key to any + * cache entry is a simple string, but that string may contain + * some specialized encoding. This class handles all of that + * encoding. + *

+ * External users of the cache should not be able to create these + * objects manually. + */ +public class CachedClassReference { + static enum EntryType { + GENERATED, + WEAVED, + IGNORED, + } + + private final String key; + private final String className; + + protected CachedClassReference(String key, CacheKeyResolver resolver) { + this(key, resolver.keyToClass(key)); + } + + /** + * Protected to allow only the WeavedClassCache initialization rights + * + * @param key encoded key of the class + * @param className the classname + */ + protected CachedClassReference(String key, String className) { + this.key = key; + this.className = className; + } + + public String getKey() { + return key; + } + + public String getClassName() { + return className; + } + + @Override + public int hashCode() { + return getKey().hashCode() + getClassName().hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) + return false; + if (this == obj) + return true; + if (getClass() != obj.getClass()) + return false; + + CachedClassReference other=(CachedClassReference) obj; + if (getKey().equals(other.getKey()) + && getClassName().equals(other.getClassName())) { + return true; + } else { + return false; + } + } + + @Override + public String toString() { + return getClassName() + "[" + getKey() + "]"; + } +} diff --git a/weaver/src/main/java/org/aspectj/weaver/tools/cache/DefaultCacheFactory.java b/weaver/src/main/java/org/aspectj/weaver/tools/cache/DefaultCacheFactory.java new file mode 100644 index 000000000..7eb796e4b --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/tools/cache/DefaultCacheFactory.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2012 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * John Kew (vmware) initial implementation + *******************************************************************************/ + +package org.aspectj.weaver.tools.cache; + +/** + * Default factory for creating the backing and resolving classes. + */ +public class DefaultCacheFactory implements CacheFactory { + public CacheKeyResolver createResolver() { + return new DefaultCacheKeyResolver(); + } + + public CacheBacking createBacking(String scope) { + return DefaultFileCacheBacking.createBacking(scope); + } +} diff --git a/weaver/src/main/java/org/aspectj/weaver/tools/cache/DefaultCacheKeyResolver.java b/weaver/src/main/java/org/aspectj/weaver/tools/cache/DefaultCacheKeyResolver.java new file mode 100644 index 000000000..4951923d3 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/tools/cache/DefaultCacheKeyResolver.java @@ -0,0 +1,117 @@ +/******************************************************************************* + * Copyright (c) 2012 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * John Kew (vmware) initial implementation + *******************************************************************************/ + +package org.aspectj.weaver.tools.cache; + +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.zip.CRC32; + +/** + * Naive default class and classloader hashing implementation useful + * for some multi-classloader environments. + *

+ * This implementation creates classloader scopes of the form:
+ * "ExampleClassLoaderName.[crc hash]" + *

+ * And weaved class keys of the form:
+ * "com.foo.BarClassName.[bytes len][crc].weaved" + *

+ * And generated class keys of the form:
+ * "com.foo.BarClassName$AjClosure.generated + */ +public class DefaultCacheKeyResolver implements CacheKeyResolver { + public static final String GENERATED_SUFFIX = ".generated"; + public static final String WEAVED_SUFFIX = ".weaved"; + + /** + * Create a scope from a set of urls and aspect urls. Creates scope + * of the form "ExampleClassLoaderName.[md5sum]" or + * "ExampleClassLoaderName.[crc]" + * + * @param cl the classloader which uses the cache, can be null + * @param aspects the aspects + * @return a unique string for URLClassloaders, otherwise a non-unique classname + */ + public String createClassLoaderScope(ClassLoader cl, List aspects) { + String name = cl != null ? cl.getClass().getSimpleName() : "unknown"; + + List hashableStrings = new LinkedList(); + StringBuilder hashable = new StringBuilder(256); + + // Add the list of loader urls to the hash list + if (cl != null && cl instanceof URLClassLoader) { + URL[] urls = ((URLClassLoader) cl).getURLs(); + for (int i = 0; i < urls.length; i++) { + hashableStrings.add(urls[i].toString()); + } + } + + hashableStrings.addAll(aspects); + Collections.sort(hashableStrings); + for (Iterator it = hashableStrings.iterator(); it.hasNext(); ) { + String url = it.next(); + hashable.append(url); + } + String hash = null; + byte[] bytes = hashable.toString().getBytes(); + hash = crc(bytes); + + return name + '.' + hash; + } + + private String crc(byte[] input) { + CRC32 crc32 = new CRC32(); + crc32.update(input); + return String.valueOf(crc32.getValue()); + } + + public String getGeneratedRegex() { + return ".*" + GENERATED_SUFFIX; + } + + public String getWeavedRegex() { + return ".*" + WEAVED_SUFFIX; + } + + + /** + * Converts a cache key back to a className + * + * @param key to convert + * @return className, e.g. "com.foo.Bar" + */ + public String keyToClass(String key) { + if (key.endsWith(GENERATED_SUFFIX)) { + return key.replaceAll(GENERATED_SUFFIX + "$", ""); + } + if (key.endsWith(WEAVED_SUFFIX)) { + return key.replaceAll("\\.[^.]+" + WEAVED_SUFFIX, ""); + } + return key; + } + + public CachedClassReference weavedKey(String className, byte[] original_bytes) { + String hash = crc(original_bytes); + return new CachedClassReference(className + "." + hash + WEAVED_SUFFIX, className); + + } + + public CachedClassReference generatedKey(String className) { + return new CachedClassReference(className + GENERATED_SUFFIX, className); + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/tools/cache/DefaultFileCacheBacking.java b/weaver/src/main/java/org/aspectj/weaver/tools/cache/DefaultFileCacheBacking.java new file mode 100644 index 000000000..21543a80d --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/tools/cache/DefaultFileCacheBacking.java @@ -0,0 +1,289 @@ +/******************************************************************************* + * Copyright (c) 2012 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * John Kew (vmware) initial implementation + * Lyor Goldstein (vmware) add support for weaved class being re-defined + *******************************************************************************/ + +package org.aspectj.weaver.tools.cache; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FilenameFilter; +import java.io.OutputStream; +import java.util.Map; + +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.FileUtil; +import org.aspectj.util.LangUtil; + + +/** + * Naive File-Backed Class Cache with no expiry or application + * centric invalidation. + *

+ * Enabled with the system property, "aj.weaving.cache.dir" + * If this system property is not set, no caching will be + * performed. + *

+ * A CRC checksum is stored alongside the class file to verify + * the bytes on read. If for some reason there is an error + * reading either the class or crc file, or if the crc does not + * match the class data the cache entry is deleted. + *

+ * An alternate implementation of this could store the class file + * as a jar/zip directly, which would have the required crc; as + * a first pass however it is somewhat useful to view these files + * in expanded form for debugging. + */ +public class DefaultFileCacheBacking extends AbstractIndexedFileCacheBacking { + private final Map index; + + private static final Object LOCK = new Object(); + + protected DefaultFileCacheBacking(File cacheDir) { + super(cacheDir); + index = readIndex(); + } + + public static final DefaultFileCacheBacking createBacking(File cacheDir) { + if (!cacheDir.exists()) { + if (!cacheDir.mkdirs()) { + MessageUtil.error("Unable to create cache directory at " + cacheDir.getName()); + return null; + } + } else if (!cacheDir.isDirectory()) { + MessageUtil.error("Not a cache directory at " + cacheDir.getName()); + return null; + } + + if (!cacheDir.canWrite()) { + MessageUtil.error("Cache directory is not writable at " + cacheDir.getName()); + return null; + } + return new DefaultFileCacheBacking(cacheDir); + } + + @Override + protected Map getIndex() { + return index; + } + + @Override + protected IndexEntry resolveIndexMapEntry (File cacheDir, IndexEntry ie) { + File cacheEntry = new File(cacheDir, ie.key); + if (ie.ignored || cacheEntry.canRead()) { + return ie; + } else { + return null; + } + } + + private void removeIndexEntry(String key) { + synchronized (LOCK) { + index.remove(key); + writeIndex(); + } + } + + private void addIndexEntry(IndexEntry ie) { + synchronized (LOCK) { + index.put(ie.key, ie); + writeIndex(); + } + } + + @Override + protected Map readIndex() { + synchronized (LOCK) { + return super.readIndex(); + } + } + + @Override + protected void writeIndex() { + synchronized (LOCK) { + super.writeIndex(); + } + } + + public void clear() { + File cacheDir=getCacheDirectory(); + int numDeleted=0; + synchronized (LOCK) { + numDeleted = FileUtil.deleteContents(cacheDir); + } + + if ((numDeleted > 0) && (logger != null) && logger.isTraceEnabled()) { + logger.info("clear(" + cacheDir + ") deleted"); + } + } + + public static CacheBacking createBacking(String scope) { + String cache = System.getProperty(WEAVED_CLASS_CACHE_DIR); + if (cache == null) { + return null; + } + + File cacheDir = new File(cache, scope); + return createBacking(cacheDir); + } + + @Override + public String[] getKeys(final String regex) { + File cacheDirectory = getCacheDirectory(); + File[] files = cacheDirectory.listFiles(new FilenameFilter() { + public boolean accept(File file, String s) { + if (s.matches(regex)) { + return true; + } + return false; + } + }); + if (LangUtil.isEmpty(files)) { + return EMPTY_KEYS; + } + String[] keys = new String[files.length]; + for (int i = 0; i < files.length; i++) { + keys[i] = files[i].getName(); + } + return keys; + } + + public CachedClassEntry get(CachedClassReference ref, byte[] originalBytes) { + File cacheDirectory = getCacheDirectory(); + String refKey=ref.getKey(); + File cacheFile = new File(cacheDirectory, refKey); + IndexEntry ie = index.get(refKey); + if (ie == null) { + // no index, delete + delete(cacheFile); + return null; + } + + // check if original file changed + if (crc(originalBytes) != ie.crcClass) { + delete(cacheFile); + return null; + } + + if (ie.ignored) { + return new CachedClassEntry(ref, WeavedClassCache.ZERO_BYTES, CachedClassEntry.EntryType.IGNORED); + } + + if (cacheFile.canRead()) { + byte[] bytes = read(cacheFile, ie.crcWeaved); + if (bytes != null) { + if (!ie.generated) { + return new CachedClassEntry(ref, bytes, CachedClassEntry.EntryType.WEAVED); + } else { + return new CachedClassEntry(ref, bytes, CachedClassEntry.EntryType.GENERATED); + } + } + } + + return null; + } + + public void put(CachedClassEntry entry, byte[] originalBytes) { + File cacheDirectory = getCacheDirectory(); + String refKey = entry.getKey(); + IndexEntry ie = index.get(refKey); + File cacheFile = new File(cacheDirectory, refKey); + final boolean writeEntryBytes; + // check if original bytes changed or the ignored/generated flags + if ((ie != null) + && ((ie.ignored != entry.isIgnored()) || (ie.generated != entry.isGenerated()) || (crc(originalBytes) != ie.crcClass))) { + delete(cacheFile); + writeEntryBytes = true; + } else { + writeEntryBytes = !cacheFile.exists(); + } + + if (writeEntryBytes) { + ie = createIndexEntry(entry, originalBytes); + if (!entry.isIgnored()) { + ie.crcWeaved = write(cacheFile, entry.getBytes()); + } + addIndexEntry(ie); + } + } + + public void remove(CachedClassReference ref) { + File cacheDirectory = getCacheDirectory(); + String refKey = ref.getKey(); + File cacheFile = new File(cacheDirectory, refKey); + synchronized (LOCK) { + removeIndexEntry(refKey); + delete(cacheFile); + } + } + + @Override + protected void delete(File file) { + synchronized (LOCK) { + super.delete(file); + } + } + + protected byte[] read(File file, long expectedCRC) { + byte[] bytes=null; + synchronized (LOCK) { + FileInputStream fis = null; + try { + fis = new FileInputStream(file); + bytes = FileUtil.readAsByteArray(fis); + } catch (Exception e) { + if ((logger != null) && logger.isTraceEnabled()) { + logger.warn("read(" + file.getAbsolutePath() + ")" + + " failed (" + e.getClass().getSimpleName() + ")" + + " to read contents: " + e.getMessage(), e); + } + } finally { + close(fis, file); + } + + // delete the file if there was an exception reading it or mismatched crc + if ((bytes == null) || (crc(bytes) != expectedCRC)) { + delete(file); + return null; + } + } + + return bytes; + } + + protected long write(File file, byte[] bytes) { + synchronized (LOCK) { + if (file.exists()) { + return -1L; + } + OutputStream out = null; + try { + out = new FileOutputStream(file); + out.write(bytes); + } catch (Exception e) { + if ((logger != null) && logger.isTraceEnabled()) { + logger.warn("write(" + file.getAbsolutePath() + ")" + + " failed (" + e.getClass().getSimpleName() + ")" + + " to write contents: " + e.getMessage(), e); + } + // delete the file if there was an exception writing it + delete(file); + return -1L; + } finally { + close(out, file); + } + + return crc(bytes); + } + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/tools/cache/FlatFileCacheBacking.java b/weaver/src/main/java/org/aspectj/weaver/tools/cache/FlatFileCacheBacking.java new file mode 100644 index 000000000..6de416a80 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/tools/cache/FlatFileCacheBacking.java @@ -0,0 +1,139 @@ +/* ******************************************************************* + * Copyright (c) 2012 VMware, Inc. custard + * + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Lyor Goldstein + * ******************************************************************/ + +package org.aspectj.weaver.tools.cache; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.StreamCorruptedException; +import java.util.Map; +import java.util.TreeMap; + +import org.aspectj.util.FileUtil; +import org.aspectj.util.LangUtil; + +/** + * Uses a "flat" files model to store the cached instrumented classes + * and aspects - i.e., each class/aspect is stored as a separate (binary) + * file. This is a good mechanism when the number of instrumented class is + * relatively small (a few 10's). The reason for it is that scanning a folder + * that has many files in it quickly becomes an I/O bottleneck. Also, some + * O/S-es may impose internal limits on the maximum number of "children" + * a folder node may have. On the other hand, it is much faster (again, for + * small number of instrumented classes) than the ZIP cache since each class/aspect + * is represented by a single file - thus adding/removing/modifying it is easier. + * + * @author Lyor Goldstein + */ +public class FlatFileCacheBacking extends AsynchronousFileCacheBacking { + private static final AsynchronousFileCacheBackingCreator defaultCreator= + new AsynchronousFileCacheBackingCreator() { + public FlatFileCacheBacking create(File cacheDir) { + return new FlatFileCacheBacking(cacheDir); + } + }; + public FlatFileCacheBacking(File cacheDir) { + super(cacheDir); + } + + public static final FlatFileCacheBacking createBacking (File cacheDir) { + return createBacking(cacheDir, defaultCreator); + } + + @Override + protected Map readClassBytes(Map indexMap, File cacheDir) { + return readClassBytes(indexMap, cacheDir.listFiles()); + } + + protected Map readClassBytes (Map indexMap, File[] files) { + Map result=new TreeMap(); + if (LangUtil.isEmpty(files)) { + return result; + } + + for (File file : files) { + if (!file.isFile()) { + continue; // skip sub-directories - we expect flat files + } + + String key=file.getName(); + if (INDEX_FILE.equalsIgnoreCase(key)) { + continue; // skip the index itself if found + } + + IndexEntry entry=indexMap.get(key); + if ((entry == null) || entry.ignored) { // if not in index or ignored then remove it + if ((logger != null) && logger.isTraceEnabled()) { + logger.info("readClassBytes(" + key + ") remove orphan/ignored: " + file.getAbsolutePath()); + } + FileUtil.deleteContents(file); + continue; + } + + try { + byte[] bytes=FileUtil.readAsByteArray(file); + long crc=crc(bytes); + if (crc != entry.crcWeaved) { + throw new StreamCorruptedException("Mismatched CRC - expected=" + entry.crcWeaved + "/got=" + crc); + } + + result.put(key, bytes); + if ((logger != null) && logger.isTraceEnabled()) { + logger.debug("readClassBytes(" + key + ") cached from " + file.getAbsolutePath()); + } + } catch(IOException e) { + if ((logger != null) && logger.isTraceEnabled()) { + logger.error("Failed (" + e.getClass().getSimpleName() + ")" + + " to read bytes from " + file.getAbsolutePath() + + ": " + e.getMessage()); + } + indexMap.remove(key); // no need for the entry if no file - force a re-write of its bytes + FileUtil.deleteContents(file); // assume some kind of corruption + continue; + } + } + + return result; + } + + @Override + protected IndexEntry resolveIndexMapEntry (File cacheDir, IndexEntry ie) { + File cacheEntry = new File(cacheDir, ie.key); + if (ie.ignored || cacheEntry.canRead()) { + return ie; + } else { + return null; + } + } + + @Override + protected void writeClassBytes (String key, byte[] bytes) throws Exception { + File dir=getCacheDirectory(), file=new File(dir, key); + FileOutputStream out=new FileOutputStream(file); + try { + out.write(bytes); + } finally { + out.close(); + } + } + + @Override + protected void removeClassBytes (String key) throws Exception { + File dir=getCacheDirectory(), file=new File(dir, key); + if (file.exists() && (!file.delete())) { + throw new StreamCorruptedException("Failed to delete " + file.getAbsolutePath()); + } + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/tools/cache/GeneratedCachedClassHandler.java b/weaver/src/main/java/org/aspectj/weaver/tools/cache/GeneratedCachedClassHandler.java new file mode 100644 index 000000000..91c332b6b --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/tools/cache/GeneratedCachedClassHandler.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2012 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * John Kew (vmware) initial implementation + * Lyor Goldstein (vmware) add support for weaved class being re-defined + *******************************************************************************/ + +package org.aspectj.weaver.tools.cache; + +import org.aspectj.weaver.tools.GeneratedClassHandler; + +/** + * Handler for generated classes; such as Shadowed closures, etc. This wraps the normal + * generated class handler with caching + */ +public class GeneratedCachedClassHandler implements GeneratedClassHandler { + private final WeavedClassCache cache; + private final GeneratedClassHandler nextGeneratedClassHandler; + + public GeneratedCachedClassHandler(WeavedClassCache cache, GeneratedClassHandler nextHandler) { + this.cache = cache; + this.nextGeneratedClassHandler = nextHandler; + } + + public void acceptClass (String name, byte[] originalBytes, byte[] wovenBytes) { + // The cache expects classNames in dot form + CachedClassReference ref = cache.createGeneratedCacheKey(name.replace('/', '.')); + cache.put(ref, originalBytes, wovenBytes); + if (nextGeneratedClassHandler != null) { + nextGeneratedClassHandler.acceptClass(name, originalBytes, wovenBytes); + } + } +} diff --git a/weaver/src/main/java/org/aspectj/weaver/tools/cache/SimpleCache.java b/weaver/src/main/java/org/aspectj/weaver/tools/cache/SimpleCache.java new file mode 100644 index 000000000..45d718a14 --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/tools/cache/SimpleCache.java @@ -0,0 +1,377 @@ +package org.aspectj.weaver.tools.cache; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.security.ProtectionDomain; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.zip.CRC32; + +import org.aspectj.weaver.Dump; +import org.aspectj.weaver.tools.Trace; +import org.aspectj.weaver.tools.TraceFactory; + + +/******************************************************************************* + * Copyright (c) 2012 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Abraham Nevado (lucierna) initial implementation + ********************************************************************************/ + +public class SimpleCache { + + private static final String SAME_BYTES_STRING = "IDEM"; + private static final byte[] SAME_BYTES = SAME_BYTES_STRING.getBytes(); + + private Map cacheMap; + private boolean enabled = false; + + // cache for generated classes + private Map generatedCache; + private static final String GENERATED_CACHE_SUBFOLDER = "panenka.cache"; + private static final String GENERATED_CACHE_SEPARATOR = ";"; + + public static final String IMPL_NAME = "shared"; + + protected SimpleCache(String folder, boolean enabled) { + this.enabled = enabled; + + cacheMap = Collections.synchronizedMap(StoreableCachingMap.init(folder)); + + if (enabled) { + String generatedCachePath = folder + File.separator + GENERATED_CACHE_SUBFOLDER; + File f = new File (generatedCachePath); + if (!f.exists()){ + f.mkdir(); + } + generatedCache = Collections.synchronizedMap(StoreableCachingMap.init(generatedCachePath,0)); + } + } + + public byte[] getAndInitialize(String classname, byte[] bytes, + ClassLoader loader, ProtectionDomain protectionDomain) { + if (!enabled) { + return null; + } + byte[] res = get(classname, bytes); + + if (Arrays.equals(SAME_BYTES, res)) { + return bytes; + } else { + if (res != null) { + initializeClass(classname, res, loader, protectionDomain); + } + return res; + } + + } + + private byte[] get(String classname, byte bytes[]) { + String key = generateKey(classname, bytes); + + byte[] res = cacheMap.get(key); + return res; + } + + public void put(String classname, byte[] origbytes, byte[] wovenbytes) { + if (!enabled) { + return; + } + + String key = generateKey(classname, origbytes); + + if (Arrays.equals(origbytes, wovenbytes)) { + cacheMap.put(key, SAME_BYTES); + return; + } + cacheMap.put(key, wovenbytes); + } + + private String generateKey(String classname, byte[] bytes) { + CRC32 checksum = new CRC32(); + checksum.update(bytes); + long crc = checksum.getValue(); + classname = classname.replace("/", "."); + return new String(classname + "-" + crc); + + } + + private static class StoreableCachingMap extends HashMap { + private String folder; + private static final String CACHENAMEIDX = "cache.idx"; + + private long lastStored = System.currentTimeMillis(); + private static int DEF_STORING_TIMER = 60000; //ms + private int storingTimer; + + private transient Trace trace; + private void initTrace(){ + trace = TraceFactory.getTraceFactory().getTrace(StoreableCachingMap.class); + } + +// private StoreableCachingMap(String folder) { +// this.folder = folder; +// initTrace(); +// } + + private StoreableCachingMap(String folder, int storingTimer){ + this.folder = folder; + initTrace(); + this.storingTimer = storingTimer; + } + + public static StoreableCachingMap init(String folder) { + return init(folder,DEF_STORING_TIMER); + + } + + public static StoreableCachingMap init(String folder, int storingTimer) { + File file = new File(folder + File.separator + CACHENAMEIDX); + if (file.exists()) { + try { + ObjectInputStream in = new ObjectInputStream( + new FileInputStream(file)); + // Deserialize the object + StoreableCachingMap sm = (StoreableCachingMap) in.readObject(); + sm.initTrace(); + in.close(); + return sm; + } catch (Exception e) { + Trace trace = TraceFactory.getTraceFactory().getTrace(StoreableCachingMap.class); + trace.error("Error reading Storable Cache", e); + } + } + + return new StoreableCachingMap(folder,storingTimer); + + } + + @Override + public Object get(Object obj) { + try { + if (super.containsKey(obj)) { + String path = (String) super.get(obj); + if (path.equals(SAME_BYTES_STRING)) { + return SAME_BYTES; + } + return readFromPath(path); + } else { + return null; + } + } catch (IOException e) { + trace.error("Error reading key:"+obj.toString(),e); + Dump.dumpWithException(e); + } + return null; + } + + @Override + public Object put(Object key, Object value) { + try { + String path = null; + byte[] valueBytes = (byte[]) value; + + if (Arrays.equals(valueBytes, SAME_BYTES)) { + path = SAME_BYTES_STRING; + } else { + path = writeToPath((String) key, valueBytes); + } + Object result = super.put(key, path); + storeMap(); + return result; + } catch (IOException e) { + trace.error("Error inserting in cache: key:"+key.toString() + "; value:"+value.toString(), e); + Dump.dumpWithException(e); + } + return null; + } + + + + public void storeMap() { + long now = System.currentTimeMillis(); + if ((now - lastStored ) < storingTimer){ + return; + } + File file = new File(folder + File.separator + CACHENAMEIDX);; + try { + ObjectOutputStream out = new ObjectOutputStream( + new FileOutputStream(file)); + // Deserialize the object + out.writeObject(this); + out.close(); + lastStored = now; + } catch (Exception e) { + trace.error("Error storing cache; cache file:"+file.getAbsolutePath(), e); + Dump.dumpWithException(e); + } + } + + private byte[] readFromPath(String fullPath) throws IOException { + FileInputStream is = null ; + try{ + is = new FileInputStream(fullPath); + } + catch (FileNotFoundException e){ + //may be caused by a generated class that has been stored in generated cache but not saved at cache folder + System.out.println("FileNotFoundExceptions: The aspectj cache is corrupt. Please clean it and reboot the server. Cache path:"+this.folder ); + e.printStackTrace(); + return null; + } + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + + int nRead; + byte[] data = new byte[16384]; + + while ((nRead = is.read(data, 0, data.length)) != -1) { + buffer.write(data, 0, nRead); + } + + buffer.flush(); + is.close(); + return buffer.toByteArray(); + + } + + private String writeToPath(String key, byte[] bytes) throws IOException { + String fullPath = folder + File.separator + key; + FileOutputStream fos = new FileOutputStream(fullPath); + fos.write(bytes); + fos.flush(); + fos.close(); + return fullPath; + } + + } + + private void initializeClass(String className, byte[] bytes, + ClassLoader loader, ProtectionDomain protectionDomain) { + String[] generatedClassesNames = getGeneratedClassesNames(className,bytes); + + if (generatedClassesNames == null) { + return; + } + for (String generatedClassName : generatedClassesNames) { + + byte[] generatedBytes = get(generatedClassName, bytes); + + if (protectionDomain == null) { + defineClass(loader, generatedClassName, generatedBytes); + } else { + defineClass(loader, generatedClassName, generatedBytes, + protectionDomain); + } + + } + + } + + private String[] getGeneratedClassesNames(String className, byte[] bytes) { + String key = generateKey(className, bytes); + + byte[] readBytes = generatedCache.get(key); + if (readBytes == null) { + return null; + } + String readString = new String(readBytes); + return readString.split(GENERATED_CACHE_SEPARATOR); + } + + public void addGeneratedClassesNames(String parentClassName, byte[] parentBytes, String generatedClassName) { + if (!enabled) { + return; + } + String key = generateKey(parentClassName, parentBytes); + + byte[] storedBytes = generatedCache.get(key); + if (storedBytes == null) { + generatedCache.put(key, generatedClassName.getBytes()); + } else { + String storedClasses = new String(storedBytes); + storedClasses += GENERATED_CACHE_SEPARATOR + generatedClassName; + generatedCache.put(key, storedClasses.getBytes()); + } + } + + private Method defineClassMethod = null; + private Method defineClassWithProtectionDomainMethod = null; + + private void defineClass(ClassLoader loader, String name, byte[] bytes) { + + Object clazz = null; + + try { + if (defineClassMethod == null) { + defineClassMethod = ClassLoader.class.getDeclaredMethod( + "defineClass", new Class[] { String.class, + bytes.getClass(), int.class, int.class }); + } + defineClassMethod.setAccessible(true); + clazz = defineClassMethod.invoke(loader, new Object[] { name, + bytes, new Integer(0), new Integer(bytes.length) }); + } catch (InvocationTargetException e) { + if (e.getTargetException() instanceof LinkageError) { + e.printStackTrace(); + } else { + System.out.println("define generated class failed" + + e.getTargetException()); + } + } catch (Exception e) { + e.printStackTrace(); + Dump.dumpWithException(e); + } + } + + private void defineClass(ClassLoader loader, String name, byte[] bytes, + ProtectionDomain protectionDomain) { + + Object clazz = null; + + try { + // System.out.println(">> Defining with protection domain " + name + + // " pd=" + protectionDomain); + if (defineClassWithProtectionDomainMethod == null) { + defineClassWithProtectionDomainMethod = ClassLoader.class + .getDeclaredMethod("defineClass", new Class[] { + String.class, bytes.getClass(), int.class, + int.class, ProtectionDomain.class }); + } + defineClassWithProtectionDomainMethod.setAccessible(true); + clazz = defineClassWithProtectionDomainMethod.invoke(loader, + new Object[] { name, bytes, Integer.valueOf(0), + new Integer(bytes.length), protectionDomain }); + } catch (InvocationTargetException e) { + if (e.getTargetException() instanceof LinkageError) { + e.printStackTrace(); + // is already defined (happens for X$ajcMightHaveAspect + // interfaces since aspects are reweaved) + // TODO maw I don't think this is OK and + } else { + e.printStackTrace(); + } + }catch (NullPointerException e) { + System.out.println("NullPointerException loading class:"+name+". Probabily caused by a corruput cache. Please clean it and reboot the server"); + } catch (Exception e) { + e.printStackTrace(); + Dump.dumpWithException(e); + } + + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/tools/cache/SimpleCacheFactory.java b/weaver/src/main/java/org/aspectj/weaver/tools/cache/SimpleCacheFactory.java new file mode 100644 index 000000000..49569dd0e --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/tools/cache/SimpleCacheFactory.java @@ -0,0 +1,104 @@ +/******************************************************************************* + * Copyright (c) 2012 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Abraham Nevado (lucierna) initial implementation + ********************************************************************************/ + +package org.aspectj.weaver.tools.cache; + +import java.io.File; + +import org.aspectj.weaver.Dump; + +public class SimpleCacheFactory { + + public static final String CACHE_ENABLED_PROPERTY = "aj.weaving.cache.enabled"; + public static final String CACHE_DIR = "aj.weaving.cache.dir"; + public static final String CACHE_IMPL = "aj.weaving.cache.impl"; + + public static final String PATH_DEFAULT= "/tmp/"; // TODO windows default...? + public static final boolean BYDEFAULT= false; + + + public static String path = PATH_DEFAULT; + public static Boolean enabled = false; + private static boolean determinedIfEnabled = false; + private static SimpleCache lacache=null; + + public static synchronized SimpleCache createSimpleCache(){ + if (lacache==null){ + if (!determinedIfEnabled) { + determineIfEnabled(); + } + + if (!enabled) { + return null; + } + + try { + path = System.getProperty(CACHE_DIR); + if (path == null){ + path = PATH_DEFAULT; + } + + } catch (Throwable t) { + path=PATH_DEFAULT; + t.printStackTrace(); + Dump.dumpWithException(t); + } + File f = new File(path); + if (!f.exists()){ + f.mkdir(); + } + lacache= new SimpleCache(path, enabled); + } + return lacache; + + } + + private static void determineIfEnabled() { + try { + String property = System.getProperty(CACHE_ENABLED_PROPERTY); + if (property == null ){ + enabled = BYDEFAULT; + } + else if (property.equalsIgnoreCase("true")){ + + String impl = System.getProperty(CACHE_IMPL); + if (SimpleCache.IMPL_NAME.equals(impl)){ + enabled = true; + } + else{ + enabled = BYDEFAULT; + } + } + else{ + enabled = BYDEFAULT; + } + + } catch (Throwable t) { + enabled=BYDEFAULT; + System.err.println("Error creating cache"); + t.printStackTrace(); + Dump.dumpWithException(t); + } + determinedIfEnabled = true; + } + + // Should behave ok with two threads going through here, well whoever gets there first will set determinedIfEnabled but only after + // it has set 'enabled' to the right value. + public static boolean isEnabled() { + if (!determinedIfEnabled) { + determineIfEnabled(); + } + return enabled; + } + + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/tools/cache/WeavedClassCache.java b/weaver/src/main/java/org/aspectj/weaver/tools/cache/WeavedClassCache.java new file mode 100644 index 000000000..b281d412f --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/tools/cache/WeavedClassCache.java @@ -0,0 +1,278 @@ +/******************************************************************************* + * Copyright (c) 2012 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * John Kew (vmware) initial implementation + * Lyor Goldstein (vmware) add support for weaved class being re-defined + *******************************************************************************/ + +package org.aspectj.weaver.tools.cache; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.IMessageHandler; +import org.aspectj.bridge.Message; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.weaver.tools.GeneratedClassHandler; + +import java.util.LinkedList; +import java.util.List; + +/** + * Manages a cache of weaved and generated classes similar to Eclipse Equinox, + * except designed to operate across multiple restarts of the JVM and with one + * cache per classloader; allowing URLClassLoaders with the same set of URI + * paths to share the same cache (with the default configuration). + *

+ * To enable the default configuration two system properties must be set: + *

+ *    "-Daj.weaving.cache.enabled=true"
+ *    "-Daj.weaving.cache.dir=/some/directory"
+ * 
+ *

+ * The class cache is often something that application developers or + * containers would like to manage, so there are a few interfaces for overriding the + * default behavior and performing other management functions. + *

+ * {@link CacheBacking}
+ * Provides an interface for implementing a custom backing store + * for the cache; The default implementation in {@link DefaultFileCacheBacking} + * provides a naive file-backed cache. An alternate implementation may ignore + * caching until signaled explicitly by the application, or only cache files + * for a specific duration. This class delegates the locking and synchronization + * requirements to the CacheBacking implementation. + *

+ * {@link CacheKeyResolver}
+ * Provides methods for creating keys from classes to be cached and for + * creating the "scope" of the cache itself for a given classloader and aspect + * list. The default implementation is provided by {@link DefaultCacheKeyResolver} + * but an alternate implementation may want to associate a cache with a particular + * application running underneath a container. + *

+ * This naive cache does not normally invalidate *any* classes; the interfaces above + * must be used to implement more intelligent behavior. Cache invalidation + * problems may occur in at least three scenarios: + *

+ *    1. New aspects are added dynamically somewhere in the classloader hierarchy; affecting
+ *       other classes elsewhere.
+ *    2. Use of declare parent in aspects to change the type hierarchy; if the cache
+ *       has not invalidated the right classes in the type hierarchy aspectj may not
+ *       be reconstruct the class incorrectly.
+ *    3. Similarly to (2), the addition of fields or methods on classes which have
+ *       already been weaved and cached could have inter-type conflicts.
+ * 
+ */ +public class WeavedClassCache { + public static final String WEAVED_CLASS_CACHE_ENABLED = "aj.weaving.cache.enabled"; + public static final String CACHE_IMPL = SimpleCacheFactory.CACHE_IMPL; + private static CacheFactory DEFAULT_FACTORY = new DefaultCacheFactory(); + public static final byte[] ZERO_BYTES = new byte[0]; + private final IMessageHandler messageHandler; + private final GeneratedCachedClassHandler cachingClassHandler; + private final CacheBacking backing; + private final CacheStatistics stats; + private final CacheKeyResolver resolver; + private final String name; + + private static final List cacheRegistry = new LinkedList(); + + protected WeavedClassCache(GeneratedClassHandler existingClassHandler, + IMessageHandler messageHandler, + String name, + CacheBacking backing, + CacheKeyResolver resolver) { + this.resolver = resolver; + this.name = name; + this.backing = backing; + this.messageHandler = messageHandler; + // wrap the existing class handler with a caching version + cachingClassHandler = new GeneratedCachedClassHandler(this, existingClassHandler); + this.stats = new CacheStatistics(); + synchronized (cacheRegistry) { + cacheRegistry.add(this); + } + } + + /** + * Creates a new cache using the resolver and backing returned by the DefaultCacheFactory. + * + * @param loader classloader for this cache + * @param aspects list of aspects used by the WeavingAdapter + * @param existingClassHandler the existing GeneratedClassHandler used by the weaver + * @param messageHandler the existing messageHandler used by the weaver + * @return + */ + public static WeavedClassCache createCache(ClassLoader loader, List aspects, GeneratedClassHandler existingClassHandler, IMessageHandler messageHandler) { + CacheKeyResolver resolver = DEFAULT_FACTORY.createResolver(); + String name = resolver.createClassLoaderScope(loader, aspects); + if (name == null) { + return null; + } + CacheBacking backing = DEFAULT_FACTORY.createBacking(name); + if (backing != null) { + return new WeavedClassCache(existingClassHandler, messageHandler, name, backing, resolver); + } + return null; + } + + public String getName() { + return name; + } + + /** + * The Cache and be extended in two ways, through a specialized CacheKeyResolver and + * a specialized CacheBacking. The default factory used to create these classes can + * be set with this method. Since each weaver will create a cache, this method must be + * called before the weaver is first initialized. + * + * @param factory + */ + public static void setDefaultCacheFactory(CacheFactory factory) { + DEFAULT_FACTORY = factory; + } + + /** + * Created a key for a generated class + * + * @param className ClassName, e.g. "com.foo.Bar" + * @return the cache key, or null if no caching should be performed + */ + public CachedClassReference createGeneratedCacheKey(String className) { + return resolver.generatedKey(className); + } + + /** + * Create a key for a normal weaved class + * + * @param className ClassName, e.g. "com.foo.Bar" + * @param originalBytes Original byte array of the class + * @return a cache key, or null if no caching should be performed + */ + public CachedClassReference createCacheKey(String className, byte[] originalBytes) { + return resolver.weavedKey(className, originalBytes); + } + + /** + * Returns a generated class handler which wraps the handler this cache was initialized + * with; this handler should be used to make sure that generated classes are added + * to the cache + */ + public GeneratedClassHandler getCachingClassHandler() { + return cachingClassHandler; + } + + /** + * Has caching been enabled through the System property, + * WEAVED_CLASS_CACHE_ENABLED + * + * @return true if caching is enabled + */ + public static boolean isEnabled() { + String enabled = System.getProperty(WEAVED_CLASS_CACHE_ENABLED); + String impl = System.getProperty(CACHE_IMPL); + return (enabled != null && (impl == null || !SimpleCache.IMPL_NAME.equalsIgnoreCase(impl) ) ); + } + + /** + * Put a class in the cache + * + * @param ref reference to the entry, as created through createCacheKey + * @param classBytes pre-weaving class bytes + * @param weavedBytes bytes to cache + */ + public void put(CachedClassReference ref, byte[] classBytes, byte[] weavedBytes) { + CachedClassEntry.EntryType type = CachedClassEntry.EntryType.WEAVED; + if (ref.getKey().matches(resolver.getGeneratedRegex())) { + type = CachedClassEntry.EntryType.GENERATED; + } + backing.put(new CachedClassEntry(ref, weavedBytes, type), classBytes); + stats.put(); + } + + /** + * Get a cache value + * + * @param ref reference to the cache entry, created through createCacheKey + * @param classBytes Pre-weaving class bytes - required to ensure that + * cached aspects refer to an unchanged original class + * @return the CacheEntry, or null if no entry exists in the cache + */ + public CachedClassEntry get(CachedClassReference ref, byte[] classBytes) { + CachedClassEntry entry = backing.get(ref, classBytes); + if (entry == null) { + stats.miss(); + } else { + stats.hit(); + if (entry.isGenerated()) stats.generated(); + if (entry.isWeaved()) stats.weaved(); + if (entry.isIgnored()) stats.ignored(); + } + return entry; + } + + /** + * Put a cache entry to indicate that the class should not be + * weaved; the original bytes of the class should be used. + * + * @param ref The cache reference + * @param classBytes The un-weaved class bytes + */ + public void ignore(CachedClassReference ref, byte[] classBytes) { + stats.putIgnored(); + backing.put(new CachedClassEntry(ref, ZERO_BYTES, CachedClassEntry.EntryType.IGNORED), classBytes); + } + + /** + * Invalidate a cache entry + * + * @param ref + */ + public void remove(CachedClassReference ref) { + backing.remove(ref); + } + + /** + * Clear the entire cache + */ + public void clear() { + backing.clear(); + } + + /** + * Get the statistics associated with this cache, or + * null if statistics have not been enabled. + * + * @return + */ + public CacheStatistics getStats() { + return stats; + } + + /** + * Return a list of all WeavedClassCaches which have been initialized + * + * @return + */ + public static List getCaches() { + synchronized (cacheRegistry) { + return new LinkedList(cacheRegistry); + } + } + + protected void error(String message, Throwable th) { + messageHandler.handleMessage(new Message(message, IMessage.ERROR, th, null)); + } + + protected void error(String message) { + MessageUtil.error(messageHandler, message); + } + + protected void info(String message) { + MessageUtil.info(message); + } + +} diff --git a/weaver/src/main/java/org/aspectj/weaver/tools/cache/ZippedFileCacheBacking.java b/weaver/src/main/java/org/aspectj/weaver/tools/cache/ZippedFileCacheBacking.java new file mode 100644 index 000000000..6ee8b728c --- /dev/null +++ b/weaver/src/main/java/org/aspectj/weaver/tools/cache/ZippedFileCacheBacking.java @@ -0,0 +1,321 @@ +/******************************************************************************* + * Copyright (c) 2012 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Lyor Goldstein (vmware) add support for weaved class being re-defined + *******************************************************************************/ + +package org.aspectj.weaver.tools.cache; + +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.StreamCorruptedException; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Map; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; + +import org.aspectj.util.FileUtil; +import org.aspectj.util.LangUtil; + +/** + * Uses a ZIP file to store the instrumented classes/aspects - each one as a + * separate {@link ZipEntry}. This mechanism is suitable for relatively + * large numbers of instrumented classes/aspects (100's and more) since it + * holds all of them in a single (ZIP) file. The down side is that any + * modifications to the cache require re-writing the entire ZIP file. This + * can cause the ZIP file to become corrupted if interrupted in mid-update, + * thus requiring the re-population of the cache on next application activation + * (though the overhead in this case is not prohibitvely high...) + */ +public class ZippedFileCacheBacking extends AsynchronousFileCacheBacking { + public static final String ZIP_FILE = "cache.zip"; + private static final AsynchronousFileCacheBackingCreator defaultCreator= + new AsynchronousFileCacheBackingCreator() { + public ZippedFileCacheBacking create(File cacheDir) { + return new ZippedFileCacheBacking(cacheDir); + } + }; + + private final File zipFile; + public ZippedFileCacheBacking(File cacheDir) { + super(cacheDir); + zipFile = new File(cacheDir, ZIP_FILE); + } + + public File getZipFile () { + return zipFile; + } + + public static final ZippedFileCacheBacking createBacking (File cacheDir) { + return createBacking(cacheDir, defaultCreator); + } + + @Override + protected void writeClassBytes(String key, byte[] bytes) throws Exception { + File outFile=getZipFile(); + Map entriesMap; + try { + entriesMap = readZipClassBytes(outFile); + } catch(Exception e) { + if ((logger != null) && logger.isTraceEnabled()) { + logger.warn("writeClassBytes(" + outFile + ")[" + key + "]" + + " failed (" + e.getClass().getSimpleName() + ")" + + " to read current data: " + e.getMessage(), + e); + } + + FileUtil.deleteContents(outFile); + return; + } + + if (entriesMap.isEmpty()) { + entriesMap = Collections.singletonMap(key, bytes); + } else { + entriesMap.put(key, bytes); + } + + try { + writeZipClassBytes(outFile, entriesMap); + } catch(Exception e) { + if ((logger != null) && logger.isTraceEnabled()) { + logger.warn("writeClassBytes(" + outFile + ")[" + key + "]" + + " failed (" + e.getClass().getSimpleName() + ")" + + " to write updated data: " + e.getMessage(), + e); + } + + FileUtil.deleteContents(outFile); + } + } + + @Override + protected void removeClassBytes(String key) throws Exception { + File outFile=getZipFile(); + Map entriesMap; + try { + entriesMap = readZipClassBytes(outFile); + } catch(Exception e) { + if ((logger != null) && logger.isTraceEnabled()) { + logger.warn("removeClassBytes(" + outFile + ")[" + key + "]" + + " failed (" + e.getClass().getSimpleName() + ")" + + " to read current data: " + e.getMessage(), + e); + } + + FileUtil.deleteContents(outFile); + return; + } + + if (!entriesMap.isEmpty()) { + if (entriesMap.remove(key) == null) { + return; // not in the data file to begin with so nothing to update + } + } + + try { + writeZipClassBytes(outFile, entriesMap); + } catch(Exception e) { + if ((logger != null) && logger.isTraceEnabled()) { + logger.warn("removeClassBytes(" + outFile + ")[" + key + "]" + + " failed (" + e.getClass().getSimpleName() + ")" + + " to write updated data: " + e.getMessage(), + e); + } + + FileUtil.deleteContents(outFile); + } + } + + @Override + protected Map readClassBytes(Map indexMap, File cacheDir) { + File dataFile=new File(cacheDir, ZIP_FILE); + Map entriesMap; + boolean okEntries=true; + try { + entriesMap = readZipClassBytes(dataFile); + } catch(Exception e) { + if ((logger != null) && logger.isTraceEnabled()) { + logger.warn("Failed (" + e.getClass().getSimpleName() + ")" + + " to read zip entries from " + dataFile + + ": " + e.getMessage(), + e); + } + + entriesMap = new TreeMap(); + okEntries = false; + } + + if (!syncClassBytesEntries(dataFile, indexMap, entriesMap)) { + okEntries = false; + } + + if (!okEntries) { + FileUtil.deleteContents(dataFile); + + if (!entriesMap.isEmpty()) { + entriesMap.clear(); + } + } + + syncIndexEntries(dataFile, indexMap, entriesMap); + + return entriesMap; + } + + // remove all non-ignored entries that have no class bytes + protected Collection syncIndexEntries (File dataFile, Map indexMap, Map entriesMap) { + Collection toDelete=null; + for (Map.Entry ie : indexMap.entrySet()) { + String key=ie.getKey(); + IndexEntry indexEntry=ie.getValue(); + if (indexEntry.ignored) { + continue; // ignored entries have no class bytes + } + + if (entriesMap.containsKey(key)) { + continue; + } + + if ((logger != null) && logger.isTraceEnabled()) { + logger.debug("syncIndexEntries(" + dataFile + ")[" + key + "] no class bytes"); + } + + if (toDelete == null) { + toDelete = new TreeSet(); + } + toDelete.add(key); + } + + if (toDelete == null) { + return Collections.emptySet(); + } + + for (String key : toDelete) { + indexMap.remove(key); + } + + return toDelete; + } + + // check if all class bytes entries are valid + protected boolean syncClassBytesEntries (File dataFile, Map indexMap, Map entriesMap) { + boolean okEntries=true; + + for (Map.Entry bytesEntry : entriesMap.entrySet()) { + String key=bytesEntry.getKey(); + IndexEntry indexEntry=indexMap.get(key); + // ignored entries should have no bytes + if ((indexEntry == null) || indexEntry.ignored) { + if ((logger != null) && logger.isTraceEnabled()) { + logger.debug("syncClassBytesEntries(" + dataFile + ")[" + key + "] bad index entry"); + } + + okEntries = false; + continue; + } + + long crc=crc(bytesEntry.getValue()); + if (crc != indexEntry.crcWeaved) { + if ((logger != null) && logger.isTraceEnabled()) { + logger.debug("syncClassBytesEntries(" + dataFile + ")[" + key + "]" + + " mismatched CRC - expected=" + indexEntry.crcWeaved + "/got=" + crc); + } + + indexMap.remove(key); + okEntries = false; + continue; + } + } + + return okEntries; + } + + @Override + protected IndexEntry resolveIndexMapEntry(File cacheDir, IndexEntry ie) { + if (cacheDir.exists()) { + return ie; // we will take care of non-existing index entries in the readClassBytes method + } else { + return null; + } + } + + public static final Map readZipClassBytes (File file) throws IOException { + if (!file.canRead()) { + return Collections.emptyMap(); + } + + Map result=new TreeMap(); + byte[] copyBuf=new byte[4096]; + ByteArrayOutputStream out=new ByteArrayOutputStream(copyBuf.length); + ZipFile zipFile=new ZipFile(file); + try { + for (Enumeration entries=zipFile.entries(); (entries != null) && entries.hasMoreElements(); ) { + ZipEntry e=entries.nextElement(); + String name=e.getName(); + if (LangUtil.isEmpty(name)) { + continue; + } + + out.reset(); + + InputStream zipStream=zipFile.getInputStream(e); + try { + for (int nRead=zipStream.read(copyBuf); nRead != (-1); nRead=zipStream.read(copyBuf)) { + out.write(copyBuf, 0, nRead); + } + } finally { + zipStream.close(); + } + + byte[] data=out.toByteArray(), prev=result.put(name, data); + if (prev != null) { + throw new StreamCorruptedException("Multiple entries for " + name); + } + } + } finally { + zipFile.close(); + } + + return result; + } + + public static final void writeZipClassBytes (File file, Map entriesMap) throws IOException { + if (entriesMap.isEmpty()) { + FileUtil.deleteContents(file); + return; + } + + File zipDir=file.getParentFile(); + if ((!zipDir.exists()) && (!zipDir.mkdirs())) { + throw new IOException("Failed to create path to " + zipDir.getAbsolutePath()); + } + + ZipOutputStream zipOut=new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(file), 4096)); + try { + for (Map.Entry bytesEntry : entriesMap.entrySet()) { + String key=bytesEntry.getKey(); + byte[] bytes=bytesEntry.getValue(); + zipOut.putNextEntry(new ZipEntry(key)); + zipOut.write(bytes); + zipOut.closeEntry(); + } + } finally { + zipOut.close(); + } + } +} diff --git a/weaver/src/main/resources/aspectj_1_5_0.dtd b/weaver/src/main/resources/aspectj_1_5_0.dtd new file mode 100644 index 000000000..314290333 --- /dev/null +++ b/weaver/src/main/resources/aspectj_1_5_0.dtd @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/weaver/src/org/aspectj/weaver/IClassFileProvider.java b/weaver/src/org/aspectj/weaver/IClassFileProvider.java deleted file mode 100644 index 029bba7c6..000000000 --- a/weaver/src/org/aspectj/weaver/IClassFileProvider.java +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * IBM Corporation - initial API and implementation - *******************************************************************************/ -package org.aspectj.weaver; - -import java.util.Iterator; - -import org.aspectj.weaver.bcel.UnwovenClassFile; - -/** - * @author colyer - * - * Clients implementing the IClassFileProvider can have a set of class files under their control woven by a weaver, by - * calling the weave(IClassFileProvider source) method. The contract is that a call to getRequestor().acceptResult() is - * providing a result for the class file most recently returned from the getClassFileIterator(). - */ -public interface IClassFileProvider { - - /** - * Answer an iterator that can be used to iterate over a set of UnwovenClassFiles to be woven. During a weave, this method may - * be called multiple times. - * - * @return iterator over UnwovenClassFiles. - */ - Iterator getClassFileIterator(); - - /** - * The client to which the woven results should be returned. - */ - IWeaveRequestor getRequestor(); - - /** - * @return true if weaver should only do some internal munging as the one needed for @AspectJ aspectOf methods creation - */ - boolean isApplyAtAspectJMungersOnly(); - -} diff --git a/weaver/src/org/aspectj/weaver/bcel/AnnotationAccessFieldVar.java b/weaver/src/org/aspectj/weaver/bcel/AnnotationAccessFieldVar.java deleted file mode 100644 index 60aa84125..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/AnnotationAccessFieldVar.java +++ /dev/null @@ -1,186 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2008 Contributors - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Andy Clement initial implementation - * ******************************************************************/ -package org.aspectj.weaver.bcel; - -import java.util.List; - -import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; -import org.aspectj.apache.bcel.classfile.annotation.ElementValue; -import org.aspectj.apache.bcel.classfile.annotation.EnumElementValue; -import org.aspectj.apache.bcel.classfile.annotation.NameValuePair; -import org.aspectj.apache.bcel.classfile.annotation.SimpleElementValue; -import org.aspectj.apache.bcel.generic.InstructionFactory; -import org.aspectj.apache.bcel.generic.InstructionList; -import org.aspectj.apache.bcel.generic.Type; -import org.aspectj.weaver.AnnotationAJ; -import org.aspectj.weaver.Member; -import org.aspectj.weaver.ResolvedMember; -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.Shadow; -import org.aspectj.weaver.UnresolvedType; - -/** - * An AnnotationAccessVar represents access to a particular annotation, whilst an AnnotationAccessFieldVar represents access to a - * specific field of that annotation. - * - * @author Andy Clement - */ -class AnnotationAccessFieldVar extends BcelVar { - - private AnnotationAccessVar annoAccessor; - private ResolvedType annoFieldOfInterest; - private String name; - private int elementValueType; - - public AnnotationAccessFieldVar(AnnotationAccessVar aav, ResolvedType annoFieldOfInterest, String name) { - super(annoFieldOfInterest, 0); - this.annoAccessor = aav; - this.name = name; - String sig = annoFieldOfInterest.getSignature(); - if (sig.length() == 1) { - switch (sig.charAt(0)) { - case 'I': - elementValueType = ElementValue.PRIMITIVE_INT; - break; - default: - throw new IllegalStateException(sig); - } - } else if (sig.equals("Ljava/lang/String;")) { - elementValueType = ElementValue.STRING; - } else if (annoFieldOfInterest.isEnum()) { - elementValueType = ElementValue.ENUM_CONSTANT; - } else { - throw new IllegalStateException(sig); - } - this.annoFieldOfInterest = annoFieldOfInterest; - } - - @Override - public void appendLoadAndConvert(InstructionList il, InstructionFactory fact, ResolvedType toType) { - // Only possible to do annotation field value extraction at MethodExecution - if (annoAccessor.getKind() != Shadow.MethodExecution) { - return; - } - String annotationOfInterestSignature = annoAccessor.getType().getSignature(); - // So we have an entity that has an annotation on and within it is the value we want - Member holder = annoAccessor.getMember(); - AnnotationAJ[] annos = holder.getAnnotations(); - for (AnnotationAJ anno : annos) { - AnnotationGen annotation = ((BcelAnnotation) anno).getBcelAnnotation(); - boolean foundValueInAnnotationUsage = false; - if (annotation.getTypeSignature().equals(annotationOfInterestSignature)) { - ResolvedMember[] annotationFields = toType.getWorld() - .resolve(UnresolvedType.forSignature(annotation.getTypeSignature())).getDeclaredMethods(); - // Check how many fields there are of the type we are looking for. If >1 then we'll need - // to use the name to choose the right one - int countOfType = 0; - for (ResolvedMember annotationField : annotationFields) { - if (annotationField.getType().equals(annoFieldOfInterest)) { - countOfType++; - } - } - - // this block deals with an annotation that has actual values (i.e. not falling back to default values) - List nvps = annotation.getValues(); - for (NameValuePair nvp : nvps) { - // If multiple of the same type, match by name - if (countOfType > 1) { - if (!nvp.getNameString().equals(name)) { - continue; - } - } - ElementValue o = nvp.getValue(); - if (o.getElementValueType() != elementValueType) { - continue; - } - if (o instanceof EnumElementValue) { - EnumElementValue v = (EnumElementValue) o; - String s = v.getEnumTypeString(); - ResolvedType rt = toType.getWorld().resolve(UnresolvedType.forSignature(s)); - if (rt.equals(toType)) { - il.append(fact.createGetStatic(rt.getName(), v.getEnumValueString(), Type.getType(rt.getSignature()))); - foundValueInAnnotationUsage = true; - } - } else if (o instanceof SimpleElementValue) { - SimpleElementValue v = (SimpleElementValue) o; - switch (v.getElementValueType()) { - case ElementValue.PRIMITIVE_INT: - il.append(fact.createConstant(v.getValueInt())); - foundValueInAnnotationUsage = true; - break; - case ElementValue.STRING: - il.append(fact.createConstant(v.getValueString())); - foundValueInAnnotationUsage = true; - break; - default: - throw new IllegalStateException("NYI: Unsupported annotation value binding for " + o); - } - } - if (foundValueInAnnotationUsage) { - break; - } - } - // this block deals with default values - if (!foundValueInAnnotationUsage) { - for (ResolvedMember annotationField : annotationFields) { - if (countOfType > 1) { - if (!annotationField.getName().equals(name)) { - continue; - } - } - if (!annotationField.getType().getSignature().equals(annoFieldOfInterest.getSignature())) { - continue; - } - if (annotationField.getType().getSignature().equals("I")) { - int ivalue = Integer.parseInt(annotationField.getAnnotationDefaultValue()); - il.append(fact.createConstant(ivalue)); - foundValueInAnnotationUsage = true; - break; - } else if (annotationField.getType().getSignature().equals("Ljava/lang/String;")) { - String svalue = annotationField.getAnnotationDefaultValue(); - il.append(fact.createConstant(svalue)); - foundValueInAnnotationUsage = true; - break; - } else { - String dvalue = annotationField.getAnnotationDefaultValue(); - // form will be LBLAHBLAHBLAH;X where X is the field within X - String typename = dvalue.substring(0, dvalue.lastIndexOf(';') + 1); - String field = dvalue.substring(dvalue.lastIndexOf(';') + 1); - ResolvedType rt = toType.getWorld().resolve(UnresolvedType.forSignature(typename)); - il.append(fact.createGetStatic(rt.getName(), field, Type.getType(rt.getSignature()))); - foundValueInAnnotationUsage = true; - break; - } - } - } - } - if (foundValueInAnnotationUsage) { - break; - } - } - } - - @Override - public void insertLoad(InstructionList il, InstructionFactory fact) { - // Only possible to do annotation field value extraction at - // MethodExecution - if (annoAccessor.getKind() != Shadow.MethodExecution) { - return; - } - appendLoadAndConvert(il, fact, annoFieldOfInterest); - } - - @Override - public String toString() { - return super.toString(); - } -} diff --git a/weaver/src/org/aspectj/weaver/bcel/AnnotationAccessVar.java b/weaver/src/org/aspectj/weaver/bcel/AnnotationAccessVar.java deleted file mode 100644 index 88a67d666..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/AnnotationAccessVar.java +++ /dev/null @@ -1,272 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2005-2008 Contributors - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Andy Clement initial implementation - * ******************************************************************/ - -package org.aspectj.weaver.bcel; - -import org.aspectj.apache.bcel.Constants; -import org.aspectj.apache.bcel.classfile.Field; -import org.aspectj.apache.bcel.generic.Instruction; -import org.aspectj.apache.bcel.generic.InstructionBranch; -import org.aspectj.apache.bcel.generic.InstructionConstants; -import org.aspectj.apache.bcel.generic.InstructionFactory; -import org.aspectj.apache.bcel.generic.InstructionHandle; -import org.aspectj.apache.bcel.generic.InstructionList; -import org.aspectj.apache.bcel.generic.ObjectType; -import org.aspectj.apache.bcel.generic.Type; -import org.aspectj.weaver.Member; -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.Shadow; -import org.aspectj.weaver.Shadow.Kind; -import org.aspectj.weaver.UnresolvedType; -import org.aspectj.weaver.ast.Var; - -/** - * Represents access to an annotation on an element, relating to some kinded pointcut. Depending on the kind of pointcut the element - * might be a field or a method and the code generators in here can retrieve the annotation from the element. - */ -public class AnnotationAccessVar extends BcelVar { - - private BcelShadow shadow; - private Kind kind; // What kind of shadow are we at? - private UnresolvedType containingType; // The type upon which we want to ask for 'member' - private Member member; // Holds the member that has the annotations (for method/field join points) - private boolean isWithin; // implies @within() or @withincode(). If false, that implies @annotation() - - public AnnotationAccessVar(BcelShadow shadow, Kind kind, ResolvedType annotationType, UnresolvedType theTargetIsStoredHere, - Member sig, boolean isWithin) { - super(annotationType, 0); - this.shadow = shadow; - this.kind = kind; - this.containingType = theTargetIsStoredHere; - this.member = sig; - this.isWithin = isWithin; - } - - public Kind getKind() { - return kind; - } - - @Override - public String toString() { - return "AnnotationAccessVar(" + getType() + ")"; - } - - @Override - public Instruction createLoad(InstructionFactory fact) { - throw new IllegalStateException("unimplemented"); - } - - @Override - public Instruction createStore(InstructionFactory fact) { - throw new IllegalStateException("unimplemented"); - } - - @Override - public InstructionList createCopyFrom(InstructionFactory fact, int oldSlot) { - throw new IllegalStateException("unimplemented"); - } - - @Override - public void appendLoad(InstructionList il, InstructionFactory fact) { - il.append(createLoadInstructions(getType(), fact)); - } - - @Override - public void appendLoadAndConvert(InstructionList il, InstructionFactory fact, ResolvedType toType) { - il.append(createLoadInstructions(toType, fact)); - } - - @Override - public void insertLoad(InstructionList il, InstructionFactory fact) { - il.insert(createLoadInstructions(getType(), fact)); - } - - private InstructionList createLoadInstructions(ResolvedType toType, InstructionFactory fact) { - - InstructionList il = new InstructionList(); - - Type jlClass = BcelWorld.makeBcelType(UnresolvedType.JL_CLASS); - Type jlString = BcelWorld.makeBcelType(UnresolvedType.JL_STRING); - Type jlClassArray = BcelWorld.makeBcelType(UnresolvedType.JAVA_LANG_CLASS_ARRAY); - Type jlaAnnotation = BcelWorld.makeBcelType(UnresolvedType.JAVA_LANG_ANNOTATION); - - Instruction pushConstant = fact.createConstant(new ObjectType(toType.getName())); - - if (kind == Shadow.MethodCall || kind == Shadow.MethodExecution || kind == Shadow.PreInitialization - || kind == Shadow.Initialization || kind == Shadow.ConstructorCall || kind == Shadow.ConstructorExecution - || kind == Shadow.AdviceExecution || - // annotations for fieldset/fieldget when an ITD is involved are stored against a METHOD - ((kind == Shadow.FieldGet || kind == Shadow.FieldSet) && member.getKind() == Member.METHOD)) { - - Type jlrMethod = BcelWorld.makeBcelType(UnresolvedType.forSignature("Ljava/lang/reflect/Method;")); - Type jlAnnotation = BcelWorld.makeBcelType(UnresolvedType.forSignature("Ljava/lang/annotation/Annotation;")); - Type[] paramTypes = BcelWorld.makeBcelTypes(member.getParameterTypes()); - - // il.append(fact.createConstant(BcelWorld.makeBcelType(containingType))); - - if (kind == Shadow.MethodCall - || kind == Shadow.MethodExecution - || kind == Shadow.AdviceExecution - || - // annotations for fieldset/fieldget when an ITD is involved are stored against a METHOD - ((kind == Shadow.FieldGet || kind == Shadow.FieldSet) && member.getKind() == Member.METHOD) - || ((kind == Shadow.ConstructorCall || kind == Shadow.ConstructorExecution) && member.getKind() == Member.METHOD)) { - - // Need to look at the cached annotation before going to fetch it again - Field annotationCachingField = shadow.getEnclosingClass().getAnnotationCachingField(shadow, toType, isWithin); - - // Basic idea here is to check if the cached field is null, if it is then initialize it, otherwise use it - il.append(fact.createGetStatic(shadow.getEnclosingClass().getName(), annotationCachingField.getName(), jlAnnotation)); - il.append(InstructionConstants.DUP); - InstructionBranch ifNonNull = InstructionFactory.createBranchInstruction(Constants.IFNONNULL, null); - il.append(ifNonNull); - il.append(InstructionConstants.POP); - il.append(fact.createConstant(BcelWorld.makeBcelType(containingType))); - - il.append(fact.createConstant(member.getName())); - buildArray(il, fact, jlClass, paramTypes, 1); - // OPTIMIZE cache result of getDeclaredMethod? - il.append(fact.createInvoke("java/lang/Class", "getDeclaredMethod", jlrMethod, - new Type[] { jlString, jlClassArray }, Constants.INVOKEVIRTUAL)); - il.append(pushConstant);// fact.createConstant(new ObjectType(toType.getName()))); - il.append(fact.createInvoke("java/lang/reflect/Method", "getAnnotation", jlaAnnotation, new Type[] { jlClass }, - Constants.INVOKEVIRTUAL)); - il.append(InstructionConstants.DUP); - il.append(fact.createPutStatic(shadow.getEnclosingClass().getName(), annotationCachingField.getName(), jlAnnotation)); - InstructionHandle ifNullElse = il.append(InstructionConstants.NOP); - ifNonNull.setTarget(ifNullElse); - - } else { // init/preinit/ctor-call/ctor-exec - il.append(fact.createConstant(BcelWorld.makeBcelType(containingType))); - buildArray(il, fact, jlClass, paramTypes, 1); - Type jlrCtor = BcelWorld.makeBcelType(UnresolvedType.JAVA_LANG_REFLECT_CONSTRUCTOR); - // OPTIMIZE cache result of getDeclaredConstructor and getAnnotation? Might be able to use it again if someone else - // needs the same annotations? - il.append(fact.createInvoke("java/lang/Class", "getDeclaredConstructor", jlrCtor, new Type[] { jlClassArray }, - Constants.INVOKEVIRTUAL)); - il.append(pushConstant); - il.append(fact.createInvoke("java/lang/reflect/Constructor", "getAnnotation", jlaAnnotation, - new Type[] { jlClass }, Constants.INVOKEVIRTUAL)); - } - } else if (kind == Shadow.FieldSet || kind == Shadow.FieldGet) { - generateBytecodeToAccessAnnotationAtFieldGetSetShadow(toType, fact, il, pushConstant); - } else if (kind == Shadow.StaticInitialization || kind == Shadow.ExceptionHandler) { - il.append(fact.createConstant(BcelWorld.makeBcelType(containingType))); - il.append(pushConstant); - il.append(fact.createInvoke("java/lang/Class", "getAnnotation", jlaAnnotation, new Type[] { jlClass }, - Constants.INVOKEVIRTUAL)); - } else { - throw new RuntimeException("Don't understand this kind " + kind); - } - il.append(Utility.createConversion(fact, jlaAnnotation, BcelWorld.makeBcelType(toType))); - return il; - } - - /** - * At a FieldGet or FieldSet shadow, generate the bytecode to access the annotation for that field. The annotation is cached so - * the code checks that cached value before proceeding. - */ - private void generateBytecodeToAccessAnnotationAtFieldGetSetShadow(ResolvedType toType, InstructionFactory fact, - InstructionList il, Instruction pushConstantAnnotationType) { - Type jlClass = BcelWorld.makeBcelType(UnresolvedType.JL_CLASS); - Type jlString = BcelWorld.makeBcelType(UnresolvedType.JL_STRING); - Type jlaAnnotation = BcelWorld.makeBcelType(UnresolvedType.JAVA_LANG_ANNOTATION); - Type jlrField = BcelWorld.makeBcelType(UnresolvedType.JAVA_LANG_REFLECT_FIELD); - - LazyClassGen shadowEnclosingClass = shadow.getEnclosingClass(); - - // The annotation for the field of interest is cached, check cached value before fetching it - Field annotationCachingField = shadowEnclosingClass.getAnnotationCachingField(shadow, toType, isWithin); - String annotationCachingFieldName = annotationCachingField.getName(); - - // Basic idea here is to check if the cached field is null, if it is then initialize it, otherwise use it - il.append(fact.createGetStatic(shadowEnclosingClass.getName(), annotationCachingFieldName, jlaAnnotation)); - il.appendDUP(); - InstructionBranch ifNonNull = new InstructionBranch(Constants.IFNONNULL, null); - il.append(ifNonNull); - il.appendPOP(); - - // get the field of interest - il.append(fact.createConstant(BcelWorld.makeBcelType(containingType))); - il.append(fact.createConstant(member.getName())); - il.append(fact.createInvoke("java/lang/Class", "getDeclaredField", jlrField, new Type[] { jlString }, - Constants.INVOKEVIRTUAL)); - il.append(pushConstantAnnotationType); - il.append(fact.createInvoke("java/lang/reflect/Field", "getAnnotation", jlaAnnotation, new Type[] { jlClass }, - Constants.INVOKEVIRTUAL)); - il.appendDUP(); - il.append(fact.createPutStatic(shadowEnclosingClass.getName(), annotationCachingFieldName, jlaAnnotation)); - InstructionHandle ifNullElse = il.appendNOP(); - ifNonNull.setTarget(ifNullElse); - } - - private void buildArray(InstructionList il, InstructionFactory fact, Type arrayElementType, Type[] arrayEntries, int dim) { - il.append(fact.createConstant(Integer.valueOf(arrayEntries == null ? 0 : arrayEntries.length))); - il.append(fact.createNewArray(arrayElementType, (short) dim)); - if (arrayEntries == null) { - return; - } - for (int i = 0; i < arrayEntries.length; i++) { - il.append(InstructionFactory.createDup(1)); - il.append(fact.createConstant(Integer.valueOf(i))); - switch (arrayEntries[i].getType()) { - case Constants.T_ARRAY: - il.append(fact.createConstant(new ObjectType(arrayEntries[i].getSignature()))); // FIXME should be getName() and not - // getSignature()? - break; - case Constants.T_BOOLEAN: - il.append(fact.createGetStatic("java/lang/Boolean", "TYPE", arrayElementType)); - break; - case Constants.T_BYTE: - il.append(fact.createGetStatic("java/lang/Byte", "TYPE", arrayElementType)); - break; - case Constants.T_CHAR: - il.append(fact.createGetStatic("java/lang/Character", "TYPE", arrayElementType)); - break; - case Constants.T_INT: - il.append(fact.createGetStatic("java/lang/Integer", "TYPE", arrayElementType)); - break; - case Constants.T_LONG: - il.append(fact.createGetStatic("java/lang/Long", "TYPE", arrayElementType)); - break; - case Constants.T_DOUBLE: - il.append(fact.createGetStatic("java/lang/Double", "TYPE", arrayElementType)); - break; - case Constants.T_FLOAT: - il.append(fact.createGetStatic("java/lang/Float", "TYPE", arrayElementType)); - break; - case Constants.T_SHORT: - il.append(fact.createGetStatic("java/lang/Short", "TYPE", arrayElementType)); - break; - default: - il.append(fact.createConstant(arrayEntries[i])); - } - il.append(InstructionConstants.AASTORE); - } - } - - public Member getMember() { - return member; - } - - /** - * Return an object that can access a particular value of this annotation. - * - * @param valueType The type from the annotation that is of interest - * @param the formal name expressed in the pointcut, can be used to disambiguate - * @return a variable that represents access to that annotation value - */ - @Override - public Var getAccessorForValue(ResolvedType valueType, String formalName) { - return new AnnotationAccessFieldVar(this, valueType, formalName); - } -} diff --git a/weaver/src/org/aspectj/weaver/bcel/AspectInstanceVar.java b/weaver/src/org/aspectj/weaver/bcel/AspectInstanceVar.java deleted file mode 100644 index 37633b71f..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/AspectInstanceVar.java +++ /dev/null @@ -1,119 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2011 Contributors - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Andy Clement - SpringSource/vmware - * ******************************************************************/ -package org.aspectj.weaver.bcel; - -import org.aspectj.apache.bcel.Constants; -import org.aspectj.apache.bcel.generic.Instruction; -import org.aspectj.apache.bcel.generic.InstructionFactory; -import org.aspectj.apache.bcel.generic.InstructionList; -import org.aspectj.weaver.ResolvedType; - -/** - * Used to represent a variable reference to an aspect instance. This is used to support the if pointcut usage of - * 'thisAspectInstance'. This variable does not have a slot, instead on requesting a reference we call aspectOf() on the aspect in - * question to retrieve it. For now it only works with singleton aspects. - */ -public class AspectInstanceVar extends BcelVar { - - public AspectInstanceVar(ResolvedType type) { - super(type, -1); - } - - // fact is used in the subtypes - public Instruction createLoad(InstructionFactory fact) { - - throw new IllegalStateException(); - // return InstructionFactory.createLoad(BcelWorld.makeBcelType(getType()), slot); - } - - public Instruction createStore(InstructionFactory fact) { - throw new IllegalStateException(); - // return InstructionFactory.createStore(BcelWorld.makeBcelType(getType()), slot); - } - - public void appendStore(InstructionList il, InstructionFactory fact) { - throw new IllegalStateException(); - // il.append(createStore(fact)); - } - - public void appendLoad(InstructionList il, InstructionFactory fact) { - throw new IllegalStateException(); - // il.append(createLoad(fact)); - } - - public void appendLoadAndConvert(InstructionList il, InstructionFactory fact, ResolvedType toType) { - throw new IllegalStateException(); - // il.append(createLoad(fact)); - // Utility.appendConversion(il, fact, getType(), toType); - } - - public void insertLoad(InstructionList il, InstructionFactory fact) { - InstructionList loadInstructions = new InstructionList(); - loadInstructions.append(fact.createInvoke(getType().getName(), "aspectOf", "()" + getType().getSignature(), - Constants.INVOKESTATIC)); - il.insert(loadInstructions); - // throw new IllegalStateException(); - // il.insert(createLoad(fact)); - } - - public InstructionList createCopyFrom(InstructionFactory fact, int oldSlot) { - throw new IllegalStateException(); - // InstructionList il = new InstructionList(); - // il.append(InstructionFactory.createLoad(BcelWorld.makeBcelType(getType()), oldSlot)); - // il.append(createStore(fact)); - // return il; - } - - // this is an array var - void appendConvertableArrayLoad(InstructionList il, InstructionFactory fact, int index, ResolvedType convertTo) { - throw new IllegalStateException(); - // ResolvedType convertFromType = getType().getResolvedComponentType(); - // appendLoad(il, fact); - // il.append(Utility.createConstant(fact, index)); - // il.append(InstructionFactory.createArrayLoad(BcelWorld.makeBcelType(convertFromType))); - // Utility.appendConversion(il, fact, convertFromType, convertTo); - } - - void appendConvertableArrayStore(InstructionList il, InstructionFactory fact, int index, BcelVar storee) { - throw new IllegalStateException(); - // ResolvedType convertToType = getType().getResolvedComponentType(); - // appendLoad(il, fact); - // il.append(Utility.createConstant(fact, index)); - // storee.appendLoad(il, fact); - // Utility.appendConversion(il, fact, storee.getType(), convertToType); - // il.append(InstructionFactory.createArrayStore(BcelWorld.makeBcelType(convertToType))); - } - - InstructionList createConvertableArrayStore(InstructionFactory fact, int index, BcelVar storee) { - throw new IllegalStateException(); - // InstructionList il = new InstructionList(); - // appendConvertableArrayStore(il, fact, index, storee); - // return il; - } - - InstructionList createConvertableArrayLoad(InstructionFactory fact, int index, ResolvedType convertTo) { - throw new IllegalStateException(); - // InstructionList il = new InstructionList(); - // appendConvertableArrayLoad(il, fact, index, convertTo); - // return il; - } - - public int getPositionInAroundState() { - throw new IllegalStateException(); - // return positionInAroundState; - } - - public void setPositionInAroundState(int positionInAroundState) { - throw new IllegalStateException(); - // this.positionInAroundState = positionInAroundState; - } -} diff --git a/weaver/src/org/aspectj/weaver/bcel/AtAjAttributes.java b/weaver/src/org/aspectj/weaver/bcel/AtAjAttributes.java deleted file mode 100644 index 2da044c11..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/AtAjAttributes.java +++ /dev/null @@ -1,2046 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2005 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * initial implementation Alexandre Vasseur - *******************************************************************************/ -package org.aspectj.weaver.bcel; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.Iterator; -import java.util.List; -import java.util.StringTokenizer; - -import org.aspectj.apache.bcel.Constants; -import org.aspectj.apache.bcel.classfile.Attribute; -import org.aspectj.apache.bcel.classfile.Constant; -import org.aspectj.apache.bcel.classfile.ConstantUtf8; -import org.aspectj.apache.bcel.classfile.Field; -import org.aspectj.apache.bcel.classfile.JavaClass; -import org.aspectj.apache.bcel.classfile.LocalVariable; -import org.aspectj.apache.bcel.classfile.LocalVariableTable; -import org.aspectj.apache.bcel.classfile.Method; -import org.aspectj.apache.bcel.classfile.Unknown; -import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; -import org.aspectj.apache.bcel.classfile.annotation.ArrayElementValue; -import org.aspectj.apache.bcel.classfile.annotation.ClassElementValue; -import org.aspectj.apache.bcel.classfile.annotation.ElementValue; -import org.aspectj.apache.bcel.classfile.annotation.NameValuePair; -import org.aspectj.apache.bcel.classfile.annotation.RuntimeAnnos; -import org.aspectj.apache.bcel.classfile.annotation.RuntimeVisAnnos; -import org.aspectj.apache.bcel.generic.Type; -import org.aspectj.asm.AsmManager; -import org.aspectj.asm.IHierarchy; -import org.aspectj.asm.IProgramElement; -import org.aspectj.bridge.IMessage; -import org.aspectj.bridge.IMessageHandler; -import org.aspectj.bridge.ISourceLocation; -import org.aspectj.bridge.Message; -import org.aspectj.bridge.MessageUtil; -import org.aspectj.weaver.Advice; -import org.aspectj.weaver.AdviceKind; -import org.aspectj.weaver.AjAttribute; -import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; -import org.aspectj.weaver.AjcMemberMaker; -import org.aspectj.weaver.BindingScope; -import org.aspectj.weaver.ISourceContext; -import org.aspectj.weaver.MethodDelegateTypeMunger; -import org.aspectj.weaver.NameMangler; -import org.aspectj.weaver.ReferenceType; -import org.aspectj.weaver.ReferenceTypeDelegate; -import org.aspectj.weaver.ResolvedMember; -import org.aspectj.weaver.ResolvedPointcutDefinition; -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.UnresolvedType; -import org.aspectj.weaver.VersionedDataInputStream; -import org.aspectj.weaver.WeaverMessages; -import org.aspectj.weaver.World; -import org.aspectj.weaver.patterns.DeclareErrorOrWarning; -import org.aspectj.weaver.patterns.DeclareParents; -import org.aspectj.weaver.patterns.DeclareParentsMixin; -import org.aspectj.weaver.patterns.DeclarePrecedence; -import org.aspectj.weaver.patterns.FormalBinding; -import org.aspectj.weaver.patterns.IScope; -import org.aspectj.weaver.patterns.ParserException; -import org.aspectj.weaver.patterns.PatternParser; -import org.aspectj.weaver.patterns.PerCflow; -import org.aspectj.weaver.patterns.PerClause; -import org.aspectj.weaver.patterns.PerFromSuper; -import org.aspectj.weaver.patterns.PerObject; -import org.aspectj.weaver.patterns.PerSingleton; -import org.aspectj.weaver.patterns.PerTypeWithin; -import org.aspectj.weaver.patterns.Pointcut; -import org.aspectj.weaver.patterns.TypePattern; - -/** - * Annotation defined aspect reader. Reads the Java 5 annotations and turns them into AjAttributes - * - * @author Alexandre Vasseur - */ -public class AtAjAttributes { - - private final static List NO_ATTRIBUTES = Collections.emptyList(); - private final static String[] EMPTY_STRINGS = new String[0]; - private final static String VALUE = "value"; - private final static String ARGNAMES = "argNames"; - private final static String POINTCUT = "pointcut"; - private final static String THROWING = "throwing"; - private final static String RETURNING = "returning"; - private final static String STRING_DESC = "Ljava/lang/String;"; - private final static String ASPECTJ_ANNOTATION_PACKAGE = "org.aspectj.lang.annotation"; - private final static char PACKAGE_INITIAL_CHAR = ASPECTJ_ANNOTATION_PACKAGE.charAt(0); - - /** - * A struct that allows to add extra arguments without always breaking the API - */ - private static class AjAttributeStruct { - - /** - * The list of AjAttribute.XXX that we are populating from the @AJ read - */ - List ajAttributes = new ArrayList(); - - /** - * The resolved type (class) for which we are reading @AJ for (be it class, method, field annotations) - */ - final ResolvedType enclosingType; - - final ISourceContext context; - final IMessageHandler handler; - - public AjAttributeStruct(ResolvedType type, ISourceContext sourceContext, IMessageHandler messageHandler) { - enclosingType = type; - context = sourceContext; - handler = messageHandler; - } - } - - /** - * A struct when we read @AJ on method - * - * @author Alexandre Vasseur - */ - private static class AjAttributeMethodStruct extends AjAttributeStruct { - - // argument names used for formal binding - private String[] m_argumentNamesLazy = null; - public String unparsedArgumentNames = null; // Set only if discovered as - // argNames attribute of - // annotation - - final Method method; - final BcelMethod bMethod; - - public AjAttributeMethodStruct(Method method, BcelMethod bMethod, ResolvedType type, ISourceContext sourceContext, - IMessageHandler messageHandler) { - super(type, sourceContext, messageHandler); - this.method = method; - this.bMethod = bMethod; - } - - public String[] getArgumentNames() { - if (m_argumentNamesLazy == null) { - m_argumentNamesLazy = getMethodArgumentNames(method, unparsedArgumentNames, this); - } - return m_argumentNamesLazy; - } - } - - /** - * A struct when we read @AJ on field - */ - private static class AjAttributeFieldStruct extends AjAttributeStruct { - - final Field field; - - // final BcelField bField; - - public AjAttributeFieldStruct(Field field, BcelField bField, ResolvedType type, ISourceContext sourceContext, - IMessageHandler messageHandler) { - super(type, sourceContext, messageHandler); - this.field = field; - // this.bField = bField; - } - } - - /** - * Annotations are RuntimeVisible only. This allow us to not visit RuntimeInvisible ones. - * - * @param attribute - * @return true if runtime visible annotation - */ - public static boolean acceptAttribute(Attribute attribute) { - return (attribute instanceof RuntimeVisAnnos); - } - - /** - * Extract class level annotations and turn them into AjAttributes. - * - * @param javaClass - * @param type - * @param context - * @param msgHandler - * @return list of AjAttributes - */ - public static List readAj5ClassAttributes(AsmManager model, JavaClass javaClass, ReferenceType type, - ISourceContext context, IMessageHandler msgHandler, boolean isCodeStyleAspect) { - boolean ignoreThisClass = javaClass.getClassName().charAt(0) == PACKAGE_INITIAL_CHAR - && javaClass.getClassName().startsWith(ASPECTJ_ANNOTATION_PACKAGE); - if (ignoreThisClass) { - return NO_ATTRIBUTES; - } - boolean containsPointcut = false; - boolean containsAnnotationClassReference = false; - Constant[] cpool = javaClass.getConstantPool().getConstantPool(); - for (int i = 0; i < cpool.length; i++) { - Constant constant = cpool[i]; - if (constant != null && constant.getTag() == Constants.CONSTANT_Utf8) { - String constantValue = ((ConstantUtf8) constant).getValue(); - if (constantValue.length() > 28 && constantValue.charAt(1) == PACKAGE_INITIAL_CHAR) { - if (constantValue.startsWith("Lorg/aspectj/lang/annotation")) { - containsAnnotationClassReference = true; - if ("Lorg/aspectj/lang/annotation/DeclareAnnotation;".equals(constantValue)) { - msgHandler.handleMessage(new Message( - "Found @DeclareAnnotation while current release does not support it (see '" + type.getName() - + "')", IMessage.WARNING, null, type.getSourceLocation())); - } - if ("Lorg/aspectj/lang/annotation/Pointcut;".equals(constantValue)) { - containsPointcut = true; - } - } - - } - } - } - if (!containsAnnotationClassReference) { - return NO_ATTRIBUTES; - } - - AjAttributeStruct struct = new AjAttributeStruct(type, context, msgHandler); - Attribute[] attributes = javaClass.getAttributes(); - boolean hasAtAspectAnnotation = false; - boolean hasAtPrecedenceAnnotation = false; - - WeaverVersionInfo wvinfo = null; - for (int i = 0; i < attributes.length; i++) { - Attribute attribute = attributes[i]; - if (acceptAttribute(attribute)) { - RuntimeAnnos rvs = (RuntimeAnnos) attribute; - // we don't need to look for several attribute occurrences since - // it cannot happen as per JSR175 - if (!isCodeStyleAspect && !javaClass.isInterface()) { - hasAtAspectAnnotation = handleAspectAnnotation(rvs, struct); - // TODO AV - if put outside the if isCodeStyleAspect then we - // would enable mix style - hasAtPrecedenceAnnotation = handlePrecedenceAnnotation(rvs, struct); - } - // there can only be one RuntimeVisible bytecode attribute - break; - } - } - for (int i = attributes.length - 1; i >= 0; i--) { - Attribute attribute = attributes[i]; - if (attribute.getName().equals(WeaverVersionInfo.AttributeName)) { - try { - VersionedDataInputStream s = new VersionedDataInputStream(new ByteArrayInputStream( - ((Unknown) attribute).getBytes()), null); - wvinfo = WeaverVersionInfo.read(s); - struct.ajAttributes.add(0, wvinfo); - } catch (IOException ioe) { - ioe.printStackTrace(); - } - } - } - if (wvinfo == null) { - // If we are in here due to a resetState() call (presumably because of reweavable state processing), the - // original type delegate will have been set with a version but that version will be missing from - // the new set of attributes (looks like a bug where the version attribute was not included in the - // data compressed into the attribute). So rather than 'defaulting' to current, we should use one - // if it set on the delegate for the type. - ReferenceTypeDelegate delegate = type.getDelegate(); - if (delegate instanceof BcelObjectType) { - wvinfo = ((BcelObjectType) delegate).getWeaverVersionAttribute(); - if (wvinfo != null) { - if (wvinfo.getMajorVersion() != WeaverVersionInfo.WEAVER_VERSION_MAJOR_UNKNOWN) { - // use this one - struct.ajAttributes.add(0, wvinfo); - } else { - wvinfo = null; - } - } - } - if (wvinfo == null) { - struct.ajAttributes.add(0, wvinfo = new AjAttribute.WeaverVersionInfo()); - } - } - - // basic semantic check - if (hasAtPrecedenceAnnotation && !hasAtAspectAnnotation) { - msgHandler.handleMessage(new Message("Found @DeclarePrecedence on a non @Aspect type '" + type.getName() + "'", - IMessage.WARNING, null, type.getSourceLocation())); - // bypass what we have read - return NO_ATTRIBUTES; - } - - // the following block will not detect @Pointcut in non @Aspect types - // for optimization purpose - if (!(hasAtAspectAnnotation || isCodeStyleAspect) && !containsPointcut) { - return NO_ATTRIBUTES; - } - - // FIXME AV - turn on when ajcMightHaveAspect - // if (hasAtAspectAnnotation && type.isInterface()) { - // msgHandler.handleMessage( - // new Message( - // "Found @Aspect on an interface type '" + type.getName() + "'", - // IMessage.WARNING, - // null, - // type.getSourceLocation() - // ) - // ); - // // bypass what we have read - // return EMPTY_LIST; - // } - - // semantic check: @Aspect must be public - // FIXME AV - do we really want to enforce that? - // if (hasAtAspectAnnotation && !javaClass.isPublic()) { - // msgHandler.handleMessage( - // new Message( - // "Found @Aspect annotation on a non public class '" + - // javaClass.getClassName() + "'", - // IMessage.ERROR, - // null, - // type.getSourceLocation() - // ) - // ); - // return EMPTY_LIST; - // } - - // code style pointcuts are class attributes - // we need to gather the @AJ pointcut right now and not at method level - // annotation extraction time - // in order to be able to resolve the pointcut references later on - // we don't need to look in super class, the pointcut reference in the - // grammar will do it - - for (int i = 0; i < javaClass.getMethods().length; i++) { - Method method = javaClass.getMethods()[i]; - if (method.getName().startsWith(NameMangler.PREFIX)) { - continue; // already dealt with by ajc... - } - // FIXME alex optimize, this method struct will gets recreated for - // advice extraction - AjAttributeMethodStruct mstruct = null; - boolean processedPointcut = false; - Attribute[] mattributes = method.getAttributes(); - for (int j = 0; j < mattributes.length; j++) { - Attribute mattribute = mattributes[j]; - if (acceptAttribute(mattribute)) { - // TODO speed all this nonsense up rather than looking - // through all the annotations every time - // same for fields - mstruct = new AjAttributeMethodStruct(method, null, type, context, msgHandler); - processedPointcut = handlePointcutAnnotation((RuntimeAnnos) mattribute, mstruct); - if (!processedPointcut) { - processedPointcut = handleDeclareMixinAnnotation((RuntimeAnnos) mattribute, mstruct); - } - // there can only be one RuntimeVisible bytecode attribute - break; - } - } - if (processedPointcut) { - struct.ajAttributes.addAll(mstruct.ajAttributes); - } - } - - // code style declare error / warning / implements / parents are field - // attributes - Field[] fs = javaClass.getFields(); - for (int i = 0; i < fs.length; i++) { - Field field = fs[i]; - if (field.getName().startsWith(NameMangler.PREFIX)) { - continue; // already dealt with by ajc... - } - // FIXME alex optimize, this method struct will gets recreated for - // advice extraction - AjAttributeFieldStruct fstruct = new AjAttributeFieldStruct(field, null, type, context, msgHandler); - Attribute[] fattributes = field.getAttributes(); - - for (int j = 0; j < fattributes.length; j++) { - Attribute fattribute = fattributes[j]; - if (acceptAttribute(fattribute)) { - RuntimeAnnos frvs = (RuntimeAnnos) fattribute; - if (handleDeclareErrorOrWarningAnnotation(model, frvs, fstruct) - || handleDeclareParentsAnnotation(frvs, fstruct)) { - // semantic check - must be in an @Aspect [remove if - // previous block bypassed in advance] - if (!type.isAnnotationStyleAspect() && !isCodeStyleAspect) { - msgHandler.handleMessage(new Message("Found @AspectJ annotations in a non @Aspect type '" - + type.getName() + "'", IMessage.WARNING, null, type.getSourceLocation())); - // go ahead - } - } - // there can only be one RuntimeVisible bytecode attribute - break; - } - } - struct.ajAttributes.addAll(fstruct.ajAttributes); - } - - return struct.ajAttributes; - } - - /** - * Extract method level annotations and turn them into AjAttributes. - * - * @param method - * @param type - * @param context - * @param msgHandler - * @return list of AjAttributes - */ - public static List readAj5MethodAttributes(Method method, BcelMethod bMethod, ResolvedType type, - ResolvedPointcutDefinition preResolvedPointcut, ISourceContext context, IMessageHandler msgHandler) { - if (method.getName().startsWith(NameMangler.PREFIX)) { - return Collections.emptyList(); // already dealt with by ajc... - } - - AjAttributeMethodStruct struct = new AjAttributeMethodStruct(method, bMethod, type, context, msgHandler); - Attribute[] attributes = method.getAttributes(); - - // we remember if we found one @AJ annotation for minimal semantic error - // reporting - // the real reporting beeing done thru AJDT and the compiler mapping @AJ - // to AjAtttribute - // or thru APT - // - // Note: we could actually skip the whole thing if type is not itself an - // @Aspect - // but then we would not see any warning. We do bypass for pointcut but - // not for advice since it would - // be too silent. - boolean hasAtAspectJAnnotation = false; - boolean hasAtAspectJAnnotationMustReturnVoid = false; - for (int i = 0; i < attributes.length; i++) { - Attribute attribute = attributes[i]; - try { - if (acceptAttribute(attribute)) { - RuntimeAnnos rvs = (RuntimeAnnos) attribute; - hasAtAspectJAnnotationMustReturnVoid = hasAtAspectJAnnotationMustReturnVoid - || handleBeforeAnnotation(rvs, struct, preResolvedPointcut); - hasAtAspectJAnnotationMustReturnVoid = hasAtAspectJAnnotationMustReturnVoid - || handleAfterAnnotation(rvs, struct, preResolvedPointcut); - hasAtAspectJAnnotationMustReturnVoid = hasAtAspectJAnnotationMustReturnVoid - || handleAfterReturningAnnotation(rvs, struct, preResolvedPointcut, bMethod); - hasAtAspectJAnnotationMustReturnVoid = hasAtAspectJAnnotationMustReturnVoid - || handleAfterThrowingAnnotation(rvs, struct, preResolvedPointcut, bMethod); - hasAtAspectJAnnotation = hasAtAspectJAnnotation || handleAroundAnnotation(rvs, struct, preResolvedPointcut); - // there can only be one RuntimeVisible bytecode attribute - break; - } - } catch (ReturningFormalNotDeclaredInAdviceSignatureException e) { - msgHandler.handleMessage(new Message(WeaverMessages.format(WeaverMessages.RETURNING_FORMAL_NOT_DECLARED_IN_ADVICE, - e.getFormalName()), IMessage.ERROR, null, bMethod.getSourceLocation())); - } catch (ThrownFormalNotDeclaredInAdviceSignatureException e) { - msgHandler.handleMessage(new Message(WeaverMessages.format(WeaverMessages.THROWN_FORMAL_NOT_DECLARED_IN_ADVICE, - e.getFormalName()), IMessage.ERROR, null, bMethod.getSourceLocation())); - } - } - hasAtAspectJAnnotation = hasAtAspectJAnnotation || hasAtAspectJAnnotationMustReturnVoid; - - // semantic check - must be in an @Aspect [remove if previous block - // bypassed in advance] - if (hasAtAspectJAnnotation && !type.isAspect()) { // isAnnotationStyleAspect()) - // { - msgHandler.handleMessage(new Message("Found @AspectJ annotations in a non @Aspect type '" + type.getName() + "'", - IMessage.WARNING, null, type.getSourceLocation())); - // go ahead - } - // semantic check - advice must be public - if (hasAtAspectJAnnotation && !struct.method.isPublic()) { - msgHandler.handleMessage(new Message("Found @AspectJ annotation on a non public advice '" - + methodToString(struct.method) + "'", IMessage.ERROR, null, type.getSourceLocation())); - // go ahead - } - - // semantic check - advice must not be static - if (hasAtAspectJAnnotation && struct.method.isStatic()) { - msgHandler.handleMessage(MessageUtil.error("Advice cannot be declared static '" + methodToString(struct.method) + "'", - type.getSourceLocation())); - // new Message( - // "Advice cannot be declared static '" + - // methodToString(struct.method) + "'", - // IMessage.ERROR, - // null, - // type.getSourceLocation() - // ) - // ); - // go ahead - } - - // semantic check for non around advice must return void - if (hasAtAspectJAnnotationMustReturnVoid && !Type.VOID.equals(struct.method.getReturnType())) { - msgHandler.handleMessage(new Message("Found @AspectJ annotation on a non around advice not returning void '" - + methodToString(struct.method) + "'", IMessage.ERROR, null, type.getSourceLocation())); - // go ahead - } - - return struct.ajAttributes; - } - - /** - * Extract field level annotations and turn them into AjAttributes. - * - * @param field - * @param type - * @param context - * @param msgHandler - * @return list of AjAttributes, always empty for now - */ - public static List readAj5FieldAttributes(Field field, BcelField bField, ResolvedType type, - ISourceContext context, IMessageHandler msgHandler) { - // Note: field annotation are for ITD and DEOW - processed at class - // level directly - return Collections.emptyList(); - } - - /** - * Read @Aspect - * - * @param runtimeAnnotations - * @param struct - * @return true if found - */ - private static boolean handleAspectAnnotation(RuntimeAnnos runtimeAnnotations, AjAttributeStruct struct) { - AnnotationGen aspect = getAnnotation(runtimeAnnotations, AjcMemberMaker.ASPECT_ANNOTATION); - if (aspect != null) { - // semantic check for inheritance (only one level up) - boolean extendsAspect = false; - if (!"java.lang.Object".equals(struct.enclosingType.getSuperclass().getName())) { - if (!struct.enclosingType.getSuperclass().isAbstract() && struct.enclosingType.getSuperclass().isAspect()) { - reportError("cannot extend a concrete aspect", struct); - return false; - } - extendsAspect = struct.enclosingType.getSuperclass().isAspect(); - } - - NameValuePair aspectPerClause = getAnnotationElement(aspect, VALUE); - final PerClause perClause; - if (aspectPerClause == null) { - // empty value means singleton unless inherited - if (!extendsAspect) { - perClause = new PerSingleton(); - } else { - perClause = new PerFromSuper(struct.enclosingType.getSuperclass().getPerClause().getKind()); - } - } else { - String perX = aspectPerClause.getValue().stringifyValue(); - if (perX == null || perX.length() <= 0) { - perClause = new PerSingleton(); - } else { - perClause = parsePerClausePointcut(perX, struct); - } - } - if (perClause == null) { - // could not parse it, ignore the aspect - return false; - } else { - perClause.setLocation(struct.context, -1, -1);// struct.context.getOffset(), - // struct.context.getOffset()+1);//FIXME - // AVASM - // Not setting version here - // struct.ajAttributes.add(new AjAttribute.WeaverVersionInfo()); - AjAttribute.Aspect aspectAttribute = new AjAttribute.Aspect(perClause); - struct.ajAttributes.add(aspectAttribute); - FormalBinding[] bindings = new org.aspectj.weaver.patterns.FormalBinding[0]; - final IScope binding; - binding = new BindingScope(struct.enclosingType, struct.context, bindings); - - // // we can't resolve here since the perclause typically refers - // to pointcuts - // // defined in the aspect that we haven't told the - // BcelObjectType about yet. - // - // perClause.resolve(binding); - - // so we prepare to do it later... - aspectAttribute.setResolutionScope(binding); - return true; - } - } - return false; - } - - /** - * Read a perClause, returns null on failure and issue messages - * - * @param perClauseString like "pertarget(.....)" - * @param struct for which we are parsing the per clause - * @return a PerClause instance - */ - private static PerClause parsePerClausePointcut(String perClauseString, AjAttributeStruct struct) { - final String pointcutString; - Pointcut pointcut = null; - TypePattern typePattern = null; - final PerClause perClause; - if (perClauseString.startsWith(PerClause.KindAnnotationPrefix.PERCFLOW.getName())) { - pointcutString = PerClause.KindAnnotationPrefix.PERCFLOW.extractPointcut(perClauseString); - pointcut = parsePointcut(pointcutString, struct, false); - perClause = new PerCflow(pointcut, false); - } else if (perClauseString.startsWith(PerClause.KindAnnotationPrefix.PERCFLOWBELOW.getName())) { - pointcutString = PerClause.KindAnnotationPrefix.PERCFLOWBELOW.extractPointcut(perClauseString); - pointcut = parsePointcut(pointcutString, struct, false); - perClause = new PerCflow(pointcut, true); - } else if (perClauseString.startsWith(PerClause.KindAnnotationPrefix.PERTARGET.getName())) { - pointcutString = PerClause.KindAnnotationPrefix.PERTARGET.extractPointcut(perClauseString); - pointcut = parsePointcut(pointcutString, struct, false); - perClause = new PerObject(pointcut, false); - } else if (perClauseString.startsWith(PerClause.KindAnnotationPrefix.PERTHIS.getName())) { - pointcutString = PerClause.KindAnnotationPrefix.PERTHIS.extractPointcut(perClauseString); - pointcut = parsePointcut(pointcutString, struct, false); - perClause = new PerObject(pointcut, true); - } else if (perClauseString.startsWith(PerClause.KindAnnotationPrefix.PERTYPEWITHIN.getName())) { - pointcutString = PerClause.KindAnnotationPrefix.PERTYPEWITHIN.extractPointcut(perClauseString); - typePattern = parseTypePattern(pointcutString, struct); - perClause = new PerTypeWithin(typePattern); - } else if (perClauseString.equalsIgnoreCase(PerClause.SINGLETON.getName() + "()")) { - perClause = new PerSingleton(); - } else { - // could not parse the @AJ perclause - fallback to singleton and - // issue an error - reportError("@Aspect per clause cannot be read '" + perClauseString + "'", struct); - return null; - } - - if (!PerClause.SINGLETON.equals(perClause.getKind()) && !PerClause.PERTYPEWITHIN.equals(perClause.getKind()) - && pointcut == null) { - // we could not parse the pointcut - return null; - } - if (PerClause.PERTYPEWITHIN.equals(perClause.getKind()) && typePattern == null) { - // we could not parse the type pattern - return null; - } - return perClause; - } - - /** - * Read @DeclarePrecedence - * - * @param runtimeAnnotations - * @param struct - * @return true if found - */ - private static boolean handlePrecedenceAnnotation(RuntimeAnnos runtimeAnnotations, AjAttributeStruct struct) { - AnnotationGen aspect = getAnnotation(runtimeAnnotations, AjcMemberMaker.DECLAREPRECEDENCE_ANNOTATION); - if (aspect != null) { - NameValuePair precedence = getAnnotationElement(aspect, VALUE); - if (precedence != null) { - String precedencePattern = precedence.getValue().stringifyValue(); - PatternParser parser = new PatternParser(precedencePattern); - DeclarePrecedence ajPrecedence = parser.parseDominates(); - struct.ajAttributes.add(new AjAttribute.DeclareAttribute(ajPrecedence)); - return true; - } - } - return false; - } - - // /** - // * Read @DeclareImplements - // * - // * @param runtimeAnnotations - // * @param struct - // * @return true if found - // */ - // private static boolean - // handleDeclareImplementsAnnotation(RuntimeAnnotations runtimeAnnotations, - // AjAttributeFieldStruct - // struct) {//, ResolvedPointcutDefinition preResolvedPointcut) { - // Annotation deci = getAnnotation(runtimeAnnotations, - // AjcMemberMaker.DECLAREIMPLEMENTS_ANNOTATION); - // if (deci != null) { - // ElementNameValuePairGen deciPatternNVP = getAnnotationElement(deci, - // VALUE); - // String deciPattern = deciPatternNVP.getValue().stringifyValue(); - // if (deciPattern != null) { - // TypePattern typePattern = parseTypePattern(deciPattern, struct); - // ResolvedType fieldType = - // UnresolvedType.forSignature(struct.field.getSignature()).resolve(struct.enclosingType.getWorld()); - // if (fieldType.isPrimitiveType()) { - // return false; - // } else if (fieldType.isInterface()) { - // TypePattern parent = new - // ExactTypePattern(UnresolvedType.forSignature(struct.field.getSignature()), - // false, false); - // parent.resolve(struct.enclosingType.getWorld()); - // List parents = new ArrayList(1); - // parents.add(parent); - // //TODO kick ISourceLocation sl = struct.bField.getSourceLocation(); ?? - // struct.ajAttributes.add( - // new AjAttribute.DeclareAttribute( - // new DeclareParents( - // typePattern, - // parents, - // false - // ) - // ) - // ); - // return true; - // } else { - // reportError("@DeclareImplements: can only be used on field whose type is an interface", - // struct); - // return false; - // } - // } - // } - // return false; - // } - - /** - * Read @DeclareParents - * - * @param runtimeAnnotations - * @param struct - * @return true if found - */ - private static boolean handleDeclareParentsAnnotation(RuntimeAnnos runtimeAnnotations, AjAttributeFieldStruct struct) {// , - // ResolvedPointcutDefinition - // preResolvedPointcut) - // { - AnnotationGen decp = getAnnotation(runtimeAnnotations, AjcMemberMaker.DECLAREPARENTS_ANNOTATION); - if (decp != null) { - NameValuePair decpPatternNVP = getAnnotationElement(decp, VALUE); - String decpPattern = decpPatternNVP.getValue().stringifyValue(); - if (decpPattern != null) { - TypePattern typePattern = parseTypePattern(decpPattern, struct); - ResolvedType fieldType = UnresolvedType.forSignature(struct.field.getSignature()).resolve( - struct.enclosingType.getWorld()); - if (fieldType.isParameterizedOrRawType()) { - fieldType = fieldType.getGenericType(); - } - if (fieldType.isInterface()) { - TypePattern parent = parseTypePattern(fieldType.getName(), struct); - FormalBinding[] bindings = new org.aspectj.weaver.patterns.FormalBinding[0]; - IScope binding = new BindingScope(struct.enclosingType, struct.context, bindings); - // first add the declare implements like - List parents = new ArrayList(1); - parents.add(parent); - DeclareParents dp = new DeclareParents(typePattern, parents, false); - dp.resolve(binding); // resolves the parent and child parts of the decp - - // resolve this so that we can use it for the - // MethodDelegateMungers below. - // eg. '@Coloured *' will change from a WildTypePattern to - // an 'AnyWithAnnotationTypePattern' after this resolution - typePattern = dp.getChild(); // this retrieves the resolved version - // TODO kick ISourceLocation sl = - // struct.bField.getSourceLocation(); ?? - // dp.setLocation(dp.getDeclaringType().getSourceContext(), - // dp.getDeclaringType().getSourceLocation().getOffset(), - // dp.getDeclaringType().getSourceLocation().getOffset()); - dp.setLocation(struct.context, -1, -1); // not ideal... - struct.ajAttributes.add(new AjAttribute.DeclareAttribute(dp)); - - // do we have a defaultImpl=xxx.class (ie implementation) - String defaultImplClassName = null; - NameValuePair defaultImplNVP = getAnnotationElement(decp, "defaultImpl"); - if (defaultImplNVP != null) { - ClassElementValue defaultImpl = (ClassElementValue) defaultImplNVP.getValue(); - defaultImplClassName = UnresolvedType.forSignature(defaultImpl.getClassString()).getName(); - if (defaultImplClassName.equals("org.aspectj.lang.annotation.DeclareParents")) { - defaultImplClassName = null; - } else { - // check public no arg ctor - ResolvedType impl = struct.enclosingType.getWorld().resolve(defaultImplClassName, false); - ResolvedMember[] mm = impl.getDeclaredMethods(); - int implModifiers = impl.getModifiers(); - boolean defaultVisibilityImpl = !(Modifier.isPrivate(implModifiers) - || Modifier.isProtected(implModifiers) || Modifier.isPublic(implModifiers)); - boolean hasNoCtorOrANoArgOne = true; - ResolvedMember foundOneOfIncorrectVisibility = null; - for (int i = 0; i < mm.length; i++) { - ResolvedMember resolvedMember = mm[i]; - if (resolvedMember.getName().equals("")) { - hasNoCtorOrANoArgOne = false; - - if (resolvedMember.getParameterTypes().length == 0) { - if (defaultVisibilityImpl) { // default visibility implementation - if (resolvedMember.isPublic() || resolvedMember.isDefault()) { - hasNoCtorOrANoArgOne = true; - } else { - foundOneOfIncorrectVisibility = resolvedMember; - } - } else if (Modifier.isPublic(implModifiers)) { // public - // implementation - if (resolvedMember.isPublic()) { - hasNoCtorOrANoArgOne = true; - } else { - foundOneOfIncorrectVisibility = resolvedMember; - } - } - } - } - if (hasNoCtorOrANoArgOne) { - break; - } - } - if (!hasNoCtorOrANoArgOne) { - if (foundOneOfIncorrectVisibility != null) { - reportError( - "@DeclareParents: defaultImpl=\"" - + defaultImplClassName - + "\" has a no argument constructor, but it is of incorrect visibility. It must be at least as visible as the type.", - struct); - } else { - reportError("@DeclareParents: defaultImpl=\"" + defaultImplClassName - + "\" has no public no-arg constructor", struct); - } - } - if (!fieldType.isAssignableFrom(impl)) { - reportError("@DeclareParents: defaultImpl=\"" + defaultImplClassName - + "\" does not implement the interface '" + fieldType.toString() + "'", struct); - } - } - - } - - // then iterate on field interface hierarchy (not object) - boolean hasAtLeastOneMethod = false; - Iterator methodIterator = fieldType.getMethodsIncludingIntertypeDeclarations(false, true); - while (methodIterator.hasNext()) { - ResolvedMember method = methodIterator.next(); - if (method.isAbstract()) { - // moved to be detected at weave time if the target - // doesnt implement the methods - // if (defaultImplClassName == null) { - // // non marker interface with no default impl - // provided - // reportError("@DeclareParents: used with a non marker interface and no defaultImpl=\"...\" provided", - // struct); - // return false; - // } - hasAtLeastOneMethod = true; - // What we are saying here: - // We have this method 'method' and we want to put a - // forwarding method into a type that matches - // typePattern that should delegate to the version - // of the method in 'defaultImplClassName' - - // Now the method may be from a supertype but the - // declaring type of the method we pass into the - // type - // munger is what is used to determine the type of - // the field that hosts the delegate instance. - // So here we create a modified method with an - // alternative declaring type so that we lookup - // the right field. See pr164016. - MethodDelegateTypeMunger mdtm = new MethodDelegateTypeMunger(method, struct.enclosingType, defaultImplClassName, typePattern); - mdtm.setFieldType(fieldType); - mdtm.setSourceLocation(struct.enclosingType.getSourceLocation()); - struct.ajAttributes.add(new AjAttribute.TypeMunger(mdtm)); - } - } - // successful so far, we thus need a bcel type munger to have - // a field hosting the mixin in the target type - if (hasAtLeastOneMethod && defaultImplClassName != null) { - ResolvedMember fieldHost = AjcMemberMaker.itdAtDeclareParentsField(null, fieldType, struct.enclosingType); - struct.ajAttributes.add(new AjAttribute.TypeMunger(new MethodDelegateTypeMunger.FieldHostTypeMunger( - fieldHost, struct.enclosingType, typePattern))); - } - return true; - } else { - reportError("@DeclareParents: can only be used on a field whose type is an interface", struct); - return false; - } - } - } - return false; - } - - /** - * Return a nicely formatted method string, for example: int X.foo(java.lang.String) - */ - public static String getMethodForMessage(AjAttributeMethodStruct methodstructure) { - StringBuffer sb = new StringBuffer(); - sb.append("Method '"); - sb.append(methodstructure.method.getReturnType().toString()); - sb.append(" ").append(methodstructure.enclosingType).append(".").append(methodstructure.method.getName()); - sb.append("("); - Type[] args = methodstructure.method.getArgumentTypes(); - if (args != null) { - for (int t = 0; t < args.length; t++) { - if (t > 0) { - sb.append(","); - } - sb.append(args[t].toString()); - } - } - sb.append(")'"); - return sb.toString(); - } - - /** - * Process any @DeclareMixin annotation. - * - * Example Declaration
- * - * @DeclareMixin("Foo+") public I createImpl(Object o) { return new Impl(o); } - * - *
- * @param runtimeAnnotations - * @param struct - * @return true if found - */ - private static boolean handleDeclareMixinAnnotation(RuntimeAnnos runtimeAnnotations, AjAttributeMethodStruct struct) { - AnnotationGen declareMixinAnnotation = getAnnotation(runtimeAnnotations, AjcMemberMaker.DECLAREMIXIN_ANNOTATION); - if (declareMixinAnnotation == null) { - // No annotation found - return false; - } - Method annotatedMethod = struct.method; - World world = struct.enclosingType.getWorld(); - NameValuePair declareMixinPatternNameValuePair = getAnnotationElement(declareMixinAnnotation, VALUE); - - // declareMixinPattern could be of the form "Bar*" or "A || B" or "Foo+" - String declareMixinPattern = declareMixinPatternNameValuePair.getValue().stringifyValue(); - TypePattern targetTypePattern = parseTypePattern(declareMixinPattern, struct); - - // Return value of the annotated method is the interface or class that the mixin delegate should have - ResolvedType methodReturnType = UnresolvedType.forSignature(annotatedMethod.getReturnType().getSignature()).resolve(world); - if (methodReturnType.isParameterizedOrRawType()) { - methodReturnType = methodReturnType.getGenericType(); - } - if (methodReturnType.isPrimitiveType()) { - reportError(getMethodForMessage(struct) + ": factory methods for a mixin cannot return void or a primitive type", - struct); - return false; - } - - if (annotatedMethod.getArgumentTypes().length > 1) { - reportError(getMethodForMessage(struct) + ": factory methods for a mixin can take a maximum of one parameter", struct); - return false; - } - - // The set of interfaces to be mixed in is either: - // supplied as a list in the 'Class[] interfaces' value in the annotation value - // supplied as just the interface return value of the annotated method - // supplied as just the class return value of the annotated method - NameValuePair interfaceListSpecified = getAnnotationElement(declareMixinAnnotation, "interfaces"); - - List newParents = new ArrayList(1); - List newInterfaceTypes = new ArrayList(1); - if (interfaceListSpecified != null) { - ArrayElementValue arrayOfInterfaceTypes = (ArrayElementValue) interfaceListSpecified.getValue(); - int numberOfTypes = arrayOfInterfaceTypes.getElementValuesArraySize(); - ElementValue[] theTypes = arrayOfInterfaceTypes.getElementValuesArray(); - for (int i = 0; i < numberOfTypes; i++) { - ClassElementValue interfaceType = (ClassElementValue) theTypes[i]; - // Check: needs to be resolvable - // TODO crappy replace required - ResolvedType ajInterfaceType = UnresolvedType.forSignature(interfaceType.getClassString().replace("/", ".")) - .resolve(world); - if (ajInterfaceType.isMissing() || !ajInterfaceType.isInterface()) { - reportError( - "Types listed in the 'interfaces' DeclareMixin annotation value must be valid interfaces. This is invalid: " - + ajInterfaceType.getName(), struct); // TODO better error location, use the method position - return false; - } - if (!ajInterfaceType.isAssignableFrom(methodReturnType)) { - reportError(getMethodForMessage(struct) + ": factory method does not return something that implements '" - + ajInterfaceType.getName() + "'", struct); - return false; - } - newInterfaceTypes.add(ajInterfaceType); - // Checking that it is a superinterface of the methods return value is done at weave time - TypePattern newParent = parseTypePattern(ajInterfaceType.getName(), struct); - newParents.add(newParent); - } - } else { - if (methodReturnType.isClass()) { - reportError( - getMethodForMessage(struct) - + ": factory methods for a mixin must either return an interface type or specify interfaces in the annotation and return a class", - struct); - return false; - } - // Use the method return type: this might be a class or an interface - TypePattern newParent = parseTypePattern(methodReturnType.getName(), struct); - newInterfaceTypes.add(methodReturnType); - newParents.add(newParent); - } - if (newParents.size() == 0) { - // Warning: did they foolishly put @DeclareMixin(value="Bar+",interfaces={}) - // TODO output warning - return false; - } - - // Create the declare parents that will add the interfaces to matching targets - FormalBinding[] bindings = new org.aspectj.weaver.patterns.FormalBinding[0]; - IScope binding = new BindingScope(struct.enclosingType, struct.context, bindings); - // how do we mark this as a decp due to decmixin? - DeclareParents dp = new DeclareParentsMixin(targetTypePattern, newParents); - dp.resolve(binding); - targetTypePattern = dp.getChild(); - - dp.setLocation(struct.context, -1, -1); // not ideal... - struct.ajAttributes.add(new AjAttribute.DeclareAttribute(dp)); - - // The factory method for building the implementation is the - // one attached to the annotation: - // Method implementationFactory = struct.method; - - boolean hasAtLeastOneMethod = false; - - for (Iterator iterator = newInterfaceTypes.iterator(); iterator.hasNext();) { - ResolvedType typeForDelegation = iterator.next(); - // TODO check for overlapping interfaces. Eg. A implements I, I extends J - if they specify interfaces={I,J} we dont - // want to do any methods twice - ResolvedMember[] methods = typeForDelegation.getMethodsWithoutIterator(true, false, false).toArray( - new ResolvedMember[0]); - for (int i = 0; i < methods.length; i++) { - ResolvedMember method = methods[i]; - if (method.isAbstract()) { - hasAtLeastOneMethod = true; - if (method.hasBackingGenericMember()) { - method = method.getBackingGenericMember(); - } - MethodDelegateTypeMunger mdtm = new MethodDelegateTypeMunger(method, struct.enclosingType, "", - targetTypePattern, struct.method.getName(), struct.method.getSignature()); - mdtm.setFieldType(methodReturnType); - mdtm.setSourceLocation(struct.enclosingType.getSourceLocation()); - struct.ajAttributes.add(new AjAttribute.TypeMunger(mdtm)); - } - } - } - // if any method delegate was created then a field to hold the delegate instance must also be added - if (hasAtLeastOneMethod) { - ResolvedMember fieldHost = AjcMemberMaker.itdAtDeclareParentsField(null, methodReturnType, struct.enclosingType); - struct.ajAttributes.add(new AjAttribute.TypeMunger(new MethodDelegateTypeMunger.FieldHostTypeMunger(fieldHost, - struct.enclosingType, targetTypePattern))); - } - return true; - } - - /** - * Read @Before - * - * @param runtimeAnnotations - * @param struct - * @return true if found - */ - private static boolean handleBeforeAnnotation(RuntimeAnnos runtimeAnnotations, AjAttributeMethodStruct struct, - ResolvedPointcutDefinition preResolvedPointcut) { - AnnotationGen before = getAnnotation(runtimeAnnotations, AjcMemberMaker.BEFORE_ANNOTATION); - if (before != null) { - NameValuePair beforeAdvice = getAnnotationElement(before, VALUE); - if (beforeAdvice != null) { - // this/target/args binding - String argumentNames = getArgNamesValue(before); - if (argumentNames != null) { - struct.unparsedArgumentNames = argumentNames; - } - FormalBinding[] bindings = new org.aspectj.weaver.patterns.FormalBinding[0]; - try { - bindings = extractBindings(struct); - } catch (UnreadableDebugInfoException unreadableDebugInfoException) { - return false; - } - IScope binding = new BindingScope(struct.enclosingType, struct.context, bindings); - - // joinpoint, staticJoinpoint binding - int extraArgument = extractExtraArgument(struct.method); - - Pointcut pc = null; - if (preResolvedPointcut != null) { - pc = preResolvedPointcut.getPointcut(); - // pc.resolve(binding); - } else { - pc = parsePointcut(beforeAdvice.getValue().stringifyValue(), struct, false); - if (pc == null) { - return false;// parse error - } - pc = pc.resolve(binding); - } - setIgnoreUnboundBindingNames(pc, bindings); - - ISourceLocation sl = struct.context.makeSourceLocation(struct.bMethod.getDeclarationLineNumber(), - struct.bMethod.getDeclarationOffset()); - struct.ajAttributes.add(new AjAttribute.AdviceAttribute(AdviceKind.Before, pc, extraArgument, sl.getOffset(), sl - .getOffset() + 1,// FIXME AVASM - struct.context)); - return true; - } - } - return false; - } - - /** - * Read @After - * - * @param runtimeAnnotations - * @param struct - * @return true if found - */ - private static boolean handleAfterAnnotation(RuntimeAnnos runtimeAnnotations, AjAttributeMethodStruct struct, - ResolvedPointcutDefinition preResolvedPointcut) { - AnnotationGen after = getAnnotation(runtimeAnnotations, AjcMemberMaker.AFTER_ANNOTATION); - if (after != null) { - NameValuePair afterAdvice = getAnnotationElement(after, VALUE); - if (afterAdvice != null) { - // this/target/args binding - FormalBinding[] bindings = new org.aspectj.weaver.patterns.FormalBinding[0]; - String argumentNames = getArgNamesValue(after); - if (argumentNames != null) { - struct.unparsedArgumentNames = argumentNames; - } - try { - bindings = extractBindings(struct); - } catch (UnreadableDebugInfoException unreadableDebugInfoException) { - return false; - } - IScope binding = new BindingScope(struct.enclosingType, struct.context, bindings); - - // joinpoint, staticJoinpoint binding - int extraArgument = extractExtraArgument(struct.method); - - Pointcut pc = null; - if (preResolvedPointcut != null) { - pc = preResolvedPointcut.getPointcut(); - } else { - pc = parsePointcut(afterAdvice.getValue().stringifyValue(), struct, false); - if (pc == null) { - return false;// parse error - } - pc.resolve(binding); - } - setIgnoreUnboundBindingNames(pc, bindings); - - ISourceLocation sl = struct.context.makeSourceLocation(struct.bMethod.getDeclarationLineNumber(), - struct.bMethod.getDeclarationOffset()); - struct.ajAttributes.add(new AjAttribute.AdviceAttribute(AdviceKind.After, pc, extraArgument, sl.getOffset(), sl - .getOffset() + 1,// FIXME AVASM - struct.context)); - return true; - } - } - return false; - } - - /** - * Read @AfterReturning - * - * @param runtimeAnnotations - * @param struct - * @return true if found - */ - private static boolean handleAfterReturningAnnotation(RuntimeAnnos runtimeAnnotations, AjAttributeMethodStruct struct, - ResolvedPointcutDefinition preResolvedPointcut, BcelMethod owningMethod) - throws ReturningFormalNotDeclaredInAdviceSignatureException { - AnnotationGen after = getAnnotation(runtimeAnnotations, AjcMemberMaker.AFTERRETURNING_ANNOTATION); - if (after != null) { - NameValuePair annValue = getAnnotationElement(after, VALUE); - NameValuePair annPointcut = getAnnotationElement(after, POINTCUT); - NameValuePair annReturned = getAnnotationElement(after, RETURNING); - - // extract the pointcut and returned type/binding - do some checks - String pointcut = null; - String returned = null; - if ((annValue != null && annPointcut != null) || (annValue == null && annPointcut == null)) { - reportError("@AfterReturning: either 'value' or 'poincut' must be provided, not both", struct); - return false; - } - if (annValue != null) { - pointcut = annValue.getValue().stringifyValue(); - } else { - pointcut = annPointcut.getValue().stringifyValue(); - } - if (isNullOrEmpty(pointcut)) { - reportError("@AfterReturning: either 'value' or 'poincut' must be provided, not both", struct); - return false; - } - if (annReturned != null) { - returned = annReturned.getValue().stringifyValue(); - if (isNullOrEmpty(returned)) { - returned = null; - } else { - // check that thrownFormal exists as the last parameter in - // the advice - String[] pNames = owningMethod.getParameterNames(); - if (pNames == null || pNames.length == 0 || !Arrays.asList(pNames).contains(returned)) { - throw new ReturningFormalNotDeclaredInAdviceSignatureException(returned); - } - } - } - String argumentNames = getArgNamesValue(after); - if (argumentNames != null) { - struct.unparsedArgumentNames = argumentNames; - } - // this/target/args binding - // exclude the return binding from the pointcut binding since it is - // an extraArg binding - FormalBinding[] bindings = new org.aspectj.weaver.patterns.FormalBinding[0]; - try { - bindings = (returned == null ? extractBindings(struct) : extractBindings(struct, returned)); - } catch (UnreadableDebugInfoException unreadableDebugInfoException) { - return false; - } - IScope binding = new BindingScope(struct.enclosingType, struct.context, bindings); - - // joinpoint, staticJoinpoint binding - int extraArgument = extractExtraArgument(struct.method); - - // return binding - if (returned != null) { - extraArgument |= Advice.ExtraArgument; - } - - Pointcut pc = null; - if (preResolvedPointcut != null) { - pc = preResolvedPointcut.getPointcut(); - } else { - pc = parsePointcut(pointcut, struct, false); - if (pc == null) { - return false;// parse error - } - pc.resolve(binding); - } - setIgnoreUnboundBindingNames(pc, bindings); - - ISourceLocation sl = struct.context.makeSourceLocation(struct.bMethod.getDeclarationLineNumber(), - struct.bMethod.getDeclarationOffset()); - struct.ajAttributes.add(new AjAttribute.AdviceAttribute(AdviceKind.AfterReturning, pc, extraArgument, sl.getOffset(), - sl.getOffset() + 1,// FIXME AVASM - struct.context)); - return true; - } - return false; - } - - /** - * Read @AfterThrowing - * - * @param runtimeAnnotations - * @param struct - * @return true if found - */ - private static boolean handleAfterThrowingAnnotation(RuntimeAnnos runtimeAnnotations, AjAttributeMethodStruct struct, - ResolvedPointcutDefinition preResolvedPointcut, BcelMethod owningMethod) - throws ThrownFormalNotDeclaredInAdviceSignatureException { - AnnotationGen after = getAnnotation(runtimeAnnotations, AjcMemberMaker.AFTERTHROWING_ANNOTATION); - if (after != null) { - NameValuePair annValue = getAnnotationElement(after, VALUE); - NameValuePair annPointcut = getAnnotationElement(after, POINTCUT); - NameValuePair annThrown = getAnnotationElement(after, THROWING); - - // extract the pointcut and throwned type/binding - do some checks - String pointcut = null; - String thrownFormal = null; - if ((annValue != null && annPointcut != null) || (annValue == null && annPointcut == null)) { - reportError("@AfterThrowing: either 'value' or 'poincut' must be provided, not both", struct); - return false; - } - if (annValue != null) { - pointcut = annValue.getValue().stringifyValue(); - } else { - pointcut = annPointcut.getValue().stringifyValue(); - } - if (isNullOrEmpty(pointcut)) { - reportError("@AfterThrowing: either 'value' or 'poincut' must be provided, not both", struct); - return false; - } - if (annThrown != null) { - thrownFormal = annThrown.getValue().stringifyValue(); - if (isNullOrEmpty(thrownFormal)) { - thrownFormal = null; - } else { - // check that thrownFormal exists as the last parameter in - // the advice - String[] pNames = owningMethod.getParameterNames(); - if (pNames == null || pNames.length == 0 || !Arrays.asList(pNames).contains(thrownFormal)) { - throw new ThrownFormalNotDeclaredInAdviceSignatureException(thrownFormal); - } - } - } - String argumentNames = getArgNamesValue(after); - if (argumentNames != null) { - struct.unparsedArgumentNames = argumentNames; - } - // this/target/args binding - // exclude the throwned binding from the pointcut binding since it - // is an extraArg binding - FormalBinding[] bindings = new org.aspectj.weaver.patterns.FormalBinding[0]; - try { - bindings = (thrownFormal == null ? extractBindings(struct) : extractBindings(struct, thrownFormal)); - } catch (UnreadableDebugInfoException unreadableDebugInfoException) { - return false; - } - IScope binding = new BindingScope(struct.enclosingType, struct.context, bindings); - - // joinpoint, staticJoinpoint binding - int extraArgument = extractExtraArgument(struct.method); - - // return binding - if (thrownFormal != null) { - extraArgument |= Advice.ExtraArgument; - } - - Pointcut pc = null; - if (preResolvedPointcut != null) { - pc = preResolvedPointcut.getPointcut(); - } else { - pc = parsePointcut(pointcut, struct, false); - if (pc == null) { - return false;// parse error - } - pc.resolve(binding); - } - setIgnoreUnboundBindingNames(pc, bindings); - - ISourceLocation sl = struct.context.makeSourceLocation(struct.bMethod.getDeclarationLineNumber(), - struct.bMethod.getDeclarationOffset()); - struct.ajAttributes.add(new AjAttribute.AdviceAttribute(AdviceKind.AfterThrowing, pc, extraArgument, sl.getOffset(), sl - .getOffset() + 1, struct.context)); - return true; - } - return false; - } - - /** - * Read @Around - * - * @param runtimeAnnotations - * @param struct - * @return true if found - */ - private static boolean handleAroundAnnotation(RuntimeAnnos runtimeAnnotations, AjAttributeMethodStruct struct, - ResolvedPointcutDefinition preResolvedPointcut) { - AnnotationGen around = getAnnotation(runtimeAnnotations, AjcMemberMaker.AROUND_ANNOTATION); - if (around != null) { - NameValuePair aroundAdvice = getAnnotationElement(around, VALUE); - if (aroundAdvice != null) { - // this/target/args binding - String argumentNames = getArgNamesValue(around); - if (argumentNames != null) { - struct.unparsedArgumentNames = argumentNames; - } - FormalBinding[] bindings = new org.aspectj.weaver.patterns.FormalBinding[0]; - try { - bindings = extractBindings(struct); - } catch (UnreadableDebugInfoException unreadableDebugInfoException) { - return false; - } - IScope binding = new BindingScope(struct.enclosingType, struct.context, bindings); - - // joinpoint, staticJoinpoint binding - int extraArgument = extractExtraArgument(struct.method); - - Pointcut pc = null; - if (preResolvedPointcut != null) { - pc = preResolvedPointcut.getPointcut(); - } else { - pc = parsePointcut(aroundAdvice.getValue().stringifyValue(), struct, false); - if (pc == null) { - return false;// parse error - } - pc.resolve(binding); - } - setIgnoreUnboundBindingNames(pc, bindings); - - ISourceLocation sl = struct.context.makeSourceLocation(struct.bMethod.getDeclarationLineNumber(), - struct.bMethod.getDeclarationOffset()); - struct.ajAttributes.add(new AjAttribute.AdviceAttribute(AdviceKind.Around, pc, extraArgument, sl.getOffset(), sl - .getOffset() + 1,// FIXME AVASM - struct.context)); - return true; - } - } - return false; - } - - /** - * Read @Pointcut and handle the resolving in a lazy way to deal with pointcut references - * - * @param runtimeAnnotations - * @param struct - * @return true if a pointcut was handled - */ - private static boolean handlePointcutAnnotation(RuntimeAnnos runtimeAnnotations, AjAttributeMethodStruct struct) { - AnnotationGen pointcut = getAnnotation(runtimeAnnotations, AjcMemberMaker.POINTCUT_ANNOTATION); - if (pointcut == null) { - return false; - } - NameValuePair pointcutExpr = getAnnotationElement(pointcut, VALUE); - - // semantic check: the method must return void, or be - // "public static boolean" for if() support - if (!(Type.VOID.equals(struct.method.getReturnType()) || (Type.BOOLEAN.equals(struct.method.getReturnType()) - && struct.method.isStatic() && struct.method.isPublic()))) { - reportWarning("Found @Pointcut on a method not returning 'void' or not 'public static boolean'", struct); - // no need to stop - } - - // semantic check: the method must not throw anything - if (struct.method.getExceptionTable() != null) { - reportWarning("Found @Pointcut on a method throwing exception", struct); - // no need to stop - } - - String argumentNames = getArgNamesValue(pointcut); - if (argumentNames != null) { - struct.unparsedArgumentNames = argumentNames; - } - // this/target/args binding - final IScope binding; - try { - if (struct.method.isAbstract()) { - binding = null; - } else { - binding = new BindingScope(struct.enclosingType, struct.context, extractBindings(struct)); - } - } catch (UnreadableDebugInfoException e) { - return false; - } - - UnresolvedType[] argumentTypes = new UnresolvedType[struct.method.getArgumentTypes().length]; - for (int i = 0; i < argumentTypes.length; i++) { - argumentTypes[i] = UnresolvedType.forSignature(struct.method.getArgumentTypes()[i].getSignature()); - } - - Pointcut pc = null; - if (struct.method.isAbstract()) { - if ((pointcutExpr != null && isNullOrEmpty(pointcutExpr.getValue().stringifyValue())) || pointcutExpr == null) { - // abstract pointcut - // leave pc = null - } else { - reportError("Found defined @Pointcut on an abstract method", struct); - return false;// stop - } - } else { - if (pointcutExpr == null || isNullOrEmpty(pointcutExpr.getValue().stringifyValue())) { - // the matches nothing pointcut (125475/125480) - perhaps not as - // cleanly supported as it could be. - } else { - // if (pointcutExpr != null) { - // use a LazyResolvedPointcutDefinition so that the pointcut is - // resolved lazily - // since for it to be resolved, we will need other pointcuts to - // be registered as well - pc = parsePointcut(pointcutExpr.getValue().stringifyValue(), struct, true); - if (pc == null) { - return false;// parse error - } - pc.setLocation(struct.context, -1, -1);// FIXME AVASM !! bMethod - // is null here.. - // } else { - // reportError("Found undefined @Pointcut on a non-abstract method", - // struct); - // return false; - // } - } - } - // do not resolve binding now but lazily - struct.ajAttributes.add(new AjAttribute.PointcutDeclarationAttribute(new LazyResolvedPointcutDefinition( - struct.enclosingType, struct.method.getModifiers(), struct.method.getName(), argumentTypes, UnresolvedType - .forSignature(struct.method.getReturnType().getSignature()), pc,// can - // be - // null - // for - // abstract - // pointcut - binding // can be null for abstract pointcut - ))); - return true; - } - - /** - * Read @DeclareError, @DeclareWarning - * - * @param runtimeAnnotations - * @param struct - * @return true if found - */ - private static boolean handleDeclareErrorOrWarningAnnotation(AsmManager model, RuntimeAnnos runtimeAnnotations, - AjAttributeFieldStruct struct) { - AnnotationGen error = getAnnotation(runtimeAnnotations, AjcMemberMaker.DECLAREERROR_ANNOTATION); - boolean hasError = false; - if (error != null) { - NameValuePair declareError = getAnnotationElement(error, VALUE); - if (declareError != null) { - if (!STRING_DESC.equals(struct.field.getSignature()) || struct.field.getConstantValue() == null) { - reportError("@DeclareError used on a non String constant field", struct); - return false; - } - Pointcut pc = parsePointcut(declareError.getValue().stringifyValue(), struct, false); - if (pc == null) { - hasError = false;// cannot parse pointcut - } else { - DeclareErrorOrWarning deow = new DeclareErrorOrWarning(true, pc, struct.field.getConstantValue().toString()); - setDeclareErrorOrWarningLocation(model, deow, struct); - struct.ajAttributes.add(new AjAttribute.DeclareAttribute(deow)); - hasError = true; - } - } - } - AnnotationGen warning = getAnnotation(runtimeAnnotations, AjcMemberMaker.DECLAREWARNING_ANNOTATION); - boolean hasWarning = false; - if (warning != null) { - NameValuePair declareWarning = getAnnotationElement(warning, VALUE); - if (declareWarning != null) { - if (!STRING_DESC.equals(struct.field.getSignature()) || struct.field.getConstantValue() == null) { - reportError("@DeclareWarning used on a non String constant field", struct); - return false; - } - Pointcut pc = parsePointcut(declareWarning.getValue().stringifyValue(), struct, false); - if (pc == null) { - hasWarning = false;// cannot parse pointcut - } else { - DeclareErrorOrWarning deow = new DeclareErrorOrWarning(false, pc, struct.field.getConstantValue().toString()); - setDeclareErrorOrWarningLocation(model, deow, struct); - struct.ajAttributes.add(new AjAttribute.DeclareAttribute(deow)); - return hasWarning = true; - } - } - } - return hasError || hasWarning; - } - - /** - * Sets the location for the declare error / warning using the corresponding IProgramElement in the structure model. This will - * only fix bug 120356 if compiled with -emacssym, however, it does mean that the cross references view in AJDT will show the - * correct information. - * - * Other possibilities for fix: 1. using the information in ajcDeclareSoft (if this is set correctly) which will fix the problem - * if compiled with ajc but not if compiled with javac. 2. creating an AjAttribute called FieldDeclarationLineNumberAttribute - * (much like MethodDeclarationLineNumberAttribute) which we can ask for the offset. This will again only fix bug 120356 when - * compiled with ajc. - * - * @param deow - * @param struct - */ - private static void setDeclareErrorOrWarningLocation(AsmManager model, DeclareErrorOrWarning deow, AjAttributeFieldStruct struct) { - IHierarchy top = (model == null ? null : model.getHierarchy()); - if (top != null && top.getRoot() != null) { - IProgramElement ipe = top.findElementForLabel(top.getRoot(), IProgramElement.Kind.FIELD, struct.field.getName()); - if (ipe != null && ipe.getSourceLocation() != null) { - ISourceLocation sourceLocation = ipe.getSourceLocation(); - int start = sourceLocation.getOffset(); - int end = start + struct.field.getName().length(); - deow.setLocation(struct.context, start, end); - return; - } - } - deow.setLocation(struct.context, -1, -1); - } - - /** - * Returns a readable representation of a method. Method.toString() is not suitable. - * - * @param method - * @return a readable representation of a method - */ - private static String methodToString(Method method) { - StringBuffer sb = new StringBuffer(); - sb.append(method.getName()); - sb.append(method.getSignature()); - return sb.toString(); - } - - /** - * Build the bindings for a given method (pointcut / advice) - * - * @param struct - * @return null if no debug info is available - */ - private static FormalBinding[] extractBindings(AjAttributeMethodStruct struct) throws UnreadableDebugInfoException { - Method method = struct.method; - String[] argumentNames = struct.getArgumentNames(); - - // assert debug info was here - if (argumentNames.length != method.getArgumentTypes().length) { - reportError( - "Cannot read debug info for @Aspect to handle formal binding in pointcuts (please compile with 'javac -g' or '' in Ant)", - struct); - throw new UnreadableDebugInfoException(); - } - - List bindings = new ArrayList(); - for (int i = 0; i < argumentNames.length; i++) { - String argumentName = argumentNames[i]; - UnresolvedType argumentType = UnresolvedType.forSignature(method.getArgumentTypes()[i].getSignature()); - - // do not bind JoinPoint / StaticJoinPoint / - // EnclosingStaticJoinPoint - // TODO solve me : this means that the JP/SJP/ESJP cannot appear as - // binding - // f.e. when applying advice on advice etc - if ((AjcMemberMaker.TYPEX_JOINPOINT.equals(argumentType) - || AjcMemberMaker.TYPEX_PROCEEDINGJOINPOINT.equals(argumentType) - || AjcMemberMaker.TYPEX_STATICJOINPOINT.equals(argumentType) - || AjcMemberMaker.TYPEX_ENCLOSINGSTATICJOINPOINT.equals(argumentType) || AjcMemberMaker.AROUND_CLOSURE_TYPE - .equals(argumentType))) { - // continue;// skip - bindings.add(new FormalBinding.ImplicitFormalBinding(argumentType, argumentName, i)); - } else { - bindings.add(new FormalBinding(argumentType, argumentName, i)); - } - } - - return bindings.toArray(new FormalBinding[] {}); - } - - // FIXME alex deal with exclude index - private static FormalBinding[] extractBindings(AjAttributeMethodStruct struct, String excludeFormal) - throws UnreadableDebugInfoException { - FormalBinding[] bindings = extractBindings(struct); - // int excludeIndex = -1; - for (int i = 0; i < bindings.length; i++) { - FormalBinding binding = bindings[i]; - if (binding.getName().equals(excludeFormal)) { - // excludeIndex = i; - bindings[i] = new FormalBinding.ImplicitFormalBinding(binding.getType(), binding.getName(), binding.getIndex()); - break; - } - } - return bindings; - // - // if (excludeIndex >= 0) { - // FormalBinding[] bindingsFiltered = new - // FormalBinding[bindings.length-1]; - // int k = 0; - // for (int i = 0; i < bindings.length; i++) { - // if (i == excludeIndex) { - // ; - // } else { - // bindingsFiltered[k] = new FormalBinding(bindings[i].getType(), - // bindings[i].getName(), k); - // k++; - // } - // } - // return bindingsFiltered; - // } else { - // return bindings; - // } - } - - /** - * Compute the flag for the xxxJoinPoint extra argument - * - * @param method - * @return extra arg flag - */ - private static int extractExtraArgument(Method method) { - Type[] methodArgs = method.getArgumentTypes(); - String[] sigs = new String[methodArgs.length]; - for (int i = 0; i < methodArgs.length; i++) { - sigs[i] = methodArgs[i].getSignature(); - } - return extractExtraArgument(sigs); - } - - /** - * Compute the flag for the xxxJoinPoint extra argument - * - * @param argumentSignatures - * @return extra arg flag - */ - public static int extractExtraArgument(String[] argumentSignatures) { - int extraArgument = 0; - for (int i = 0; i < argumentSignatures.length; i++) { - if (AjcMemberMaker.TYPEX_JOINPOINT.getSignature().equals(argumentSignatures[i])) { - extraArgument |= Advice.ThisJoinPoint; - } else if (AjcMemberMaker.TYPEX_PROCEEDINGJOINPOINT.getSignature().equals(argumentSignatures[i])) { - extraArgument |= Advice.ThisJoinPoint; - } else if (AjcMemberMaker.TYPEX_STATICJOINPOINT.getSignature().equals(argumentSignatures[i])) { - extraArgument |= Advice.ThisJoinPointStaticPart; - } else if (AjcMemberMaker.TYPEX_ENCLOSINGSTATICJOINPOINT.getSignature().equals(argumentSignatures[i])) { - extraArgument |= Advice.ThisEnclosingJoinPointStaticPart; - } - } - return extraArgument; - } - - /** - * Returns the runtime (RV/RIV) annotation of type annotationType or null if no such annotation - * - * @param rvs - * @param annotationType - * @return annotation - */ - private static AnnotationGen getAnnotation(RuntimeAnnos rvs, UnresolvedType annotationType) { - final String annotationTypeName = annotationType.getName(); - for (AnnotationGen rv : rvs.getAnnotations()) { - if (annotationTypeName.equals(rv.getTypeName())) { - return rv; - } - } - return null; - } - - /** - * Returns the value of a given element of an annotation or null if not found Caution: Does not handles default value. - * - * @param annotation - * @param elementName - * @return annotation NVP - */ - private static NameValuePair getAnnotationElement(AnnotationGen annotation, String elementName) { - for (NameValuePair element : annotation.getValues()) { - if (elementName.equals(element.getNameString())) { - return element; - } - } - return null; - } - - /** - * Return the argNames set for an annotation or null if it is not specified. - */ - private static String getArgNamesValue(AnnotationGen anno) { - List elements = anno.getValues(); - for (NameValuePair element : elements) { - if (ARGNAMES.equals(element.getNameString())) { - return element.getValue().stringifyValue(); - } - } - return null; - } - - private static String lastbit(String fqname) { - int i = fqname.lastIndexOf("."); - if (i == -1) { - return fqname; - } else { - return fqname.substring(i + 1); - } - } - - /** - * Extract the method argument names. First we try the debug info attached to the method (the LocalVariableTable) - if we cannot - * find that we look to use the argNames value that may have been supplied on the associated annotation. If that fails we just - * don't know and return an empty string. - * - * @param method - * @param argNamesFromAnnotation - * @param methodStruct - * @return method argument names - */ - private static String[] getMethodArgumentNames(Method method, String argNamesFromAnnotation, - AjAttributeMethodStruct methodStruct) { - if (method.getArgumentTypes().length == 0) { - return EMPTY_STRINGS; - } - - final int startAtStackIndex = method.isStatic() ? 0 : 1; - final List arguments = new ArrayList(); - LocalVariableTable lt = method.getLocalVariableTable(); - if (lt != null) { - LocalVariable[] lvt = lt.getLocalVariableTable(); - for (int j = 0; j < lvt.length; j++) { - LocalVariable localVariable = lvt[j]; - if (localVariable != null) { // pr348488 - if (localVariable.getStartPC() == 0) { - if (localVariable.getIndex() >= startAtStackIndex) { - arguments.add(new MethodArgument(localVariable.getName(), localVariable.getIndex())); - } - } - } else { - String typename = (methodStruct.enclosingType != null ? methodStruct.enclosingType.getName() : ""); - System.err.println("AspectJ: 348488 debug: unusual local variable table for method " + typename + "." - + method.getName()); - } - } - if (arguments.size() == 0) { - // The local variable table is causing us trouble, try the annotation value - // See 539121 for a jacoco variant of the cobertura issue below - if (argNamesFromAnnotation != null) { - String[] argNames = extractArgNamesFromAnnotationValue(method, argNamesFromAnnotation, methodStruct); - if (argNames.length != 0) { - return argNames; - } - } - // could be cobertura code where some extra bytecode has been stuffed in at the start of the method - // but the local variable table hasn't been repaired - for example: - // LocalVariable(start_pc = 6, length = 40, index = 0:com.example.ExampleAspect this) - // LocalVariable(start_pc = 6, length = 40, index = 1:org.aspectj.lang.ProceedingJoinPoint pjp) - // LocalVariable(start_pc = 6, length = 40, index = 2:int __cobertura__line__number__) - // LocalVariable(start_pc = 6, length = 40, index = 3:int __cobertura__branch__number__) - LocalVariable localVariable = lvt[0]; - if (localVariable != null) { // pr348488 - if (localVariable.getStartPC() != 0) { - // looks suspicious so let's use this information - for (int j = 0; j < lvt.length && arguments.size() < method.getArgumentTypes().length; j++) { - localVariable = lvt[j]; - if (localVariable.getIndex() >= startAtStackIndex) { - arguments.add(new MethodArgument(localVariable.getName(), localVariable.getIndex())); - } - } - } - } - } - } else { - if (argNamesFromAnnotation != null) { - String[] argNames = extractArgNamesFromAnnotationValue(method, argNamesFromAnnotation, methodStruct); - if (argNames != null) { - return argNames; - } - } - } - - if (arguments.size() != method.getArgumentTypes().length) { - return EMPTY_STRINGS; - } - - // sort by index - Collections.sort(arguments, new Comparator() { - public int compare(MethodArgument mo, MethodArgument mo1) { - if (mo.indexOnStack == mo1.indexOnStack) { - return 0; - } else if (mo.indexOnStack > mo1.indexOnStack) { - return 1; - } else { - return -1; - } - } - }); - String[] argumentNames = new String[arguments.size()]; - int i = 0; - for (MethodArgument methodArgument : arguments) { - argumentNames[i++] = methodArgument.name; - } - return argumentNames; - } - - private static String[] extractArgNamesFromAnnotationValue(Method method, String argNamesFromAnnotation, - AjAttributeMethodStruct methodStruct) { - StringTokenizer st = new StringTokenizer(argNamesFromAnnotation, " ,"); - List args = new ArrayList(); - while (st.hasMoreTokens()) { - args.add(st.nextToken()); - } - if (args.size() != method.getArgumentTypes().length) { - StringBuffer shortString = new StringBuffer().append(lastbit(method.getReturnType().toString())).append(" ") - .append(method.getName()); - if (method.getArgumentTypes().length > 0) { - shortString.append("("); - for (int i = 0; i < method.getArgumentTypes().length; i++) { - shortString.append(lastbit(method.getArgumentTypes()[i].toString())); - if ((i + 1) < method.getArgumentTypes().length) { - shortString.append(","); - } - - } - shortString.append(")"); - } - reportError("argNames annotation value does not specify the right number of argument names for the method '" - + shortString.toString() + "'", methodStruct); - return EMPTY_STRINGS; - } - return args.toArray(new String[] {}); - } - - /** - * A method argument, used for sorting by indexOnStack (ie order in signature) - * - * @author Alexandre Vasseur - */ - private static class MethodArgument { - String name; - int indexOnStack; - - public MethodArgument(String name, int indexOnStack) { - this.name = name; - this.indexOnStack = indexOnStack; - } - } - - /** - * LazyResolvedPointcutDefinition lazyly resolve the pointcut so that we have time to register all pointcut referenced before - * pointcut resolution happens - * - * @author Alexandre Vasseur - */ - public static class LazyResolvedPointcutDefinition extends ResolvedPointcutDefinition { - private final Pointcut m_pointcutUnresolved; // null for abstract - // pointcut - private final IScope m_binding; - - private Pointcut m_lazyPointcut = null; - - public LazyResolvedPointcutDefinition(UnresolvedType declaringType, int modifiers, String name, - UnresolvedType[] parameterTypes, UnresolvedType returnType, Pointcut pointcut, IScope binding) { - super(declaringType, modifiers, name, parameterTypes, returnType, Pointcut.makeMatchesNothing(Pointcut.RESOLVED)); - m_pointcutUnresolved = pointcut; - m_binding = binding; - } - - @Override - public Pointcut getPointcut() { - if (m_lazyPointcut == null && m_pointcutUnresolved == null) { - m_lazyPointcut = Pointcut.makeMatchesNothing(Pointcut.CONCRETE); - } - if (m_lazyPointcut == null && m_pointcutUnresolved != null) { - m_lazyPointcut = m_pointcutUnresolved.resolve(m_binding); - m_lazyPointcut.copyLocationFrom(m_pointcutUnresolved); - } - return m_lazyPointcut; - } - } - - /** - * Helper to test empty strings - * - * @param s - * @return true if empty or null - */ - private static boolean isNullOrEmpty(String s) { - return (s == null || s.length() <= 0); - } - - /** - * Set the pointcut bindings for which to ignore unbound issues, so that we can implicitly bind xxxJoinPoint for @AJ advices - * - * @param pointcut - * @param bindings - */ - private static void setIgnoreUnboundBindingNames(Pointcut pointcut, FormalBinding[] bindings) { - // register ImplicitBindings as to be ignored since unbound - // TODO is it likely to fail in a bad way if f.e. this(jp) etc ? - List ignores = new ArrayList(); - for (int i = 0; i < bindings.length; i++) { - FormalBinding formalBinding = bindings[i]; - if (formalBinding instanceof FormalBinding.ImplicitFormalBinding) { - ignores.add(formalBinding.getName()); - } - } - pointcut.m_ignoreUnboundBindingForNames = ignores.toArray(new String[ignores.size()]); - } - - /** - * A check exception when we cannot read debug info (needed for formal binding) - */ - private static class UnreadableDebugInfoException extends Exception { - } - - /** - * Report an error - * - * @param message - * @param location - */ - private static void reportError(String message, AjAttributeStruct location) { - if (!location.handler.isIgnoring(IMessage.ERROR)) { - location.handler.handleMessage(new Message(message, location.enclosingType.getSourceLocation(), true)); - } - } - - // private static void reportError(String message, IMessageHandler handler, ISourceLocation sourceLocation) { - // if (!handler.isIgnoring(IMessage.ERROR)) { - // handler.handleMessage(new Message(message, sourceLocation, true)); - // } - // } - - /** - * Report a warning - * - * @param message - * @param location - */ - private static void reportWarning(String message, AjAttributeStruct location) { - if (!location.handler.isIgnoring(IMessage.WARNING)) { - location.handler.handleMessage(new Message(message, location.enclosingType.getSourceLocation(), false)); - } - } - - /** - * Parse the given pointcut, return null on failure and issue an error - * - * @param pointcutString - * @param struct - * @param allowIf - * @return pointcut, unresolved - */ - private static Pointcut parsePointcut(String pointcutString, AjAttributeStruct struct, boolean allowIf) { - try { - PatternParser parser = new PatternParser(pointcutString, struct.context); - Pointcut pointcut = parser.parsePointcut(); - parser.checkEof(); - pointcut.check(null, struct.enclosingType.getWorld()); - if (!allowIf && pointcutString.indexOf("if()") >= 0 && hasIf(pointcut)) { - reportError("if() pointcut is not allowed at this pointcut location '" + pointcutString + "'", struct); - return null; - } - pointcut.setLocation(struct.context, -1, -1);// FIXME -1,-1 is not - // good enough - return pointcut; - } catch (ParserException e) { - reportError("Invalid pointcut '" + pointcutString + "': " + e.toString() - + (e.getLocation() == null ? "" : " at position " + e.getLocation().getStart()), struct); - return null; - } - } - - private static boolean hasIf(Pointcut pointcut) { - IfFinder visitor = new IfFinder(); - pointcut.accept(visitor, null); - return visitor.hasIf; - } - - /** - * Parse the given type pattern, return null on failure and issue an error - * - * @param patternString - * @param location - * @return type pattern - */ - private static TypePattern parseTypePattern(String patternString, AjAttributeStruct location) { - try { - TypePattern typePattern = new PatternParser(patternString).parseTypePattern(); - typePattern.setLocation(location.context, -1, -1);// FIXME -1,-1 is - // not good - // enough - return typePattern; - } catch (ParserException e) { - reportError("Invalid type pattern'" + patternString + "' : " + e.getLocation(), location); - return null; - } - } - - static class ThrownFormalNotDeclaredInAdviceSignatureException extends Exception { - - private final String formalName; - - public ThrownFormalNotDeclaredInAdviceSignatureException(String formalName) { - this.formalName = formalName; - } - - public String getFormalName() { - return formalName; - } - } - - static class ReturningFormalNotDeclaredInAdviceSignatureException extends Exception { - - private final String formalName; - - public ReturningFormalNotDeclaredInAdviceSignatureException(String formalName) { - this.formalName = formalName; - } - - public String getFormalName() { - return formalName; - } - } -} \ No newline at end of file diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelAccessForInlineMunger.java b/weaver/src/org/aspectj/weaver/bcel/BcelAccessForInlineMunger.java deleted file mode 100644 index b81f7ffb1..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/BcelAccessForInlineMunger.java +++ /dev/null @@ -1,386 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2005 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * Alexandre Vasseur initial implementation - *******************************************************************************/ -package org.aspectj.weaver.bcel; - -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.aspectj.apache.bcel.Constants; -import org.aspectj.apache.bcel.classfile.ConstantPool; -import org.aspectj.apache.bcel.generic.FieldInstruction; -import org.aspectj.apache.bcel.generic.Instruction; -import org.aspectj.apache.bcel.generic.InstructionConstants; -import org.aspectj.apache.bcel.generic.InstructionFactory; -import org.aspectj.apache.bcel.generic.InstructionHandle; -import org.aspectj.apache.bcel.generic.InstructionList; -import org.aspectj.apache.bcel.generic.InvokeDynamic; -import org.aspectj.apache.bcel.generic.InvokeInstruction; -import org.aspectj.apache.bcel.generic.Type; -import org.aspectj.weaver.AjAttribute; -import org.aspectj.weaver.AjcMemberMaker; -import org.aspectj.weaver.Member; -import org.aspectj.weaver.NameMangler; -import org.aspectj.weaver.ResolvedMember; -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.Shadow; -import org.aspectj.weaver.UnresolvedType; - -/** - * Looks for all access to method or field that are not public within the body of the around advices and replace the invocations to - * a wrapper call so that the around advice can further be inlined. - *

- * This munger is used for @AJ aspects for which inlining wrapper is not done at compile time. - *

- * Specific state and logic is kept in the munger ala ITD so that call/get/set pointcuts can still be matched on the wrapped member - * thanks to the EffectiveSignature attribute. - * - * @author Alexandre Vasseur - * @author Andy Clement - */ -public class BcelAccessForInlineMunger extends BcelTypeMunger { - - private Map inlineAccessors; - - private LazyClassGen aspectGen; - - /** - * The wrapper methods representing any created inlineAccessors - */ - private Set inlineAccessorMethodGens; - - public BcelAccessForInlineMunger(ResolvedType aspectType) { - super(null, aspectType); - if (aspectType.getWorld().isXnoInline()) { - throw new Error("This should not happen"); - } - } - - @Override - public boolean munge(BcelClassWeaver weaver) { - aspectGen = weaver.getLazyClassGen(); - inlineAccessors = new HashMap(0); - inlineAccessorMethodGens = new HashSet(); - - // look for all @Around advices - for (LazyMethodGen methodGen : aspectGen.getMethodGens()) { - if (methodGen.hasAnnotation(UnresolvedType.forName("org/aspectj/lang/annotation/Around"))) { - openAroundAdvice(methodGen); - } - } - - // add the accessors - for (LazyMethodGen lazyMethodGen : inlineAccessorMethodGens) { - aspectGen.addMethodGen(lazyMethodGen); - } - - // flush some - inlineAccessorMethodGens = null; - // we keep m_inlineAccessorsResolvedMembers for shadow matching - - return true; - } - - /** - * Looks in the wrapper we have added so that we can find their effective signature if needed - */ - @Override - public ResolvedMember getMatchingSyntheticMember(Member member) { - ResolvedMember rm = inlineAccessors.get(member.getName());// + member.getSignature()); -// System.err.println("lookup for " + member.getName() + ":" + member.getSignature() + " = " -// + (rm == null ? "" : rm.getName())); - return rm; - } - - @Override - public ResolvedMember getSignature() { - return null; - } - - /** - * Match only the aspect for which we act - */ - @Override - public boolean matches(ResolvedType onType) { - return aspectType.equals(onType); - } - - /** - * Prepare the around advice, flag it as cannot be inlined if it can't be - */ - private void openAroundAdvice(LazyMethodGen aroundAdvice) { - InstructionHandle curr = aroundAdvice.getBody().getStart(); - InstructionHandle end = aroundAdvice.getBody().getEnd(); - ConstantPool cpg = aroundAdvice.getEnclosingClass().getConstantPool(); - InstructionFactory factory = aroundAdvice.getEnclosingClass().getFactory(); - - boolean realizedCannotInline = false; - while (curr != end) { - if (realizedCannotInline) { - // we know we cannot inline this advice so no need for futher handling - break; - } - InstructionHandle next = curr.getNext(); - Instruction inst = curr.getInstruction(); - - // open-up method call - if ((inst instanceof InvokeInstruction)) { - InvokeInstruction invoke = (InvokeInstruction) inst; - if (invoke instanceof InvokeDynamic) { - realizedCannotInline = true; - break; - } - ResolvedType callee = aspectGen.getWorld().resolve(UnresolvedType.forName(invoke.getClassName(cpg))); - - // look in the whole method list and not just declared for super calls and alike - List methods = callee.getMethodsWithoutIterator(false, true, false); - for (ResolvedMember resolvedMember : methods) { - if (invoke.getName(cpg).equals(resolvedMember.getName()) - && invoke.getSignature(cpg).equals(resolvedMember.getSignature()) && !resolvedMember.isPublic()) { - if ("".equals(invoke.getName(cpg))) { - // skipping open up for private constructor - // can occur when aspect new a private inner type - // too complex to handle new + dup + .. + invokespecial here. - aroundAdvice.setCanInline(false); - realizedCannotInline = true; - } else { - // specific handling for super.foo() calls, where foo is non public - ResolvedType memberType = aspectGen.getWorld().resolve(resolvedMember.getDeclaringType()); - if (!aspectType.equals(memberType) && memberType.isAssignableFrom(aspectType)) { - // old test was... - // if (aspectType.getSuperclass() != null - // && aspectType.getSuperclass().getName().equals(resolvedMember.getDeclaringType().getName())) { - ResolvedMember accessor = createOrGetInlineAccessorForSuperDispatch(resolvedMember); - InvokeInstruction newInst = factory.createInvoke(aspectType.getName(), accessor.getName(), - BcelWorld.makeBcelType(accessor.getReturnType()), - BcelWorld.makeBcelTypes(accessor.getParameterTypes()), Constants.INVOKEVIRTUAL); - curr.setInstruction(newInst); - } else { - ResolvedMember accessor = createOrGetInlineAccessorForMethod(resolvedMember); - InvokeInstruction newInst = factory.createInvoke(aspectType.getName(), accessor.getName(), - BcelWorld.makeBcelType(accessor.getReturnType()), - BcelWorld.makeBcelTypes(accessor.getParameterTypes()), Constants.INVOKESTATIC); - curr.setInstruction(newInst); - } - } - - break;// ok we found a matching callee member and swapped the instruction with the accessor - } - } - } else if (inst instanceof FieldInstruction) { - FieldInstruction invoke = (FieldInstruction) inst; - ResolvedType callee = aspectGen.getWorld().resolve(UnresolvedType.forName(invoke.getClassName(cpg))); - for (int i = 0; i < callee.getDeclaredJavaFields().length; i++) { - ResolvedMember resolvedMember = callee.getDeclaredJavaFields()[i]; - if (invoke.getName(cpg).equals(resolvedMember.getName()) - && invoke.getSignature(cpg).equals(resolvedMember.getSignature()) && !resolvedMember.isPublic()) { - final ResolvedMember accessor; - if ((inst.opcode == Constants.GETFIELD) || (inst.opcode == Constants.GETSTATIC)) { - accessor = createOrGetInlineAccessorForFieldGet(resolvedMember); - } else { - accessor = createOrGetInlineAccessorForFieldSet(resolvedMember); - } - InvokeInstruction newInst = factory.createInvoke(aspectType.getName(), accessor.getName(), - BcelWorld.makeBcelType(accessor.getReturnType()), - BcelWorld.makeBcelTypes(accessor.getParameterTypes()), Constants.INVOKESTATIC); - curr.setInstruction(newInst); - - break;// ok we found a matching callee member and swapped the instruction with the accessor - } - } - } - - curr = next; - } - - // no reason for not inlining this advice - // since it is used for @AJ advice that cannot be inlined by defauilt - // make sure we set inline to true since we have done this analysis - if (!realizedCannotInline) { - aroundAdvice.setCanInline(true); - } - } - - /** - * Find (or add if not yet created) an inline wrapper for a non public method call - */ - private ResolvedMember createOrGetInlineAccessorForMethod(ResolvedMember resolvedMember) { - String accessorName = NameMangler.inlineAccessMethodForMethod(resolvedMember.getName(), resolvedMember.getDeclaringType(), - aspectType); - String key = accessorName;// new StringBuilder(accessorName).append(resolvedMember.getSignature()).toString(); - ResolvedMember inlineAccessor = inlineAccessors.get(key); -// System.err.println(key + " accessor=" + inlineAccessor); - if (inlineAccessor == null) { - // add static method to aspect - inlineAccessor = AjcMemberMaker.inlineAccessMethodForMethod(aspectType, resolvedMember); - - // add new accessor method to aspect bytecode - InstructionFactory factory = aspectGen.getFactory(); - LazyMethodGen method = makeMethodGen(aspectGen, inlineAccessor); - method.makeSynthetic(); - List methodAttributes = new ArrayList(); - methodAttributes.add(new AjAttribute.AjSynthetic()); - methodAttributes.add(new AjAttribute.EffectiveSignatureAttribute(resolvedMember, Shadow.MethodCall, false)); - method.addAttribute(Utility.bcelAttribute(methodAttributes.get(0), aspectGen.getConstantPool())); - // flag the effective signature, so that we can deobfuscate the signature to apply method call pointcut - method.addAttribute(Utility.bcelAttribute(methodAttributes.get(1), aspectGen.getConstantPool())); - - inlineAccessorMethodGens.add(method); - - InstructionList il = method.getBody(); - int register = 0; - for (int i = 0, max = inlineAccessor.getParameterTypes().length; i < max; i++) { - UnresolvedType ptype = inlineAccessor.getParameterTypes()[i]; - Type type = BcelWorld.makeBcelType(ptype); - il.append(InstructionFactory.createLoad(type, register)); - register += type.getSize(); - } - il.append(Utility.createInvoke(factory, Modifier.isStatic(resolvedMember.getModifiers()) ? Constants.INVOKESTATIC - : Constants.INVOKEVIRTUAL, resolvedMember)); - il.append(InstructionFactory.createReturn(BcelWorld.makeBcelType(inlineAccessor.getReturnType()))); - - inlineAccessors.put(key, new BcelMethod(aspectGen.getBcelObjectType(), method.getMethod(), methodAttributes)); - } - return inlineAccessor; - } - - /** - * Add an inline wrapper for a non public super.method call - */ - private ResolvedMember createOrGetInlineAccessorForSuperDispatch(ResolvedMember resolvedMember) { - String accessor = NameMangler.superDispatchMethod(aspectType, resolvedMember.getName()); - - String key = accessor; - ResolvedMember inlineAccessor = inlineAccessors.get(key); - - if (inlineAccessor == null) { - // add super accessor method to class: - inlineAccessor = AjcMemberMaker.superAccessMethod(aspectType, resolvedMember); - - // add new accessor method to aspect bytecode - InstructionFactory factory = aspectGen.getFactory(); - LazyMethodGen method = makeMethodGen(aspectGen, inlineAccessor); - // flag it synthetic, AjSynthetic - method.makeSynthetic(); - List methodAttributes = new ArrayList(); - methodAttributes.add(new AjAttribute.AjSynthetic()); - methodAttributes.add(new AjAttribute.EffectiveSignatureAttribute(resolvedMember, Shadow.MethodCall, false)); - method.addAttribute(Utility.bcelAttribute(methodAttributes.get(0), aspectGen.getConstantPool())); - // flag the effective signature, so that we can deobfuscate the signature to apply method call pointcut - method.addAttribute(Utility.bcelAttribute(methodAttributes.get(1), aspectGen.getConstantPool())); - - inlineAccessorMethodGens.add(method); - - InstructionList il = method.getBody(); - il.append(InstructionConstants.ALOAD_0); - int register = 1; - for (int i = 0; i < inlineAccessor.getParameterTypes().length; i++) { - UnresolvedType typeX = inlineAccessor.getParameterTypes()[i]; - Type type = BcelWorld.makeBcelType(typeX); - il.append(InstructionFactory.createLoad(type, register)); - register += type.getSize(); - } - il.append(Utility.createInvoke(factory, Constants.INVOKESPECIAL, resolvedMember)); - il.append(InstructionFactory.createReturn(BcelWorld.makeBcelType(inlineAccessor.getReturnType()))); - - inlineAccessors.put(key, new BcelMethod(aspectGen.getBcelObjectType(), method.getMethod(), methodAttributes)); - } - return inlineAccessor; - } - - /** - * Add an inline wrapper for a non public field get - */ - private ResolvedMember createOrGetInlineAccessorForFieldGet(ResolvedMember resolvedMember) { - String accessor = NameMangler.inlineAccessMethodForFieldGet(resolvedMember.getName(), resolvedMember.getDeclaringType(), - aspectType); - String key = accessor; - ResolvedMember inlineAccessor = inlineAccessors.get(key); - - if (inlineAccessor == null) { - // add static method to aspect - inlineAccessor = AjcMemberMaker.inlineAccessMethodForFieldGet(aspectType, resolvedMember); - - // add new accessor method to aspect bytecode - InstructionFactory factory = aspectGen.getFactory(); - LazyMethodGen method = makeMethodGen(aspectGen, inlineAccessor); - // flag it synthetic, AjSynthetic - method.makeSynthetic(); - List methodAttributes = new ArrayList(); - methodAttributes.add(new AjAttribute.AjSynthetic()); - methodAttributes.add(new AjAttribute.EffectiveSignatureAttribute(resolvedMember, Shadow.FieldGet, false)); - // flag the effective signature, so that we can deobfuscate the signature to apply method call pointcut - method.addAttribute(Utility.bcelAttribute(methodAttributes.get(0), aspectGen.getConstantPool())); - method.addAttribute(Utility.bcelAttribute(methodAttributes.get(1), aspectGen.getConstantPool())); - - inlineAccessorMethodGens.add(method); - - InstructionList il = method.getBody(); - if (Modifier.isStatic(resolvedMember.getModifiers())) { - // field accessed is static so no "this" as accessor sole parameter - } else { - il.append(InstructionConstants.ALOAD_0); - } - il.append(Utility.createGet(factory, resolvedMember)); - il.append(InstructionFactory.createReturn(BcelWorld.makeBcelType(inlineAccessor.getReturnType()))); - - inlineAccessors.put(key, new BcelMethod(aspectGen.getBcelObjectType(), method.getMethod(), methodAttributes)); - } - return inlineAccessor; - } - - /** - * Add an inline wrapper for a non public field set - */ - private ResolvedMember createOrGetInlineAccessorForFieldSet(ResolvedMember resolvedMember) { - String accessor = NameMangler.inlineAccessMethodForFieldSet(resolvedMember.getName(), resolvedMember.getDeclaringType(), - aspectType); - String key = accessor; - ResolvedMember inlineAccessor = inlineAccessors.get(key); - - if (inlineAccessor == null) { - // add static method to aspect - inlineAccessor = AjcMemberMaker.inlineAccessMethodForFieldSet(aspectType, resolvedMember); - - // add new accessor method to aspect bytecode - InstructionFactory factory = aspectGen.getFactory(); - LazyMethodGen method = makeMethodGen(aspectGen, inlineAccessor); - // flag it synthetic, AjSynthetic - method.makeSynthetic(); - List methodAttributes = new ArrayList(); - methodAttributes.add(new AjAttribute.AjSynthetic()); - methodAttributes.add(new AjAttribute.EffectiveSignatureAttribute(resolvedMember, Shadow.FieldSet, false)); - method.addAttribute(Utility.bcelAttribute(methodAttributes.get(0), aspectGen.getConstantPool())); - // flag the effective signature, so that we can deobfuscate the signature to apply method call pointcut - method.addAttribute(Utility.bcelAttribute(methodAttributes.get(1), aspectGen.getConstantPool())); - - inlineAccessorMethodGens.add(method); - - InstructionList il = method.getBody(); - if (Modifier.isStatic(resolvedMember.getModifiers())) { - // field accessed is static so sole parameter is field value to be set - il.append(InstructionFactory.createLoad(BcelWorld.makeBcelType(resolvedMember.getReturnType()), 0)); - } else { - il.append(InstructionConstants.ALOAD_0); - il.append(InstructionFactory.createLoad(BcelWorld.makeBcelType(resolvedMember.getReturnType()), 1)); - } - il.append(Utility.createSet(factory, resolvedMember)); - il.append(InstructionConstants.RETURN); - inlineAccessors.put(key, new BcelMethod(aspectGen.getBcelObjectType(), method.getMethod(), methodAttributes)); - } - return inlineAccessor; - } -} diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelAdvice.java b/weaver/src/org/aspectj/weaver/bcel/BcelAdvice.java deleted file mode 100644 index d22b17d12..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/BcelAdvice.java +++ /dev/null @@ -1,801 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * Alexandre Vasseur support for @AJ aspects - * ******************************************************************/ - -package org.aspectj.weaver.bcel; - -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Map; - -import org.aspectj.apache.bcel.Constants; -import org.aspectj.apache.bcel.classfile.LocalVariable; -import org.aspectj.apache.bcel.classfile.LocalVariableTable; -import org.aspectj.apache.bcel.generic.InstructionConstants; -import org.aspectj.apache.bcel.generic.InstructionFactory; -import org.aspectj.apache.bcel.generic.InstructionHandle; -import org.aspectj.apache.bcel.generic.InstructionList; -import org.aspectj.apache.bcel.generic.LineNumberTag; -import org.aspectj.apache.bcel.generic.LocalVariableTag; -import org.aspectj.bridge.ISourceLocation; -import org.aspectj.bridge.Message; -import org.aspectj.weaver.Advice; -import org.aspectj.weaver.AdviceKind; -import org.aspectj.weaver.AjAttribute; -import org.aspectj.weaver.BCException; -import org.aspectj.weaver.IEclipseSourceContext; -import org.aspectj.weaver.ISourceContext; -import org.aspectj.weaver.Lint; -import org.aspectj.weaver.Member; -import org.aspectj.weaver.ReferenceType; -import org.aspectj.weaver.ReferenceTypeDelegate; -import org.aspectj.weaver.ResolvedMember; -import org.aspectj.weaver.ResolvedMemberImpl; -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.Shadow; -import org.aspectj.weaver.ShadowMunger; -import org.aspectj.weaver.UnresolvedType; -import org.aspectj.weaver.WeaverMessages; -import org.aspectj.weaver.World; -import org.aspectj.weaver.ast.Literal; -import org.aspectj.weaver.ast.Test; -import org.aspectj.weaver.patterns.ExactTypePattern; -import org.aspectj.weaver.patterns.ExposedState; -import org.aspectj.weaver.patterns.PerClause; -import org.aspectj.weaver.patterns.Pointcut; - -/** - * Advice implemented for BCEL - * - * @author Erik Hilsdale - * @author Jim Hugunin - * @author Andy Clement - */ -class BcelAdvice extends Advice { - - /** - * If a match is not entirely statically determinable, this captures the runtime test that must succeed in order for the advice - * to run. - */ - private Test runtimeTest; - private ExposedState exposedState; - private int containsInvokedynamic = 0;// 0 = dontknow, 1=no, 2=yes - - public BcelAdvice(AjAttribute.AdviceAttribute attribute, Pointcut pointcut, Member adviceSignature, ResolvedType concreteAspect) { - super(attribute, pointcut, simplify(attribute.getKind(), adviceSignature)); - this.concreteAspect = concreteAspect; - } - - /** - * A heavyweight BcelMethod object is only required for around advice that will be inlined. For other kinds of advice it is - * possible to save some space. - */ - private static Member simplify(AdviceKind kind, Member adviceSignature) { - if (adviceSignature != null) { - UnresolvedType adviceDeclaringType = adviceSignature.getDeclaringType(); - // if it isnt around advice or it is but inlining is turned off then shrink it to a ResolvedMemberImpl - if (kind != AdviceKind.Around - || ((adviceDeclaringType instanceof ResolvedType) && ((ResolvedType) adviceDeclaringType).getWorld() - .isXnoInline())) { - if (adviceSignature instanceof BcelMethod) { - BcelMethod bm = (BcelMethod) adviceSignature; - if (bm.getMethod() != null && bm.getMethod().getAnnotations() != null) { - return adviceSignature; - } - ResolvedMemberImpl simplermember = new ResolvedMemberImpl(bm.getKind(), bm.getDeclaringType(), - bm.getModifiers(), bm.getReturnType(), bm.getName(), bm.getParameterTypes());// ,bm.getExceptions(),bm.getBackingGenericMember() - // ); - simplermember.setParameterNames(bm.getParameterNames()); - return simplermember; - } - } - } - return adviceSignature; - } - - @Override - public ShadowMunger concretize(ResolvedType fromType, World world, PerClause clause) { - if (!world.areAllLintIgnored()) { - suppressLintWarnings(world); - } - ShadowMunger ret = super.concretize(fromType, world, clause); - if (!world.areAllLintIgnored()) { - clearLintSuppressions(world, this.suppressedLintKinds); - } - IfFinder ifinder = new IfFinder(); - ret.getPointcut().accept(ifinder, null); - boolean hasGuardTest = ifinder.hasIf && getKind() != AdviceKind.Around; - boolean isAround = getKind() == AdviceKind.Around; - if ((getExtraParameterFlags() & ThisJoinPoint) != 0) { - if (!isAround && !hasGuardTest && world.getLint().noGuardForLazyTjp.isEnabled()) { - // can't build tjp lazily, no suitable test... - // ... only want to record it once against the advice(bug 133117) - world.getLint().noGuardForLazyTjp.signal("", getSourceLocation()); - } - } - return ret; - } - - @Override - public ShadowMunger parameterizeWith(ResolvedType declaringType, Map typeVariableMap) { - Pointcut pc = getPointcut().parameterizeWith(typeVariableMap, declaringType.getWorld()); - - BcelAdvice ret = null; - Member adviceSignature = signature; - // allows for around advice where the return value is a type variable (see pr115250) - if (signature instanceof ResolvedMember && signature.getDeclaringType().isGenericType()) { - adviceSignature = ((ResolvedMember) signature).parameterizedWith(declaringType.getTypeParameters(), declaringType, - declaringType.isParameterizedType()); - } - ret = new BcelAdvice(this.attribute, pc, adviceSignature, this.concreteAspect); - return ret; - } - - @Override - public boolean match(Shadow shadow, World world) { - if (world.areAllLintIgnored()) { - return super.match(shadow, world); - } else { - suppressLintWarnings(world); - boolean ret = super.match(shadow, world); - clearLintSuppressions(world, this.suppressedLintKinds); - return ret; - } - } - - @Override - public void specializeOn(Shadow shadow) { - if (getKind() == AdviceKind.Around) { - ((BcelShadow) shadow).initializeForAroundClosure(); - } - - // XXX this case is just here for supporting lazy test code - if (getKind() == null) { - exposedState = new ExposedState(0); - return; - } - if (getKind().isPerEntry()) { - exposedState = new ExposedState(0); - } else if (getKind().isCflow()) { - exposedState = new ExposedState(nFreeVars); - } else if (getSignature() != null) { - exposedState = new ExposedState(getSignature()); - } else { - exposedState = new ExposedState(0); - return; // XXX this case is just here for supporting lazy test code - } - - World world = shadow.getIWorld(); - if (!world.areAllLintIgnored()) { - suppressLintWarnings(world); - } - exposedState.setConcreteAspect(concreteAspect); - runtimeTest = getPointcut().findResidue(shadow, exposedState); - if (!world.areAllLintIgnored()) { - clearLintSuppressions(world, this.suppressedLintKinds); - } - - // these initializations won't be performed by findResidue, but need to be - // so that the joinpoint is primed for weaving - if (getKind() == AdviceKind.PerThisEntry) { - shadow.getThisVar(); - } else if (getKind() == AdviceKind.PerTargetEntry) { - shadow.getTargetVar(); - } - - // make sure thisJoinPoint parameters are initialized - if ((getExtraParameterFlags() & ThisJoinPointStaticPart) != 0) { - ((BcelShadow) shadow).getThisJoinPointStaticPartVar(); - ((BcelShadow) shadow).getEnclosingClass().warnOnAddedStaticInitializer(shadow, getSourceLocation()); - } - - if ((getExtraParameterFlags() & ThisJoinPoint) != 0) { - boolean hasGuardTest = runtimeTest != Literal.TRUE && getKind() != AdviceKind.Around; - boolean isAround = getKind() == AdviceKind.Around; - ((BcelShadow) shadow).requireThisJoinPoint(hasGuardTest, isAround); - ((BcelShadow) shadow).getEnclosingClass().warnOnAddedStaticInitializer(shadow, getSourceLocation()); - if (!hasGuardTest && world.getLint().multipleAdviceStoppingLazyTjp.isEnabled()) { - // collect up the problematic advice - ((BcelShadow) shadow).addAdvicePreventingLazyTjp(this); - } - } - - if ((getExtraParameterFlags() & ThisEnclosingJoinPointStaticPart) != 0) { - ((BcelShadow) shadow).getThisEnclosingJoinPointStaticPartVar(); - ((BcelShadow) shadow).getEnclosingClass().warnOnAddedStaticInitializer(shadow, getSourceLocation()); - } - } - - private boolean canInline(Shadow s) { - if (attribute.isProceedInInners()) { - return false; - } - // XXX this guard seems to only be needed for bad test cases - if (concreteAspect == null || concreteAspect.isMissing()) { - return false; - } - - if (concreteAspect.getWorld().isXnoInline()) { - return false; - } - // System.err.println("isWoven? " + ((BcelObjectType)concreteAspect).getLazyClassGen().getWeaverState()); - BcelObjectType boType = BcelWorld.getBcelObjectType(concreteAspect); - if (boType == null) { - // Could be a symptom that the aspect failed to build last build... return the default answer of false - return false; - } - // Need isJava8 check - // Does the advice contain invokedynamic... - if (boType.javaClass.getMajor() == Constants.MAJOR_1_8) { - if (containsInvokedynamic == 0) { - containsInvokedynamic = 1; - LazyMethodGen lmg = boType.getLazyClassGen().getLazyMethodGen(this.signature.getName(), this.signature.getSignature(), true); - // Check Java8 supertypes - ResolvedType searchType = concreteAspect; - while (lmg == null) { - searchType = searchType.getSuperclass(); - if (searchType == null) break; - ReferenceTypeDelegate rtd = ((ReferenceType)searchType).getDelegate(); - if (rtd instanceof BcelObjectType) { - BcelObjectType bot = (BcelObjectType)rtd; - if (bot.javaClass.getMajor() < Constants.MAJOR_1_8) { - break; - } - lmg = bot.getLazyClassGen().getLazyMethodGen(this.signature.getName(), this.signature.getSignature(), true); - } - } - if (lmg != null) { - InstructionList ilist = lmg.getBody(); - for (InstructionHandle src = ilist.getStart(); src != null; src = src.getNext()) { - if (src.getInstruction().opcode == Constants.INVOKEDYNAMIC) { - containsInvokedynamic = 2; - break; - } - } - } - } - } - if (containsInvokedynamic == 2) { - return false; - } - return boType.getLazyClassGen().isWoven(); - } - - private boolean aspectIsBroken() { - if (concreteAspect instanceof ReferenceType) { - ReferenceTypeDelegate rtDelegate = ((ReferenceType) concreteAspect).getDelegate(); - if (!(rtDelegate instanceof BcelObjectType)) { - return true; - } - } - return false; - } - - @Override - public boolean implementOn(Shadow s) { - hasMatchedAtLeastOnce = true; - - // pr263323 - if the aspect is broken then the delegate will not be usable for weaving - if (aspectIsBroken()) { - return false; - } - - BcelShadow shadow = (BcelShadow) s; - - // remove any unnecessary exceptions if the compiler option is set to - // error or warning and if this piece of advice throws exceptions - // (bug 129282). This may be expanded to include other compiler warnings - // at the moment it only deals with 'declared exception is not thrown' - if (!shadow.getWorld().isIgnoringUnusedDeclaredThrownException() && !getThrownExceptions().isEmpty()) { - Member member = shadow.getSignature(); - if (member instanceof BcelMethod) { - removeUnnecessaryProblems((BcelMethod) member, ((BcelMethod) member).getDeclarationLineNumber()); - } else { - // we're in a call shadow therefore need the line number of the - // declared method (which may be in a different type). However, - // we want to remove the problems from the CompilationResult - // held within the current type's EclipseSourceContext so need - // the enclosing shadow too - ResolvedMember resolvedMember = shadow.getSignature().resolve(shadow.getWorld()); - if (resolvedMember instanceof BcelMethod && shadow.getEnclosingShadow() instanceof BcelShadow) { - Member enclosingMember = shadow.getEnclosingShadow().getSignature(); - if (enclosingMember instanceof BcelMethod) { - removeUnnecessaryProblems((BcelMethod) enclosingMember, - ((BcelMethod) resolvedMember).getDeclarationLineNumber()); - } - } - } - } - - if (shadow.getIWorld().isJoinpointSynchronizationEnabled() && shadow.getKind() == Shadow.MethodExecution - && (s.getSignature().getModifiers() & Modifier.SYNCHRONIZED) != 0) { - shadow.getIWorld().getLint().advisingSynchronizedMethods.signal(new String[] { shadow.toString() }, - shadow.getSourceLocation(), new ISourceLocation[] { getSourceLocation() }); - } - - // FIXME AV - see #75442, this logic is not enough so for now comment it out until we fix the bug - // // callback for perObject AJC MightHaveAspect postMunge (#75442) - // if (getConcreteAspect() != null - // && getConcreteAspect().getPerClause() != null - // && PerClause.PEROBJECT.equals(getConcreteAspect().getPerClause().getKind())) { - // final PerObject clause; - // if (getConcreteAspect().getPerClause() instanceof PerFromSuper) { - // clause = (PerObject)((PerFromSuper) getConcreteAspect().getPerClause()).lookupConcretePerClause(getConcreteAspect()); - // } else { - // clause = (PerObject) getConcreteAspect().getPerClause(); - // } - // if (clause.isThis()) { - // PerObjectInterfaceTypeMunger.registerAsAdvisedBy(s.getThisVar().getType(), getConcreteAspect()); - // } else { - // PerObjectInterfaceTypeMunger.registerAsAdvisedBy(s.getTargetVar().getType(), getConcreteAspect()); - // } - // } - if (runtimeTest == Literal.FALSE) { // not usually allowed, except in one case (260384) - Member sig = shadow.getSignature(); - if (sig.getArity() == 0 && shadow.getKind() == Shadow.MethodCall && sig.getName().charAt(0) == 'c' - && sig.getReturnType().equals(ResolvedType.OBJECT) && sig.getName().equals("clone")) { - return false; - } - } - - if (getKind() == AdviceKind.Before) { - shadow.weaveBefore(this); - } else if (getKind() == AdviceKind.AfterReturning) { - shadow.weaveAfterReturning(this); - } else if (getKind() == AdviceKind.AfterThrowing) { - UnresolvedType catchType = hasExtraParameter() ? getExtraParameterType() : UnresolvedType.THROWABLE; - shadow.weaveAfterThrowing(this, catchType); - } else if (getKind() == AdviceKind.After) { - shadow.weaveAfter(this); - } else if (getKind() == AdviceKind.Around) { - // Note: under regular LTW the aspect is usually loaded after the first use of any class affected by it. - // This means that as long as the aspect has not been thru the LTW, it's woven state is unknown - // and thus canInline(s) will return false. - // To force inlining (test), ones can do Class aspect = FQNAspect.class in the clinit of the target class - // FIXME AV : for AJC compiled @AJ aspect (or any code style aspect), the woven state can never be known - // if the aspect belongs to a parent classloader. In that case the aspect will never be inlined. - // It might be dangerous to change that especially for @AJ aspect non compiled with AJC since if those - // are not weaved (f.e. use of some limited LTW etc) then they cannot be prepared for inlining. - // One solution would be to flag @AJ aspect with an annotation as "prepared" and query that one. - LazyClassGen enclosingClass = shadow.getEnclosingClass(); - if (enclosingClass != null && enclosingClass.isInterface() && shadow.getEnclosingMethod().getName().charAt(0) == '<') { - // Do not add methods with bodies to an interface (252198, 163005) - shadow.getWorld().getLint().cannotAdviseJoinpointInInterfaceWithAroundAdvice.signal(shadow.toString(), - shadow.getSourceLocation()); - return false; - } - if (!canInline(s)) { - shadow.weaveAroundClosure(this, hasDynamicTests()); - } else { - shadow.weaveAroundInline(this, hasDynamicTests()); - } - } else if (getKind() == AdviceKind.InterInitializer) { - shadow.weaveAfterReturning(this); - } else if (getKind().isCflow()) { - shadow.weaveCflowEntry(this, getSignature()); - } else if (getKind() == AdviceKind.PerThisEntry) { - shadow.weavePerObjectEntry(this, (BcelVar) shadow.getThisVar()); - } else if (getKind() == AdviceKind.PerTargetEntry) { - shadow.weavePerObjectEntry(this, (BcelVar) shadow.getTargetVar()); - } else if (getKind() == AdviceKind.Softener) { - shadow.weaveSoftener(this, ((ExactTypePattern) exceptionType).getType()); - } else if (getKind() == AdviceKind.PerTypeWithinEntry) { - // PTWIMPL Entry to ptw is the static initialization of a type that matched the ptw type pattern - shadow.weavePerTypeWithinAspectInitialization(this, shadow.getEnclosingType()); - } else { - throw new BCException("unimplemented kind: " + getKind()); - } - return true; - } - - private void removeUnnecessaryProblems(BcelMethod method, int problemLineNumber) { - ISourceContext sourceContext = method.getSourceContext(); - if (sourceContext instanceof IEclipseSourceContext) { - ((IEclipseSourceContext) sourceContext).removeUnnecessaryProblems(method, problemLineNumber); - } - } - - // ---- implementations - - private Collection collectCheckedExceptions(UnresolvedType[] excs) { - if (excs == null || excs.length == 0) { - return Collections.emptyList(); - } - - Collection ret = new ArrayList(); - World world = concreteAspect.getWorld(); - ResolvedType runtimeException = world.getCoreType(UnresolvedType.RUNTIME_EXCEPTION); - ResolvedType error = world.getCoreType(UnresolvedType.ERROR); - - for (int i = 0, len = excs.length; i < len; i++) { - ResolvedType t = world.resolve(excs[i], true); - if (t.isMissing()) { - world.getLint().cantFindType - .signal(WeaverMessages.format(WeaverMessages.CANT_FIND_TYPE_EXCEPTION_TYPE, excs[i].getName()), - getSourceLocation()); - // IMessage msg = new Message( - // WeaverMessages.format(WeaverMessages.CANT_FIND_TYPE_EXCEPTION_TYPE,excs[i].getName()), - // "",IMessage.ERROR,getSourceLocation(),null,null); - // world.getMessageHandler().handleMessage(msg); - } - if (!(runtimeException.isAssignableFrom(t) || error.isAssignableFrom(t))) { - ret.add(t); - } - } - - return ret; - } - - private Collection thrownExceptions = null; - - @Override - public Collection getThrownExceptions() { - if (thrownExceptions == null) { - // ??? can we really lump in Around here, how does this interact with Throwable - if (concreteAspect != null && concreteAspect.getWorld() != null && // null tests for test harness - (getKind().isAfter() || getKind() == AdviceKind.Before || getKind() == AdviceKind.Around)) { - World world = concreteAspect.getWorld(); - ResolvedMember m = world.resolve(signature); - if (m == null) { - thrownExceptions = Collections.emptyList(); - } else { - thrownExceptions = collectCheckedExceptions(m.getExceptions()); - } - } else { - thrownExceptions = Collections.emptyList(); - } - } - return thrownExceptions; - } - - /** - * The munger must not check for the advice exceptions to be declared by the shadow in the case of @AJ aspects so that around - * can throws Throwable - * - * @return - */ - @Override - public boolean mustCheckExceptions() { - if (getConcreteAspect() == null) { - return true; - } - return !getConcreteAspect().isAnnotationStyleAspect(); - } - - // only call me after prepare has been called - @Override - public boolean hasDynamicTests() { - // if (hasExtraParameter() && getKind() == AdviceKind.AfterReturning) { - // UnresolvedType extraParameterType = getExtraParameterType(); - // if (! extraParameterType.equals(UnresolvedType.OBJECT) - // && ! extraParameterType.isPrimitive()) - // return true; - // } - - return runtimeTest != null && !(runtimeTest == Literal.TRUE);// || pointcutTest == Literal.NO_TEST); - } - - /** - * get the instruction list for the really simple version of this advice. Is broken apart for other advice, but if you want it - * in one block, this is the method to call. - * - * @param s The shadow around which these instructions will eventually live. - * @param extraArgVar The var that will hold the return value or thrown exception for afterX advice - * @param ifNoAdvice The instructionHandle to jump to if the dynamic tests for this munger fails. - */ - InstructionList getAdviceInstructions(BcelShadow s, BcelVar extraArgVar, InstructionHandle ifNoAdvice) { - BcelShadow shadow = s; - InstructionFactory fact = shadow.getFactory(); - BcelWorld world = shadow.getWorld(); - - InstructionList il = new InstructionList(); - - // we test to see if we have the right kind of thing... - // after throwing does this just by the exception mechanism. - if (hasExtraParameter() && getKind() == AdviceKind.AfterReturning) { - UnresolvedType extraParameterType = getExtraParameterType(); - if (!extraParameterType.equals(UnresolvedType.OBJECT) && !extraParameterType.isPrimitiveType()) { - il.append(BcelRenderer.renderTest(fact, world, - Test.makeInstanceof(extraArgVar, getExtraParameterType().resolve(world)), null, ifNoAdvice, null)); - } - } - il.append(getAdviceArgSetup(shadow, extraArgVar, null)); - il.append(getNonTestAdviceInstructions(shadow)); - - InstructionHandle ifYesAdvice = il.getStart(); - il.insert(getTestInstructions(shadow, ifYesAdvice, ifNoAdvice, ifYesAdvice)); - - // If inserting instructions at the start of a method, we need a nice line number for this entry - // in the stack trace - if (shadow.getKind() == Shadow.MethodExecution && getKind() == AdviceKind.Before) { - int lineNumber = 0; - // Uncomment this code if you think we should use the method decl line number when it exists... - // // If the advised join point is in a class built by AspectJ, we can use the declaration line number - // boolean b = shadow.getEnclosingMethod().getMemberView().hasDeclarationLineNumberInfo(); - // if (b) { - // lineNumber = shadow.getEnclosingMethod().getMemberView().getDeclarationLineNumber(); - // } else { // If it wasn't, the best we can do is the line number of the first instruction in the method - lineNumber = shadow.getEnclosingMethod().getMemberView().getLineNumberOfFirstInstruction(); - // } - InstructionHandle start = il.getStart(); - if (lineNumber > 0) { - start.addTargeter(new LineNumberTag(lineNumber)); - } - // Fix up the local variables: find any that have a startPC of 0 and ensure they target the new start of the method - LocalVariableTable lvt = shadow.getEnclosingMethod().getMemberView().getMethod().getLocalVariableTable(); - if (lvt != null) { - LocalVariable[] lvTable = lvt.getLocalVariableTable(); - for (int i = 0; i < lvTable.length; i++) { - LocalVariable lv = lvTable[i]; - if (lv.getStartPC() == 0) { - start.addTargeter(new LocalVariableTag(lv.getSignature(), lv.getName(), lv.getIndex(), 0)); - } - } - } - } - - return il; - } - - public InstructionList getAdviceArgSetup(BcelShadow shadow, BcelVar extraVar, InstructionList closureInstantiation) { - InstructionFactory fact = shadow.getFactory(); - BcelWorld world = shadow.getWorld(); - InstructionList il = new InstructionList(); - - // if (targetAspectField != null) { - // il.append(fact.createFieldAccess( - // targetAspectField.getDeclaringType().getName(), - // targetAspectField.getName(), - // BcelWorld.makeBcelType(targetAspectField.getType()), - // Constants.GETSTATIC)); - // } - // - // System.err.println("BcelAdvice: " + exposedState); - - if (exposedState.getAspectInstance() != null) { - il.append(BcelRenderer.renderExpr(fact, world, exposedState.getAspectInstance())); - } - // pr121385 - boolean x = this.getDeclaringAspect().resolve(world).isAnnotationStyleAspect(); - final boolean isAnnotationStyleAspect = getConcreteAspect() != null && getConcreteAspect().isAnnotationStyleAspect() && x; - boolean previousIsClosure = false; - for (int i = 0, len = exposedState.size(); i < len; i++) { - if (exposedState.isErroneousVar(i)) { - continue; // Erroneous vars have already had error msgs reported! - } - BcelVar v = (BcelVar) exposedState.get(i); - - if (v == null) { - // if not @AJ aspect, go on with the regular binding handling - if (!isAnnotationStyleAspect) { - - } else { - // ATAJ: for @AJ aspects, handle implicit binding of xxJoinPoint - // if (getKind() == AdviceKind.Around) { - // previousIsClosure = true; - // il.append(closureInstantiation); - if ("Lorg/aspectj/lang/ProceedingJoinPoint;".equals(getSignature().getParameterTypes()[i].getSignature())) { - // make sure we are in an around, since we deal with the closure, not the arg here - if (getKind() != AdviceKind.Around) { - previousIsClosure = false; - getConcreteAspect() - .getWorld() - .getMessageHandler() - .handleMessage( - new Message("use of ProceedingJoinPoint is allowed only on around advice (" + "arg " - + i + " in " + toString() + ")", this.getSourceLocation(), true)); - // try to avoid verify error and pass in null - il.append(InstructionConstants.ACONST_NULL); - } else { - if (previousIsClosure) { - il.append(InstructionConstants.DUP); - } else { - previousIsClosure = true; - il.append(closureInstantiation.copy()); - } - } - } else if ("Lorg/aspectj/lang/JoinPoint$StaticPart;".equals(getSignature().getParameterTypes()[i] - .getSignature())) { - previousIsClosure = false; - if ((getExtraParameterFlags() & ThisJoinPointStaticPart) != 0) { - shadow.getThisJoinPointStaticPartBcelVar().appendLoad(il, fact); - } - } else if ("Lorg/aspectj/lang/JoinPoint;".equals(getSignature().getParameterTypes()[i].getSignature())) { - previousIsClosure = false; - if ((getExtraParameterFlags() & ThisJoinPoint) != 0) { - il.append(shadow.loadThisJoinPoint()); - } - } else if ("Lorg/aspectj/lang/JoinPoint$EnclosingStaticPart;".equals(getSignature().getParameterTypes()[i] - .getSignature())) { - previousIsClosure = false; - if ((getExtraParameterFlags() & ThisEnclosingJoinPointStaticPart) != 0) { - shadow.getThisEnclosingJoinPointStaticPartBcelVar().appendLoad(il, fact); - } - } else if (hasExtraParameter()) { - previousIsClosure = false; - extraVar.appendLoadAndConvert(il, fact, getExtraParameterType().resolve(world)); - } else { - previousIsClosure = false; - getConcreteAspect() - .getWorld() - .getMessageHandler() - .handleMessage( - new Message("use of ProceedingJoinPoint is allowed only on around advice (" + "arg " + i - + " in " + toString() + ")", this.getSourceLocation(), true)); - // try to avoid verify error and pass in null - il.append(InstructionConstants.ACONST_NULL); - } - } - } else { - UnresolvedType desiredTy = getBindingParameterTypes()[i]; - v.appendLoadAndConvert(il, fact, desiredTy.resolve(world)); - } - } - - // ATAJ: for code style aspect, handles the extraFlag as usual ie not - // in the middle of the formal bindings but at the end, in a rock solid ordering - if (!isAnnotationStyleAspect) { - if (getKind() == AdviceKind.Around) { - il.append(closureInstantiation); - } else if (hasExtraParameter()) { - extraVar.appendLoadAndConvert(il, fact, getExtraParameterType().resolve(world)); - } - - // handle thisJoinPoint parameters - // these need to be in that same order as parameters in - // org.aspectj.ajdt.internal.compiler.ast.AdviceDeclaration - if ((getExtraParameterFlags() & ThisJoinPointStaticPart) != 0) { - shadow.getThisJoinPointStaticPartBcelVar().appendLoad(il, fact); - } - - if ((getExtraParameterFlags() & ThisJoinPoint) != 0) { - il.append(shadow.loadThisJoinPoint()); - } - - if ((getExtraParameterFlags() & ThisEnclosingJoinPointStaticPart) != 0) { - shadow.getThisEnclosingJoinPointStaticPartBcelVar().appendLoad(il, fact); - } - } - - return il; - } - - public InstructionList getNonTestAdviceInstructions(BcelShadow shadow) { - return new InstructionList(Utility.createInvoke(shadow.getFactory(), shadow.getWorld(), getOriginalSignature())); - } - - @Override - public Member getOriginalSignature() { - Member sig = getSignature(); - if (sig instanceof ResolvedMember) { - ResolvedMember rsig = (ResolvedMember) sig; - if (rsig.hasBackingGenericMember()) { - return rsig.getBackingGenericMember(); - } - } - return sig; - } - - public InstructionList getTestInstructions(BcelShadow shadow, InstructionHandle sk, InstructionHandle fk, InstructionHandle next) { - // System.err.println("test: " + pointcutTest); - return BcelRenderer.renderTest(shadow.getFactory(), shadow.getWorld(), runtimeTest, sk, fk, next); - } - - public int compareTo(Object other) { - if (!(other instanceof BcelAdvice)) { - return 0; - } - BcelAdvice o = (BcelAdvice) other; - - // System.err.println("compareTo: " + this + ", " + o); - if (kind.getPrecedence() != o.kind.getPrecedence()) { - if (kind.getPrecedence() > o.kind.getPrecedence()) { - return +1; - } else { - return -1; - } - } - - if (kind.isCflow()) { - // System.err.println("sort: " + this + " innerCflowEntries " + innerCflowEntries); - // System.err.println(" " + o + " innerCflowEntries " + o.innerCflowEntries); - boolean isBelow = (kind == AdviceKind.CflowBelowEntry); - - if (this.innerCflowEntries.contains(o)) { - return isBelow ? +1 : -1; - } else if (o.innerCflowEntries.contains(this)) { - return isBelow ? -1 : +1; - } else { - return 0; - } - } - - if (kind.isPerEntry() || kind == AdviceKind.Softener) { - return 0; - } - - // System.out.println("compare: " + this + " with " + other); - World world = concreteAspect.getWorld(); - - int ret = concreteAspect.getWorld().compareByPrecedence(concreteAspect, o.concreteAspect); - if (ret != 0) { - return ret; - } - - ResolvedType declaringAspect = getDeclaringAspect().resolve(world); - ResolvedType o_declaringAspect = o.getDeclaringAspect().resolve(world); - - if (declaringAspect == o_declaringAspect) { - if (kind.isAfter() || o.kind.isAfter()) { - return this.getStart() < o.getStart() ? -1 : +1; - } else { - return this.getStart() < o.getStart() ? +1 : -1; - } - } else if (declaringAspect.isAssignableFrom(o_declaringAspect)) { - return -1; - } else if (o_declaringAspect.isAssignableFrom(declaringAspect)) { - return +1; - } else { - return 0; - } - } - - public BcelVar[] getExposedStateAsBcelVars(boolean isAround) { - // ATAJ aspect - if (isAround) { - // the closure instantiation has the same mapping as the extracted method from wich it is called - if (getConcreteAspect() != null && getConcreteAspect().isAnnotationStyleAspect()) { - return BcelVar.NONE; - } - } - - // System.out.println("vars: " + Arrays.asList(exposedState.vars)); - if (exposedState == null) { - return BcelVar.NONE; - } - int len = exposedState.vars.length; - BcelVar[] ret = new BcelVar[len]; - for (int i = 0; i < len; i++) { - ret[i] = (BcelVar) exposedState.vars[i]; - } - return ret; // (BcelVar[]) exposedState.vars; - } - - protected void suppressLintWarnings(World inWorld) { - if (suppressedLintKinds == null) { - if (signature instanceof BcelMethod) { - this.suppressedLintKinds = Utility.getSuppressedWarnings(signature.getAnnotations(), inWorld.getLint()); - } else { - this.suppressedLintKinds = Collections.emptyList(); - return; - } - } - inWorld.getLint().suppressKinds(suppressedLintKinds); - } - - protected void clearLintSuppressions(World inWorld, Collection toClear) { - inWorld.getLint().clearSuppressions(toClear); - } - - /** - * For testing only - */ - public BcelAdvice(AdviceKind kind, Pointcut pointcut, Member signature, int extraArgumentFlags, int start, int end, - ISourceContext sourceContext, ResolvedType concreteAspect) { - this(new AjAttribute.AdviceAttribute(kind, pointcut, extraArgumentFlags, start, end, sourceContext), pointcut, signature, - concreteAspect); - thrownExceptions = Collections.emptyList(); // !!! interaction with unit tests - } - -} \ No newline at end of file diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelAnnotation.java b/weaver/src/org/aspectj/weaver/bcel/BcelAnnotation.java deleted file mode 100644 index 275eae512..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/BcelAnnotation.java +++ /dev/null @@ -1,151 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2008 Contributors - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * ******************************************************************/ -package org.aspectj.weaver.bcel; - -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; -import org.aspectj.apache.bcel.classfile.annotation.ArrayElementValue; -import org.aspectj.apache.bcel.classfile.annotation.ElementValue; -import org.aspectj.apache.bcel.classfile.annotation.EnumElementValue; -import org.aspectj.apache.bcel.classfile.annotation.NameValuePair; -import org.aspectj.weaver.AbstractAnnotationAJ; -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.UnresolvedType; -import org.aspectj.weaver.World; - -/** - * Wraps a Bcel Annotation object and uses it to answer AnnotationAJ method calls. This is cheaper than translating all Bcel - * annotations into AnnotationAJ objects. - * - * @author AndyClement - */ -public class BcelAnnotation extends AbstractAnnotationAJ { - - private final AnnotationGen bcelAnnotation; - - public BcelAnnotation(AnnotationGen theBcelAnnotation, World world) { - super(UnresolvedType.forSignature(theBcelAnnotation.getTypeSignature()).resolve(world)); - this.bcelAnnotation = theBcelAnnotation; - } - - public BcelAnnotation(AnnotationGen theBcelAnnotation, ResolvedType resolvedAnnotationType) { - super(resolvedAnnotationType); - this.bcelAnnotation = theBcelAnnotation; - } - - public String toString() { - StringBuffer sb = new StringBuffer(); - List nvPairs = bcelAnnotation.getValues(); - sb.append("Anno[" + getTypeSignature() + " " + (isRuntimeVisible() ? "rVis" : "rInvis")); - if (nvPairs.size() > 0) { - sb.append(" "); - int i = 0; - for (NameValuePair element : nvPairs) { - if (i > 0) { - sb.append(','); - } - sb.append(element.getNameString()).append("=").append(element.getValue().toString()); - i++; - } - } - sb.append("]"); - return sb.toString(); - } - - /** - * {@inheritDoc} - */ - @Override - public Set getTargets() { - if (!type.equals(UnresolvedType.AT_TARGET)) { - return Collections.emptySet(); - } - List values = bcelAnnotation.getValues(); - NameValuePair envp = values.get(0); - ArrayElementValue aev = (ArrayElementValue) envp.getValue(); - ElementValue[] evs = aev.getElementValuesArray(); - Set targets = new HashSet(); - for (int i = 0; i < evs.length; i++) { - EnumElementValue ev = (EnumElementValue) evs[i]; - targets.add(ev.getEnumValueString()); - } - return targets; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean hasNameValuePair(String name, String value) { - return bcelAnnotation.hasNameValuePair(name, value); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean hasNamedValue(String name) { - return bcelAnnotation.hasNamedValue(name); - } - - /** - * {@inheritDoc} - */ - @Override - public String stringify() { - StringBuffer sb = new StringBuffer(); - sb.append("@").append(type.getClassName()); - List values = bcelAnnotation.getValues(); - if (values != null && values.size() != 0) { - sb.append("("); - for (NameValuePair nvPair : values) { - sb.append(nvPair.getNameString()).append("=").append(nvPair.getValue().stringifyValue()); - } - sb.append(")"); - } - return sb.toString(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isRuntimeVisible() { - return this.bcelAnnotation.isRuntimeVisible(); - } - - /** - * @return return the real bcel annotation being wrapped - */ - public AnnotationGen getBcelAnnotation() { - return bcelAnnotation; - } - - /** - * {@inheritDoc} - */ - public String getStringFormOfValue(String name) { - List annotationValues = this.bcelAnnotation.getValues(); - if (annotationValues == null || annotationValues.size() == 0) { - return null; - } else { - for (NameValuePair nvPair : annotationValues) { - if (nvPair.getNameString().equals(name)) { - return nvPair.getValue().stringifyValue(); - } - } - return null; - } - } -} diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelCflowAccessVar.java b/weaver/src/org/aspectj/weaver/bcel/BcelCflowAccessVar.java deleted file mode 100644 index 2640edd95..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/BcelCflowAccessVar.java +++ /dev/null @@ -1,87 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - -package org.aspectj.weaver.bcel; - -import org.aspectj.apache.bcel.Constants; -import org.aspectj.apache.bcel.generic.Instruction; -import org.aspectj.apache.bcel.generic.InstructionFactory; -import org.aspectj.apache.bcel.generic.InstructionList; -import org.aspectj.apache.bcel.generic.Type; -import org.aspectj.weaver.Member; -import org.aspectj.weaver.NameMangler; -import org.aspectj.weaver.ResolvedType; - -/** - * XXX Erik and I need to discuss this hierarchy. Having FieldRef extend Var is convenient, but hopefully there's a better design. - * - * This is always a static reference. - */ -public class BcelCflowAccessVar extends BcelVar { - - private Member stackField; - private int index; - - /** - * @param type The type to convert to from Object - * @param stackField the member containing the CFLOW_STACK_TYPE - * @param index yeah yeah - */ - public BcelCflowAccessVar(ResolvedType type, Member stackField, int index) { - super(type, 0); - this.stackField = stackField; - this.index = index; - } - - public String toString() { - return "BcelCflowAccessVar(" + getType() + " " + stackField + "." + index + ")"; - } - - public Instruction createLoad(InstructionFactory fact) { - throw new RuntimeException("unimplemented"); - } - - public Instruction createStore(InstructionFactory fact) { - throw new RuntimeException("unimplemented"); - } - - public InstructionList createCopyFrom(InstructionFactory fact, int oldSlot) { - throw new RuntimeException("unimplemented"); - } - - public void appendLoad(InstructionList il, InstructionFactory fact) { - il.append(createLoadInstructions(getType(), fact)); - } - - public InstructionList createLoadInstructions(ResolvedType toType, InstructionFactory fact) { - InstructionList il = new InstructionList(); - - il.append(Utility.createGet(fact, stackField)); - il.append(Utility.createConstant(fact, index)); - il.append(fact.createInvoke(NameMangler.CFLOW_STACK_TYPE, "get", Type.OBJECT, new Type[] { Type.INT }, - Constants.INVOKEVIRTUAL)); - il.append(Utility.createConversion(fact, Type.OBJECT, BcelWorld.makeBcelType(toType))); - - return il; - - } - - public void appendLoadAndConvert(InstructionList il, InstructionFactory fact, ResolvedType toType) { - il.append(createLoadInstructions(toType, fact)); - - } - - public void insertLoad(InstructionList il, InstructionFactory fact) { - il.insert(createLoadInstructions(getType(), fact)); - } - -} diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelCflowCounterFieldAdder.java b/weaver/src/org/aspectj/weaver/bcel/BcelCflowCounterFieldAdder.java deleted file mode 100644 index a70a5d8a9..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/BcelCflowCounterFieldAdder.java +++ /dev/null @@ -1,88 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * IBM Corporation - initial API and implementation - * (Andy Clement) - *******************************************************************************/ - -package org.aspectj.weaver.bcel; - -import org.aspectj.apache.bcel.Constants; -import org.aspectj.apache.bcel.generic.FieldGen; -import org.aspectj.apache.bcel.generic.InstructionFactory; -import org.aspectj.apache.bcel.generic.InstructionList; -import org.aspectj.apache.bcel.generic.ObjectType; -import org.aspectj.apache.bcel.generic.Type; -import org.aspectj.weaver.Member; -import org.aspectj.weaver.NameMangler; -import org.aspectj.weaver.ResolvedMember; -import org.aspectj.weaver.ResolvedType; - -/** - * This type munger will modify a given class (see the munge() method) to include a field representing a CflowCounter object. - */ -public class BcelCflowCounterFieldAdder extends BcelTypeMunger { - private ResolvedMember cflowCounterField; - - public BcelCflowCounterFieldAdder(ResolvedMember cflowCounterField) { - super(null, (ResolvedType) cflowCounterField.getDeclaringType()); - this.cflowCounterField = cflowCounterField; - } - - public boolean munge(BcelClassWeaver weaver) { - LazyClassGen gen = weaver.getLazyClassGen(); - - // Only munge one type! - if (!gen.getType().equals(cflowCounterField.getDeclaringType())) - return false; - - // Create the field declaration. - // Something like: "public static final CflowCounter ajc$cflowCounter$0;" - FieldGen f = new FieldGen(cflowCounterField.getModifiers(), BcelWorld.makeBcelType(cflowCounterField.getReturnType()), - cflowCounterField.getName(), gen.getConstantPool()); - - gen.addField(f, getSourceLocation()); - - // Modify the ajc$preClinit() method to initialize it. - // Something like: "ajc$cflowCounter$0 = new CflowCounter();" - LazyMethodGen clinit = gen.getAjcPreClinit(); // StaticInitializer(); - InstructionList setup = new InstructionList(); - InstructionFactory fact = gen.getFactory(); - - setup.append(fact.createNew(new ObjectType(NameMangler.CFLOW_COUNTER_TYPE))); - setup.append(InstructionFactory.createDup(1)); - setup.append(fact.createInvoke(NameMangler.CFLOW_COUNTER_TYPE, "", Type.VOID, new Type[0], Constants.INVOKESPECIAL)); - - setup.append(Utility.createSet(fact, cflowCounterField)); - clinit.getBody().insert(setup); - - return true; - } - - public ResolvedMember getMatchingSyntheticMember(Member member) { - return null; - } - - public ResolvedMember getSignature() { - return cflowCounterField; - } - - public boolean matches(ResolvedType onType) { - return onType.equals(cflowCounterField.getDeclaringType()); - } - - public boolean existsToSupportShadowMunging() { - return true; - } - - public String toString() { - return "(BcelTypeMunger: CflowField " + cflowCounterField.getDeclaringType().getName() + " " + cflowCounterField.getName() - + ")"; - } - -} diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelCflowStackFieldAdder.java b/weaver/src/org/aspectj/weaver/bcel/BcelCflowStackFieldAdder.java deleted file mode 100644 index 455edd174..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/BcelCflowStackFieldAdder.java +++ /dev/null @@ -1,77 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - -package org.aspectj.weaver.bcel; - -import org.aspectj.apache.bcel.Constants; -import org.aspectj.apache.bcel.generic.FieldGen; -import org.aspectj.apache.bcel.generic.InstructionFactory; -import org.aspectj.apache.bcel.generic.InstructionList; -import org.aspectj.apache.bcel.generic.Type; -import org.aspectj.weaver.Member; -import org.aspectj.weaver.NameMangler; -import org.aspectj.weaver.ResolvedMember; -import org.aspectj.weaver.ResolvedType; - -public class BcelCflowStackFieldAdder extends BcelTypeMunger { - private ResolvedMember cflowStackField; - - public BcelCflowStackFieldAdder(ResolvedMember cflowStackField) { - super(null, (ResolvedType) cflowStackField.getDeclaringType()); - this.cflowStackField = cflowStackField; - } - - @Override - public boolean munge(BcelClassWeaver weaver) { - LazyClassGen gen = weaver.getLazyClassGen(); - if (!gen.getType().equals(cflowStackField.getDeclaringType())) { - return false; - } - FieldGen f = new FieldGen(cflowStackField.getModifiers(), BcelWorld.makeBcelType(cflowStackField.getReturnType()), - cflowStackField.getName(), gen.getConstantPool()); - gen.addField(f, getSourceLocation()); - - LazyMethodGen clinit = gen.getAjcPreClinit(); // StaticInitializer(); - InstructionList setup = new InstructionList(); - InstructionFactory fact = gen.getFactory(); - - setup.append(fact.createNew(NameMangler.CFLOW_STACK_TYPE)); - setup.append(InstructionFactory.createDup(1)); - setup.append(fact.createInvoke(NameMangler.CFLOW_STACK_TYPE, "", Type.VOID, Type.NO_ARGS, Constants.INVOKESPECIAL)); - - setup.append(Utility.createSet(fact, cflowStackField)); - clinit.getBody().insert(setup); - - return true; - } - - @Override - public ResolvedMember getMatchingSyntheticMember(Member member) { - return null; - } - - @Override - public ResolvedMember getSignature() { - return cflowStackField; - } - - @Override - public boolean matches(ResolvedType onType) { - return onType.equals(cflowStackField.getDeclaringType()); - } - - @Override - public boolean existsToSupportShadowMunging() { - return true; - } - -} diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelClassWeaver.java b/weaver/src/org/aspectj/weaver/bcel/BcelClassWeaver.java deleted file mode 100644 index 83919ed50..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/BcelClassWeaver.java +++ /dev/null @@ -1,3377 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - -package org.aspectj.weaver.bcel; - -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; - -import org.aspectj.apache.bcel.Constants; -import org.aspectj.apache.bcel.classfile.BootstrapMethods; -import org.aspectj.apache.bcel.classfile.ConstantPool; -import org.aspectj.apache.bcel.classfile.Method; -import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; -import org.aspectj.apache.bcel.generic.FieldGen; -import org.aspectj.apache.bcel.generic.FieldInstruction; -import org.aspectj.apache.bcel.generic.Instruction; -import org.aspectj.apache.bcel.generic.InstructionBranch; -import org.aspectj.apache.bcel.generic.InstructionCP; -import org.aspectj.apache.bcel.generic.InstructionConstants; -import org.aspectj.apache.bcel.generic.InstructionFactory; -import org.aspectj.apache.bcel.generic.InstructionHandle; -import org.aspectj.apache.bcel.generic.InstructionLV; -import org.aspectj.apache.bcel.generic.InstructionList; -import org.aspectj.apache.bcel.generic.InstructionSelect; -import org.aspectj.apache.bcel.generic.InstructionTargeter; -import org.aspectj.apache.bcel.generic.InvokeInstruction; -import org.aspectj.apache.bcel.generic.LineNumberTag; -import org.aspectj.apache.bcel.generic.LocalVariableTag; -import org.aspectj.apache.bcel.generic.MULTIANEWARRAY; -import org.aspectj.apache.bcel.generic.MethodGen; -import org.aspectj.apache.bcel.generic.ObjectType; -import org.aspectj.apache.bcel.generic.RET; -import org.aspectj.apache.bcel.generic.Tag; -import org.aspectj.apache.bcel.generic.Type; -import org.aspectj.asm.AsmManager; -import org.aspectj.bridge.IMessage; -import org.aspectj.bridge.ISourceLocation; -import org.aspectj.bridge.Message; -import org.aspectj.bridge.MessageUtil; -import org.aspectj.bridge.WeaveMessage; -import org.aspectj.bridge.context.CompilationAndWeavingContext; -import org.aspectj.bridge.context.ContextToken; -import org.aspectj.util.PartialOrder; -import org.aspectj.weaver.AjAttribute; -import org.aspectj.weaver.AjcMemberMaker; -import org.aspectj.weaver.AnnotationAJ; -import org.aspectj.weaver.BCException; -import org.aspectj.weaver.ConcreteTypeMunger; -import org.aspectj.weaver.IClassWeaver; -import org.aspectj.weaver.IntMap; -import org.aspectj.weaver.Member; -import org.aspectj.weaver.MissingResolvedTypeWithKnownSignature; -import org.aspectj.weaver.NameMangler; -import org.aspectj.weaver.NewConstructorTypeMunger; -import org.aspectj.weaver.NewFieldTypeMunger; -import org.aspectj.weaver.NewMethodTypeMunger; -import org.aspectj.weaver.ResolvedMember; -import org.aspectj.weaver.ResolvedMemberImpl; -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.ResolvedTypeMunger; -import org.aspectj.weaver.Shadow; -import org.aspectj.weaver.ShadowMunger; -import org.aspectj.weaver.UnresolvedType; -import org.aspectj.weaver.UnresolvedTypeVariableReferenceType; -import org.aspectj.weaver.WeaverStateInfo; -import org.aspectj.weaver.World; -import org.aspectj.weaver.model.AsmRelationshipProvider; -import org.aspectj.weaver.patterns.DeclareAnnotation; -import org.aspectj.weaver.patterns.ExactTypePattern; -import org.aspectj.weaver.tools.Trace; -import org.aspectj.weaver.tools.TraceFactory; - -class BcelClassWeaver implements IClassWeaver { - - private static Trace trace = TraceFactory.getTraceFactory().getTrace(BcelClassWeaver.class); - - public static boolean weave(BcelWorld world, LazyClassGen clazz, List shadowMungers, - List typeMungers, List lateTypeMungers, boolean inReweavableMode) { - BcelClassWeaver classWeaver = new BcelClassWeaver(world, clazz, shadowMungers, typeMungers, lateTypeMungers); - classWeaver.setReweavableMode(inReweavableMode); - boolean b = classWeaver.weave(); - return b; - } - - // -------------------------------------------- - - private final LazyClassGen clazz; - private final List shadowMungers; - private final List typeMungers; - private final List lateTypeMungers; - - private List[] indexedShadowMungers; - private boolean canMatchBodyShadows = false; - - private final BcelObjectType ty; // alias of clazz.getType() - private final BcelWorld world; // alias of ty.getWorld() - private final ConstantPool cpg; // alias of clazz.getConstantPoolGen() - private final InstructionFactory fact; // alias of clazz.getFactory(); - - private final List addedLazyMethodGens = new ArrayList(); - private final Set addedDispatchTargets = new HashSet(); - - private boolean inReweavableMode = false; - - private List addedSuperInitializersAsList = null; - private final Map addedSuperInitializers = new HashMap(); - private final List addedThisInitializers = new ArrayList(); - private final List addedClassInitializers = new ArrayList(); - - private final Map mapToAnnotationHolder = new HashMap(); - - // private BcelShadow clinitShadow = null; - - /** - * This holds the initialization and pre-initialization shadows for this class that were actually matched by mungers (if no - * match, then we don't even create the shadows really). - */ - private final List initializationShadows = new ArrayList(); - - private BcelClassWeaver(BcelWorld world, LazyClassGen clazz, List shadowMungers, - List typeMungers, List lateTypeMungers) { - super(); - this.world = world; - this.clazz = clazz; - this.shadowMungers = shadowMungers; - this.typeMungers = typeMungers; - this.lateTypeMungers = lateTypeMungers; - this.ty = clazz.getBcelObjectType(); - this.cpg = clazz.getConstantPool(); - this.fact = clazz.getFactory(); - - indexShadowMungers(); - - initializeSuperInitializerMap(ty.getResolvedTypeX()); - if (!checkedXsetForLowLevelContextCapturing) { - Properties p = world.getExtraConfiguration(); - if (p != null) { - String s = p.getProperty(World.xsetCAPTURE_ALL_CONTEXT, "false"); - captureLowLevelContext = s.equalsIgnoreCase("true"); - if (captureLowLevelContext) { - world.getMessageHandler().handleMessage( - MessageUtil.info("[" + World.xsetCAPTURE_ALL_CONTEXT - + "=true] Enabling collection of low level context for debug/crash messages")); - } - } - checkedXsetForLowLevelContextCapturing = true; - } - } - - private boolean canMatch(Shadow.Kind kind) { - return indexedShadowMungers[kind.getKey()] != null; - } - - // private void fastMatchShadowMungers(List shadowMungers, ArrayList - // mungers, Kind kind) { - // FastMatchInfo info = new FastMatchInfo(clazz.getType(), kind); - // for (Iterator i = shadowMungers.iterator(); i.hasNext();) { - // ShadowMunger munger = (ShadowMunger) i.next(); - // FuzzyBoolean fb = munger.getPointcut().fastMatch(info); - // WeaverMetrics.recordFastMatchResult(fb);// Could pass: - // munger.getPointcut().toString() - // if (fb.maybeTrue()) mungers.add(munger); - // } - // } - - private void initializeSuperInitializerMap(ResolvedType child) { - ResolvedType[] superInterfaces = child.getDeclaredInterfaces(); - for (int i = 0, len = superInterfaces.length; i < len; i++) { - if (ty.getResolvedTypeX().isTopmostImplementor(superInterfaces[i])) { - if (addSuperInitializer(superInterfaces[i])) { - initializeSuperInitializerMap(superInterfaces[i]); - } - } - } - } - - /** - * Process the shadow mungers into array 'buckets', each bucket represents a shadow kind and contains a list of shadowmungers - * that could potentially apply at that shadow kind. - */ - private void indexShadowMungers() { - // beware the annoying property that SHADOW_KINDS[i].getKey == (i+1) ! - indexedShadowMungers = new List[Shadow.MAX_SHADOW_KIND + 1]; - for (ShadowMunger shadowMunger : shadowMungers) { - int couldMatchKinds = shadowMunger.getPointcut().couldMatchKinds(); - for (Shadow.Kind kind : Shadow.SHADOW_KINDS) { - if (kind.isSet(couldMatchKinds)) { - byte k = kind.getKey(); - if (indexedShadowMungers[k] == null) { - indexedShadowMungers[k] = new ArrayList(); - if (!kind.isEnclosingKind()) { - canMatchBodyShadows = true; - } - } - indexedShadowMungers[k].add(shadowMunger); - } - } - } - } - - private boolean addSuperInitializer(ResolvedType onType) { - if (onType.isRawType() || onType.isParameterizedType()) { - onType = onType.getGenericType(); - } - IfaceInitList l = addedSuperInitializers.get(onType); - if (l != null) { - return false; - } - l = new IfaceInitList(onType); - addedSuperInitializers.put(onType, l); - return true; - } - - public void addInitializer(ConcreteTypeMunger cm) { - NewFieldTypeMunger m = (NewFieldTypeMunger) cm.getMunger(); - ResolvedType onType = m.getSignature().getDeclaringType().resolve(world); - if (onType.isRawType()) { - onType = onType.getGenericType(); - } - - if (Modifier.isStatic(m.getSignature().getModifiers())) { - addedClassInitializers.add(cm); - } else { - if (onType == ty.getResolvedTypeX()) { - addedThisInitializers.add(cm); - } else { - IfaceInitList l = addedSuperInitializers.get(onType); - l.list.add(cm); - } - } - } - - private static class IfaceInitList implements PartialOrder.PartialComparable { - final ResolvedType onType; - List list = new ArrayList(); - - IfaceInitList(ResolvedType onType) { - this.onType = onType; - } - - public int compareTo(Object other) { - IfaceInitList o = (IfaceInitList) other; - if (onType.isAssignableFrom(o.onType)) { - return +1; - } else if (o.onType.isAssignableFrom(onType)) { - return -1; - } else { - return 0; - } - } - - public int fallbackCompareTo(Object other) { - return 0; - } - } - - // XXX this is being called, but the result doesn't seem to be being used - public boolean addDispatchTarget(ResolvedMember m) { - return addedDispatchTargets.add(m); - } - - public void addLazyMethodGen(LazyMethodGen gen) { - addedLazyMethodGens.add(gen); - } - - public void addOrReplaceLazyMethodGen(LazyMethodGen mg) { - if (alreadyDefined(clazz, mg)) { - return; - } - - for (Iterator i = addedLazyMethodGens.iterator(); i.hasNext();) { - LazyMethodGen existing = i.next(); - if (signaturesMatch(mg, existing)) { - if (existing.definingType == null) { - // this means existing was introduced on the class itself - return; - } else if (mg.definingType.isAssignableFrom(existing.definingType)) { - // existing is mg's subtype and dominates mg - return; - } else if (existing.definingType.isAssignableFrom(mg.definingType)) { - // mg is existing's subtype and dominates existing - i.remove(); - addedLazyMethodGens.add(mg); - return; - } else { - throw new BCException("conflict between: " + mg + " and " + existing); - } - } - } - addedLazyMethodGens.add(mg); - } - - private boolean alreadyDefined(LazyClassGen clazz, LazyMethodGen mg) { - for (Iterator i = clazz.getMethodGens().iterator(); i.hasNext();) { - LazyMethodGen existing = i.next(); - if (signaturesMatch(mg, existing)) { - if (!mg.isAbstract() && existing.isAbstract()) { - i.remove(); - return false; - } - return true; - } - } - return false; - } - - private boolean signaturesMatch(LazyMethodGen mg, LazyMethodGen existing) { - return mg.getName().equals(existing.getName()) && mg.getSignature().equals(existing.getSignature()); - } - - protected static LazyMethodGen makeBridgeMethod(LazyClassGen gen, ResolvedMember member) { - - // remove abstract modifier - int mods = member.getModifiers(); - if (Modifier.isAbstract(mods)) { - mods = mods - Modifier.ABSTRACT; - } - - LazyMethodGen ret = new LazyMethodGen(mods, BcelWorld.makeBcelType(member.getReturnType()), member.getName(), - BcelWorld.makeBcelTypes(member.getParameterTypes()), UnresolvedType.getNames(member.getExceptions()), gen); - - // 43972 : Static crosscutting makes interfaces unusable for javac - // ret.makeSynthetic(); - return ret; - } - - /** - * Create a single bridge method called 'theBridgeMethod' that bridges to 'whatToBridgeTo' - */ - private static void createBridgeMethod(BcelWorld world, LazyMethodGen whatToBridgeToMethodGen, LazyClassGen clazz, ResolvedMember theBridgeMethod) { - InstructionList body; - InstructionFactory fact; - int pos = 0; - - ResolvedMember whatToBridgeTo = whatToBridgeToMethodGen.getMemberView(); - - if (whatToBridgeTo == null) { - whatToBridgeTo = new ResolvedMemberImpl(Member.METHOD, whatToBridgeToMethodGen.getEnclosingClass().getType(), - whatToBridgeToMethodGen.getAccessFlags(), whatToBridgeToMethodGen.getName(), - whatToBridgeToMethodGen.getSignature()); - } - // The bridge method in this type will have the same signature as the one in the supertype - LazyMethodGen bridgeMethod = makeBridgeMethod(clazz, theBridgeMethod); - int newflags = bridgeMethod.getAccessFlags() | Constants.ACC_BRIDGE | Constants.ACC_SYNTHETIC ;// BRIDGE = 0x00000040 - - if ((newflags & 0x00000100) != 0) { - newflags = newflags - 0x100;// NATIVE = 0x00000100 - need to clear it - } - - bridgeMethod.setAccessFlags(newflags); - Type returnType = BcelWorld.makeBcelType(theBridgeMethod.getReturnType()); - Type[] paramTypes = BcelWorld.makeBcelTypes(theBridgeMethod.getParameterTypes()); - Type[] newParamTypes = whatToBridgeToMethodGen.getArgumentTypes(); - body = bridgeMethod.getBody(); - fact = clazz.getFactory(); - - if (!whatToBridgeToMethodGen.isStatic()) { - body.append(InstructionFactory.createThis()); - pos++; - } - for (int i = 0, len = paramTypes.length; i < len; i++) { - Type paramType = paramTypes[i]; - body.append(InstructionFactory.createLoad(paramType, pos)); - if (!newParamTypes[i].equals(paramTypes[i])) { - if (world.forDEBUG_bridgingCode) { - System.err.println("Bridging: Cast " + newParamTypes[i] + " from " + paramTypes[i]); - } - body.append(fact.createCast(paramTypes[i], newParamTypes[i])); - } - pos += paramType.getSize(); - } - - body.append(Utility.createInvoke(fact, world, whatToBridgeTo)); - body.append(InstructionFactory.createReturn(returnType)); - clazz.addMethodGen(bridgeMethod); - } - - /** - * Weave a class and indicate through the return value whether the class was modified. - * - * @return true if the class was modified - */ - public boolean weave() { - if (clazz.isWoven() && !clazz.isReweavable()) { - if (world.getLint().nonReweavableTypeEncountered.isEnabled()) { - world.getLint().nonReweavableTypeEncountered.signal(clazz.getType().getName(), ty.getSourceLocation()); - } - // Integer uniqueID = new Integer(rm.hashCode() * deca.hashCode()); - // if (!reportedProblems.contains(uniqueID)) { - // reportedProblems.add(uniqueID); - // world.getLint().elementAlreadyAnnotated.signal(new String[] { rm.toString(), - // world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.ALREADY_WOVEN, clazz.getType().getName()), - // ty.getSourceLocation(), null); - return false; - } - - Set aspectsAffectingType = null; - if (inReweavableMode || clazz.getType().isAspect()) { - aspectsAffectingType = new HashSet(); - } - - boolean isChanged = false; - - // we want to "touch" all aspects - if (clazz.getType().isAspect()) { - isChanged = true; - } - - WeaverStateInfo typeWeaverState = (world.isOverWeaving() ? getLazyClassGen().getType().getWeaverState() : null); - // start by munging all typeMungers - for (ConcreteTypeMunger o : typeMungers) { - if (!(o instanceof BcelTypeMunger)) { - // ???System.err.println("surprising: " + o); - continue; - } - BcelTypeMunger munger = (BcelTypeMunger) o; - - if (typeWeaverState != null && typeWeaverState.isAspectAlreadyApplied(munger.getAspectType())) { - continue; - } - boolean typeMungerAffectedType = munger.munge(this); - if (typeMungerAffectedType) { - isChanged = true; - if (inReweavableMode || clazz.getType().isAspect()) { - aspectsAffectingType.add(munger.getAspectType().getSignature()); - } - } - } - - // Weave special half type/half shadow mungers... - isChanged = weaveDeclareAtMethodCtor(clazz) || isChanged; - isChanged = weaveDeclareAtField(clazz) || isChanged; - - // XXX do major sort of stuff - // sort according to: Major: type hierarchy - // within each list: dominates - // don't forget to sort addedThisInitialiers according to dominates - addedSuperInitializersAsList = new ArrayList(addedSuperInitializers.values()); - addedSuperInitializersAsList = PartialOrder.sort(addedSuperInitializersAsList); - if (addedSuperInitializersAsList == null) { - throw new BCException("circularity in inter-types"); - } - - // this will create a static initializer if there isn't one - // this is in just as bad taste as NOPs - LazyMethodGen staticInit = clazz.getStaticInitializer(); - staticInit.getBody().insert(genInitInstructions(addedClassInitializers, true)); - - // now go through each method, and match against each method. This - // sets up each method's {@link LazyMethodGen#matchedShadows} field, - // and it also possibly adds to {@link #initializationShadows}. - List methodGens = new ArrayList(clazz.getMethodGens()); - for (LazyMethodGen member : methodGens) { - if (!member.hasBody()) { - continue; - } - if (world.isJoinpointSynchronizationEnabled() && world.areSynchronizationPointcutsInUse() - && member.getMethod().isSynchronized()) { - transformSynchronizedMethod(member); - } - boolean shadowMungerMatched = match(member); - if (shadowMungerMatched) { - // For matching mungers, add their declaring aspects to the list - // that affected this type - if (inReweavableMode || clazz.getType().isAspect()) { - aspectsAffectingType.addAll(findAspectsForMungers(member)); - } - isChanged = true; - } - } - - // now we weave all but the initialization shadows - for (LazyMethodGen methodGen : methodGens) { - if (!methodGen.hasBody()) { - continue; - } - implement(methodGen); - } - - // if we matched any initialization shadows, we inline and weave - if (!initializationShadows.isEmpty()) { - // Repeat next step until nothing left to inline...cant go on - // infinetly as compiler will have detected and reported - // "Recursive constructor invocation" - List recursiveCtors = new ArrayList(); - while (inlineSelfConstructors(methodGens, recursiveCtors)) { - } - positionAndImplement(initializationShadows); - } - - // now proceed with late type mungers - if (lateTypeMungers != null) { - for (Iterator i = lateTypeMungers.iterator(); i.hasNext();) { - BcelTypeMunger munger = (BcelTypeMunger) i.next(); - if (munger.matches(clazz.getType())) { - boolean typeMungerAffectedType = munger.munge(this); - if (typeMungerAffectedType) { - isChanged = true; - if (inReweavableMode || clazz.getType().isAspect()) { - aspectsAffectingType.add(munger.getAspectType().getSignature()); - } - } - } - } - } - - // FIXME AV - see #75442, for now this is not enough to fix the bug, - // comment that out until we really fix it - // // flush to save some memory - // PerObjectInterfaceTypeMunger.unregisterFromAsAdvisedBy(clazz.getType() - // ); - - // finally, if we changed, we add in the introduced methods. - if (isChanged) { - clazz.getOrCreateWeaverStateInfo(inReweavableMode); - weaveInAddedMethods(); - } - - if (inReweavableMode) { - WeaverStateInfo wsi = clazz.getOrCreateWeaverStateInfo(true); - wsi.addAspectsAffectingType(aspectsAffectingType); - if (!world.isOverWeaving()) { - wsi.setUnwovenClassFileData(ty.getJavaClass().getBytes()); - wsi.setReweavable(true); - } else { - wsi.markOverweavingInUse(); - } - } else { - clazz.getOrCreateWeaverStateInfo(false).setReweavable(false); - } - - // tidyup, reduce ongoing memory usage of BcelMethods that hang around - for (LazyMethodGen mg : methodGens) { - BcelMethod method = mg.getMemberView(); - if (method != null) { - method.wipeJoinpointSignatures(); - } - } - - return isChanged; - } - - // **************************** start of bridge method creation code - // ***************** - - // FIXASC tidy this lot up !! - // FIXASC refactor into ResolvedType or even ResolvedMember? - /** - * Check if a particular method is overriding another - refactored into this helper so it can be used from multiple places. - * @return method that is overriding if it - */ - private static ResolvedMember isOverriding(ResolvedType typeToCheck, ResolvedMember methodThatMightBeGettingOverridden, - String mname, String mrettype, int mmods, boolean inSamePackage, UnresolvedType[] methodParamsArray) { - // Check if we can be an override... - if (Modifier.isStatic(methodThatMightBeGettingOverridden.getModifiers())) { - // we can't be overriding a static method - return null; - } - if (Modifier.isPrivate(methodThatMightBeGettingOverridden.getModifiers())) { - // we can't be overriding a private method - return null; - } - if (!methodThatMightBeGettingOverridden.getName().equals(mname)) { - // names do not match (this will also skip and ) - return null; - } - if (methodThatMightBeGettingOverridden.getParameterTypes().length != methodParamsArray.length) { - // not the same number of parameters - return null; - } - if (!isVisibilityOverride(mmods, methodThatMightBeGettingOverridden, inSamePackage)) { - // not override from visibility point of view - return null; - } - - if (typeToCheck.getWorld().forDEBUG_bridgingCode) { - System.err.println(" Bridging:seriously considering this might be getting overridden '" - + methodThatMightBeGettingOverridden + "'"); - } - - World w = typeToCheck.getWorld(); - - // Look at erasures of parameters (List erased is List) - boolean sameParams = true; - for (int p = 0, max = methodThatMightBeGettingOverridden.getParameterTypes().length; p < max; p++) { - - UnresolvedType mtmbgoParameter = methodThatMightBeGettingOverridden.getParameterTypes()[p]; - UnresolvedType ptype = methodParamsArray[p]; - - if (mtmbgoParameter.isTypeVariableReference()) { - if (!mtmbgoParameter.resolve(w).isAssignableFrom(ptype.resolve(w))) { - sameParams = false; - } - } else { - // old condition: - boolean b = !methodThatMightBeGettingOverridden.getParameterTypes()[p].getErasureSignature().equals( - methodParamsArray[p].getErasureSignature()); - - UnresolvedType parameterType = methodThatMightBeGettingOverridden.getParameterTypes()[p]; - - // Collapse to first bound (isn't that the same as erasure! - if (parameterType instanceof UnresolvedTypeVariableReferenceType) { - parameterType = ((UnresolvedTypeVariableReferenceType) parameterType).getTypeVariable().getFirstBound(); - } - - if (b) { // !parameterType.resolve(w).equals(parameterType2.resolve(w))) { - sameParams = false; - } - } - // - // if (!ut.getErasureSignature().equals(ut2.getErasureSignature())) - // sameParams = false; - } - - // If the 'typeToCheck' represents a parameterized type then the method - // will be the parameterized form of the - // generic method in the generic type. So if the method was 'void - // m(List lt, T t)' and the parameterized type here - // is I then the method we are looking at will be 'void - // m(List lt, String t)' which when erased - // is 'void m(List lt,String t)' - so if the parameters *do* match then - // there is a generic method we are - // overriding - - // FIXASC Why bother with the return type? If it is incompatible then the code has other problems! - if (sameParams) { - if (typeToCheck.isParameterizedType()) { - return methodThatMightBeGettingOverridden.getBackingGenericMember(); - } else if (!methodThatMightBeGettingOverridden.getReturnType().getErasureSignature().equals(mrettype)) { - // addressing the wierd situation from bug 147801 - // just check whether these things are in the right relationship - // for covariance... - ResolvedType superReturn = typeToCheck.getWorld().resolve( - UnresolvedType.forSignature(methodThatMightBeGettingOverridden.getReturnType().getErasureSignature())); - ResolvedType subReturn = typeToCheck.getWorld().resolve(UnresolvedType.forSignature(mrettype)); - if (superReturn.isAssignableFrom(subReturn)) { - return methodThatMightBeGettingOverridden; - } - // } else if (typeToCheck.isParameterizedType()) { - // return methodThatMightBeGettingOverridden.getBackingGenericMember(); - } else { - return methodThatMightBeGettingOverridden; - } - } - return null; - } - - /** - * Looks at the visibility modifiers between two methods, and knows whether they are from classes in the same package, and - * decides whether one overrides the other. - * - * @return true if there is an overrides rather than a 'hides' relationship - */ - static boolean isVisibilityOverride(int methodMods, ResolvedMember inheritedMethod, boolean inSamePackage) { - int inheritedModifiers = inheritedMethod.getModifiers(); - if (Modifier.isStatic(inheritedModifiers)) { - return false; - } - if (methodMods == inheritedModifiers) { - return true; - } - - if (Modifier.isPrivate(inheritedModifiers)) { - return false; - } - - boolean isPackageVisible = !Modifier.isPrivate(inheritedModifiers) && !Modifier.isProtected(inheritedModifiers) - && !Modifier.isPublic(inheritedModifiers); - if (isPackageVisible && !inSamePackage) { - return false; - } - - return true; - } - - /** - * This method recurses up a specified type looking for a method that overrides the one passed in. - * - * @return the method being overridden or null if none is found - */ - public static void checkForOverride(ResolvedType typeToCheck, String mname, String mparams, String mrettype, - int mmods, String mpkg, UnresolvedType[] methodParamsArray, List overriddenMethodsCollector) { - - if (typeToCheck == null) { - return; - } - if (typeToCheck instanceof MissingResolvedTypeWithKnownSignature) { - return; // we just can't tell ! - } - - - if (typeToCheck.getWorld().forDEBUG_bridgingCode) { - System.err.println(" Bridging:checking for override of " + mname + " in " + typeToCheck); - } - - String packageName = typeToCheck.getPackageName(); - if (packageName == null) { - packageName = ""; - } - // used when looking at visibility rules - boolean inSamePackage = packageName.equals(mpkg); - - ResolvedMember[] methods = typeToCheck.getDeclaredMethods(); - for (int ii = 0; ii < methods.length; ii++) { - // the method we are going to check - ResolvedMember methodThatMightBeGettingOverridden = methods[ii]; - ResolvedMember isOverriding = isOverriding(typeToCheck, methodThatMightBeGettingOverridden, mname, mrettype, mmods, - inSamePackage, methodParamsArray); - if (isOverriding != null) { - overriddenMethodsCollector.add(isOverriding); - } - } - // was: List l = typeToCheck.getInterTypeMungers(); - List l = (typeToCheck.isRawType() ? typeToCheck.getGenericType().getInterTypeMungers() : typeToCheck - .getInterTypeMungers()); - for (Iterator iterator = l.iterator(); iterator.hasNext();) { - ConcreteTypeMunger o = iterator.next(); - // FIXME asc if its not a BcelTypeMunger then its an - // EclipseTypeMunger ... do I need to worry about that? - if (o instanceof BcelTypeMunger) { - BcelTypeMunger element = (BcelTypeMunger) o; - if (element.getMunger() instanceof NewMethodTypeMunger) { - if (typeToCheck.getWorld().forDEBUG_bridgingCode) { - System.err.println("Possible ITD candidate " + element); - } - ResolvedMember aMethod = element.getSignature(); - ResolvedMember isOverriding = isOverriding(typeToCheck, aMethod, mname, mrettype, mmods, inSamePackage, - methodParamsArray); - if (isOverriding != null) { - overriddenMethodsCollector.add(isOverriding); - } - } - } - } - - if (typeToCheck.equals(UnresolvedType.OBJECT)) { - return; - } - - ResolvedType superclass = typeToCheck.getSuperclass(); - checkForOverride(superclass, mname, mparams, mrettype, mmods, mpkg, methodParamsArray,overriddenMethodsCollector); - - ResolvedType[] interfaces = typeToCheck.getDeclaredInterfaces(); - for (int i = 0; i < interfaces.length; i++) { - ResolvedType anInterface = interfaces[i]; - checkForOverride(anInterface, mname, mparams, mrettype, mmods, mpkg, methodParamsArray,overriddenMethodsCollector); - } - } - - /** - * We need to determine if any methods in this type require bridge methods - this method should only be called if necessary to - * do this calculation, i.e. we are on a 1.5 VM (where covariance/generics exist) and the type hierarchy for the specified class - * has changed (via decp/itd). - * - * See pr108101 - */ - public static boolean calculateAnyRequiredBridgeMethods(BcelWorld world, LazyClassGen clazz) { - world.ensureAdvancedConfigurationProcessed(); - - if (!world.isInJava5Mode()) { - return false; // just double check... the caller should have already - } - if (clazz.isInterface()) { - return false; // dont bother if we are an interface - } - - boolean didSomething = false; // set if we build any bridge methods - // So what methods do we have right now in this class? - List methods = clazz.getMethodGens(); - - // Keep a set of all methods from this type - it'll help us to check if bridge methods - // have already been created, we don't want to do it twice! - Set methodsSet = new HashSet(); - for (int i = 0; i < methods.size(); i++) { - LazyMethodGen aMethod = methods.get(i); - StringBuilder sb = new StringBuilder(aMethod.getName()); - sb.append(aMethod.getSignature()); - methodsSet.add(sb.toString()); // e.g. "foo(Ljava/lang/String;)V" - } - - // Now go through all the methods in this type - for (int i = 0; i < methods.size(); i++) { - // This is the local method that we *might* have to bridge to - LazyMethodGen bridgeToCandidate = methods.get(i); - if (bridgeToCandidate.isBridgeMethod()) { - continue; // Doh! - } - String name = bridgeToCandidate.getName(); - String psig = bridgeToCandidate.getParameterSignature(); - String rsig = bridgeToCandidate.getReturnType().getSignature(); - - // if (bridgeToCandidate.isAbstract()) continue; - if (bridgeToCandidate.isStatic()) { - continue; // ignore static methods - } - if (name.endsWith("init>")) { - continue; // Skip constructors and static initializers - } - - if (world.forDEBUG_bridgingCode) { - System.err.println("Bridging: Determining if we have to bridge to " + clazz.getName() + "." + name + "" + bridgeToCandidate.getSignature()); - } - - // Let's take a look at the superclass - ResolvedType theSuperclass = clazz.getSuperClass(); - if (world.forDEBUG_bridgingCode) { - System.err.println("Bridging: Checking supertype " + theSuperclass); - } - String pkgName = clazz.getPackageName(); - UnresolvedType[] bm = BcelWorld.fromBcel(bridgeToCandidate.getArgumentTypes()); - List overriddenMethodsCollector = new ArrayList(); - checkForOverride(theSuperclass, name, psig, rsig, bridgeToCandidate.getAccessFlags(), pkgName, bm, overriddenMethodsCollector); - if (overriddenMethodsCollector.size() != 0) { - for (ResolvedMember overriddenMethod: overriddenMethodsCollector) { - String key = new StringBuilder(overriddenMethod.getName()).append(overriddenMethod.getSignatureErased()).toString(); // pr237419 - boolean alreadyHaveABridgeMethod = methodsSet.contains(key); - if (!alreadyHaveABridgeMethod) { - if (world.forDEBUG_bridgingCode) { - System.err.println("Bridging:bridging to '" + overriddenMethod + "'"); - } - createBridgeMethod(world, bridgeToCandidate, clazz, overriddenMethod); - methodsSet.add(key); - didSomething = true; - } - } - } - - // Check superinterfaces - String[] interfaces = clazz.getInterfaceNames(); - for (int j = 0; j < interfaces.length; j++) { - if (world.forDEBUG_bridgingCode) { - System.err.println("Bridging:checking superinterface " + interfaces[j]); - } - ResolvedType interfaceType = world.resolve(interfaces[j]); - overriddenMethodsCollector.clear(); - checkForOverride(interfaceType, name, psig, rsig, bridgeToCandidate.getAccessFlags(), - clazz.getPackageName(), bm, overriddenMethodsCollector); - for (ResolvedMember overriddenMethod: overriddenMethodsCollector) { - String key = new StringBuffer().append(overriddenMethod.getName()).append(overriddenMethod.getSignatureErased()).toString(); // pr237419 - boolean alreadyHaveABridgeMethod = methodsSet.contains(key); - if (!alreadyHaveABridgeMethod) { - createBridgeMethod(world, bridgeToCandidate, clazz, overriddenMethod); - methodsSet.add(key); - didSomething = true; - if (world.forDEBUG_bridgingCode) { - System.err.println("Bridging:bridging to " + overriddenMethod); - } - } - } - } - } - - return didSomething; - } - - // **************************** end of bridge method creation code ***************** - - /** - * Weave any declare @method/@ctor statements into the members of the supplied class - */ - private boolean weaveDeclareAtMethodCtor(LazyClassGen clazz) { - List reportedProblems = new ArrayList(); - - List allDecams = world.getDeclareAnnotationOnMethods(); - if (allDecams.isEmpty()) { - return false; - } - - boolean isChanged = false; - - // deal with ITDs - List itdMethodsCtors = getITDSubset(clazz, ResolvedTypeMunger.Method); - itdMethodsCtors.addAll(getITDSubset(clazz, ResolvedTypeMunger.Constructor)); - if (!itdMethodsCtors.isEmpty()) { - // Can't use the subset called 'decaMs' as it won't be right for - // ITDs... - isChanged = weaveAtMethodOnITDSRepeatedly(allDecams, itdMethodsCtors, reportedProblems); - } - - List decaMs = getMatchingSubset(allDecams, clazz.getType()); - if (decaMs.isEmpty()) { - return false; // nothing to do - } - - Set unusedDecams = new HashSet(); - unusedDecams.addAll(decaMs); - - // These methods may have been targeted with declare annotation. Example: ITD on an interface - // where the top most implementor gets a real method. The top most implementor method - // is an 'addedLazyMethodGen' - if (addedLazyMethodGens!=null) { - for (LazyMethodGen method: addedLazyMethodGens) { - // They have no resolvedmember of their own, conjure one up for matching purposes - ResolvedMember resolvedmember = - new ResolvedMemberImpl(ResolvedMember.METHOD,method.getEnclosingClass().getType(),method.getAccessFlags(), - BcelWorld.fromBcel(method.getReturnType()),method.getName(), - BcelWorld.fromBcel(method.getArgumentTypes()),UnresolvedType.forNames(method.getDeclaredExceptions())); - resolvedmember.setAnnotationTypes(method.getAnnotationTypes()); - resolvedmember.setAnnotations(method.getAnnotations()); - - List worthRetrying = new ArrayList(); - boolean modificationOccured = false; - for (DeclareAnnotation decam: decaMs) { - if (decam.matches(resolvedmember, world)) { - if (doesAlreadyHaveAnnotation(resolvedmember, decam, reportedProblems,false)) { - // remove the declare @method since don't want an error when the annotation is already there - unusedDecams.remove(decam); - continue; - } - - AnnotationGen a = ((BcelAnnotation) decam.getAnnotation()).getBcelAnnotation(); - // create copy to get the annotation type into the right constant pool - AnnotationAJ aj = new BcelAnnotation(new AnnotationGen(a, clazz.getConstantPool(), true),world); - method.addAnnotation(aj); - resolvedmember.addAnnotation(decam.getAnnotation()); - - AsmRelationshipProvider.addDeclareAnnotationMethodRelationship(decam.getSourceLocation(), - clazz.getName(), resolvedmember, world.getModelAsAsmManager()); - reportMethodCtorWeavingMessage(clazz, resolvedmember, decam, method.getDeclarationLineNumber()); - isChanged = true; - modificationOccured = true; - unusedDecams.remove(decam); - } else if (!decam.isStarredAnnotationPattern()) { - // an annotation is specified that might be put on by a subsequent decaf - worthRetrying.add(decam); - } - } - - // Multiple secondary passes - while (!worthRetrying.isEmpty() && modificationOccured) { - modificationOccured = false; - // lets have another go - List forRemoval = new ArrayList(); - for (DeclareAnnotation decam : worthRetrying) { - if (decam.matches(resolvedmember, world)) { - if (doesAlreadyHaveAnnotation(resolvedmember, decam, reportedProblems,false)) { - // remove the declare @method since don't - // want an error when - // the annotation is already there - unusedDecams.remove(decam); - continue; // skip this one... - } - AnnotationGen a = ((BcelAnnotation) decam.getAnnotation()).getBcelAnnotation(); - // create copy to get the annotation type into the right constant pool - AnnotationAJ aj = new BcelAnnotation(new AnnotationGen(a, clazz.getConstantPool(), true),world); - method.addAnnotation(aj); - resolvedmember.addAnnotation(decam.getAnnotation()); - AsmRelationshipProvider.addDeclareAnnotationMethodRelationship(decam.getSourceLocation(), - clazz.getName(), resolvedmember, world.getModelAsAsmManager());// getMethod()); - isChanged = true; - modificationOccured = true; - forRemoval.add(decam); - unusedDecams.remove(decam); - } - } - worthRetrying.removeAll(forRemoval); - } - } - } - - - // deal with all the other methods... - List members = clazz.getMethodGens(); - if (!members.isEmpty()) { - for (int memberCounter = 0; memberCounter < members.size(); memberCounter++) { - LazyMethodGen mg = members.get(memberCounter); - if (!mg.getName().startsWith(NameMangler.PREFIX)) { - - // Single first pass - List worthRetrying = new ArrayList(); - boolean modificationOccured = false; - List annotationsToAdd = null; - for (DeclareAnnotation decaM : decaMs) { - - if (decaM.matches(mg.getMemberView(), world)) { - if (doesAlreadyHaveAnnotation(mg.getMemberView(), decaM, reportedProblems,true)) { - // remove the declare @method since don't want - // an error when the annotation is already there - unusedDecams.remove(decaM); - continue; // skip this one... - } - - if (annotationsToAdd == null) { - annotationsToAdd = new ArrayList(); - } - AnnotationGen a = ((BcelAnnotation) decaM.getAnnotation()).getBcelAnnotation(); - AnnotationGen ag = new AnnotationGen(a, clazz.getConstantPool(), true); - annotationsToAdd.add(ag); - mg.addAnnotation(decaM.getAnnotation()); - - AsmRelationshipProvider.addDeclareAnnotationMethodRelationship(decaM.getSourceLocation(), - clazz.getName(), mg.getMemberView(), world.getModelAsAsmManager());// getMethod()); - reportMethodCtorWeavingMessage(clazz, mg.getMemberView(), decaM, mg.getDeclarationLineNumber()); - isChanged = true; - modificationOccured = true; - // remove the declare @method since have matched - // against it - unusedDecams.remove(decaM); - } else { - if (!decaM.isStarredAnnotationPattern()) { - worthRetrying.add(decaM); // an annotation is - // specified that - // might be put on - // by a subsequent - // decaf - } - } - } - - // Multiple secondary passes - while (!worthRetrying.isEmpty() && modificationOccured) { - modificationOccured = false; - // lets have another go - List forRemoval = new ArrayList(); - for (DeclareAnnotation decaM : worthRetrying) { - if (decaM.matches(mg.getMemberView(), world)) { - if (doesAlreadyHaveAnnotation(mg.getMemberView(), decaM, reportedProblems,true)) { - // remove the declare @method since don't - // want an error when - // the annotation is already there - unusedDecams.remove(decaM); - continue; // skip this one... - } - - if (annotationsToAdd == null) { - annotationsToAdd = new ArrayList(); - } - AnnotationGen a = ((BcelAnnotation) decaM.getAnnotation()).getBcelAnnotation(); - // create copy to get the annotation type into the right constant pool - AnnotationGen ag = new AnnotationGen(a, clazz.getConstantPool(), true); - annotationsToAdd.add(ag); - mg.addAnnotation(decaM.getAnnotation()); - AsmRelationshipProvider.addDeclareAnnotationMethodRelationship(decaM.getSourceLocation(), - clazz.getName(), mg.getMemberView(), world.getModelAsAsmManager());// getMethod()); - isChanged = true; - modificationOccured = true; - forRemoval.add(decaM); - // remove the declare @method since have matched - // against it - unusedDecams.remove(decaM); - } - } - worthRetrying.removeAll(forRemoval); - } - if (annotationsToAdd != null) { - Method oldMethod = mg.getMethod(); - MethodGen myGen = new MethodGen(oldMethod, clazz.getClassName(), clazz.getConstantPool(), false); - for (AnnotationGen a : annotationsToAdd) { - myGen.addAnnotation(a); - } - Method newMethod = myGen.getMethod(); - members.set(memberCounter, new LazyMethodGen(newMethod, clazz)); - } - - } - } - checkUnusedDeclareAts(unusedDecams, false); - } - return isChanged; - } - - // TAG: WeavingMessage - private void reportMethodCtorWeavingMessage(LazyClassGen clazz, ResolvedMember member, DeclareAnnotation decaM, - int memberLineNumber) { - if (!getWorld().getMessageHandler().isIgnoring(IMessage.WEAVEINFO)) { - StringBuffer parmString = new StringBuffer("("); - UnresolvedType[] paramTypes = member.getParameterTypes(); - for (int i = 0; i < paramTypes.length; i++) { - UnresolvedType type = paramTypes[i]; - String s = org.aspectj.apache.bcel.classfile.Utility.signatureToString(type.getSignature()); - if (s.lastIndexOf('.') != -1) { - s = s.substring(s.lastIndexOf('.') + 1); - } - parmString.append(s); - if ((i + 1) < paramTypes.length) { - parmString.append(","); - } - } - parmString.append(")"); - String methodName = member.getName(); - StringBuffer sig = new StringBuffer(); - sig.append(org.aspectj.apache.bcel.classfile.Utility.accessToString(member.getModifiers())); - sig.append(" "); - sig.append(member.getReturnType().toString()); - sig.append(" "); - sig.append(member.getDeclaringType().toString()); - sig.append("."); - sig.append(methodName.equals("") ? "new" : methodName); - sig.append(parmString); - - StringBuffer loc = new StringBuffer(); - if (clazz.getFileName() == null) { - loc.append("no debug info available"); - } else { - loc.append(clazz.getFileName()); - if (memberLineNumber != -1) { - loc.append(":" + memberLineNumber); - } - } - getWorld().getMessageHandler().handleMessage( - WeaveMessage.constructWeavingMessage( - WeaveMessage.WEAVEMESSAGE_ANNOTATES, - new String[] { sig.toString(), loc.toString(), decaM.getAnnotationString(), - methodName.startsWith("") ? "constructor" : "method", decaM.getAspect().toString(), - Utility.beautifyLocation(decaM.getSourceLocation()) })); - } - } - - /** - * Looks through a list of declare annotation statements and only returns those that could possibly match on a field/method/ctor - * in type. - */ - private List getMatchingSubset(List declareAnnotations, ResolvedType type) { - List subset = new ArrayList(); - for (DeclareAnnotation da : declareAnnotations) { - if (da.couldEverMatch(type)) { - subset.add(da); - } - } - return subset; - } - - /** - * Get a subset of all the type mungers defined on this aspect - */ - private List getITDSubset(LazyClassGen clazz, ResolvedTypeMunger.Kind wantedKind) { - List subset = new ArrayList(); - for (ConcreteTypeMunger typeMunger : clazz.getBcelObjectType().getTypeMungers()) { - if (typeMunger.getMunger().getKind() == wantedKind) { - subset.add(typeMunger); - } - } - return subset; - } - - public LazyMethodGen locateAnnotationHolderForFieldMunger(LazyClassGen clazz, ConcreteTypeMunger fieldMunger) { - NewFieldTypeMunger newFieldMunger = (NewFieldTypeMunger) fieldMunger.getMunger(); - ResolvedMember lookingFor = AjcMemberMaker.interFieldInitializer(newFieldMunger.getSignature(), clazz.getType()); - for (LazyMethodGen method : clazz.getMethodGens()) { - if (method.getName().equals(lookingFor.getName())) { - return method; - } - } - return null; - } - - // FIXME asc refactor this to neaten it up - public LazyMethodGen locateAnnotationHolderForMethodCtorMunger(LazyClassGen clazz, ConcreteTypeMunger methodCtorMunger) { - ResolvedTypeMunger rtMunger = methodCtorMunger.getMunger(); - ResolvedMember lookingFor = null; - if (rtMunger instanceof NewMethodTypeMunger) { - NewMethodTypeMunger nftm = (NewMethodTypeMunger) rtMunger; - lookingFor = AjcMemberMaker.interMethodDispatcher(nftm.getSignature(), methodCtorMunger.getAspectType()); - } else if (rtMunger instanceof NewConstructorTypeMunger) { - NewConstructorTypeMunger nftm = (NewConstructorTypeMunger) rtMunger; - lookingFor = AjcMemberMaker.postIntroducedConstructor(methodCtorMunger.getAspectType(), nftm.getSignature() - .getDeclaringType(), nftm.getSignature().getParameterTypes()); - } else { - throw new BCException("Not sure what this is: " + methodCtorMunger); - } - String name = lookingFor.getName(); - String paramSignature = lookingFor.getParameterSignature(); - for (LazyMethodGen member : clazz.getMethodGens()) { - if (member.getName().equals(name) && member.getParameterSignature().equals(paramSignature)) { - return member; - } - } - return null; - } - - /** - * Applies some set of declare @field constructs (List) to some bunch of ITDfields (List. It - * will iterate over the fields repeatedly until everything has been applied. - * - */ - private boolean weaveAtFieldRepeatedly(List decaFs, List itdFields, - List reportedErrors) { - boolean isChanged = false; - for (Iterator iter = itdFields.iterator(); iter.hasNext();) { - BcelTypeMunger fieldMunger = (BcelTypeMunger) iter.next(); - ResolvedMember itdIsActually = fieldMunger.getSignature(); - Set worthRetrying = new LinkedHashSet(); - boolean modificationOccured = false; - - for (Iterator iter2 = decaFs.iterator(); iter2.hasNext();) { - DeclareAnnotation decaF = iter2.next(); - if (decaF.matches(itdIsActually, world)) { - if (decaF.isRemover()) { - LazyMethodGen annotationHolder = locateAnnotationHolderForFieldMunger(clazz, fieldMunger); - if (annotationHolder.hasAnnotation(decaF.getAnnotationType())) { - isChanged = true; - // something to remove - annotationHolder.removeAnnotation(decaF.getAnnotationType()); - AsmRelationshipProvider.addDeclareAnnotationRelationship(world.getModelAsAsmManager(), - decaF.getSourceLocation(), itdIsActually.getSourceLocation(), true); - } else { - worthRetrying.add(decaF); - } - } else { - - LazyMethodGen annotationHolder = locateAnnotationHolderForFieldMunger(clazz, fieldMunger); - if (doesAlreadyHaveAnnotation(annotationHolder, itdIsActually, decaF, reportedErrors)) { - continue; // skip this one... - } - annotationHolder.addAnnotation(decaF.getAnnotation()); - AsmRelationshipProvider.addDeclareAnnotationRelationship(world.getModelAsAsmManager(), - decaF.getSourceLocation(), itdIsActually.getSourceLocation(), false); - isChanged = true; - modificationOccured = true; - } - } else { - if (!decaF.isStarredAnnotationPattern()) { - worthRetrying.add(decaF); // an annotation is specified - // that might be put on by a - // subsequent decaf - } - } - } - - while (!worthRetrying.isEmpty() && modificationOccured) { - modificationOccured = false; - List forRemoval = new ArrayList(); - for (Iterator iter2 = worthRetrying.iterator(); iter2.hasNext();) { - DeclareAnnotation decaF = iter2.next(); - if (decaF.matches(itdIsActually, world)) { - if (decaF.isRemover()) { - LazyMethodGen annotationHolder = locateAnnotationHolderForFieldMunger(clazz, fieldMunger); - if (annotationHolder.hasAnnotation(decaF.getAnnotationType())) { - isChanged = true; - // something to remove - annotationHolder.removeAnnotation(decaF.getAnnotationType()); - AsmRelationshipProvider.addDeclareAnnotationRelationship(world.getModelAsAsmManager(), - decaF.getSourceLocation(), itdIsActually.getSourceLocation(), true); - forRemoval.add(decaF); - } - } else { - LazyMethodGen annotationHolder = locateAnnotationHolderForFieldMunger(clazz, fieldMunger); - if (doesAlreadyHaveAnnotation(annotationHolder, itdIsActually, decaF, reportedErrors)) { - continue; // skip this one... - } - annotationHolder.addAnnotation(decaF.getAnnotation()); - AsmRelationshipProvider.addDeclareAnnotationRelationship(world.getModelAsAsmManager(), - decaF.getSourceLocation(), itdIsActually.getSourceLocation(), false); - isChanged = true; - modificationOccured = true; - forRemoval.add(decaF); - } - } - } - worthRetrying.removeAll(forRemoval); - } - } - return isChanged; - } - - /** - * Applies some set of declare @method/@ctor constructs (List) to some bunch of ITDmembers - * (List. It will iterate over the fields repeatedly until everything has been applied. - */ - private boolean weaveAtMethodOnITDSRepeatedly(List decaMCs, - List itdsForMethodAndConstructor, List reportedErrors) { - boolean isChanged = false; - AsmManager asmManager = world.getModelAsAsmManager(); - for (ConcreteTypeMunger methodctorMunger : itdsForMethodAndConstructor) { - // for (Iterator iter = itdsForMethodAndConstructor.iterator(); iter.hasNext();) { - // BcelTypeMunger methodctorMunger = (BcelTypeMunger) iter.next(); - ResolvedMember unMangledInterMethod = methodctorMunger.getSignature(); - List worthRetrying = new ArrayList(); - boolean modificationOccured = false; - - for (Iterator iter2 = decaMCs.iterator(); iter2.hasNext();) { - DeclareAnnotation decaMC = iter2.next(); - if (decaMC.matches(unMangledInterMethod, world)) { - LazyMethodGen annotationHolder = locateAnnotationHolderForMethodCtorMunger(clazz, methodctorMunger); - if (annotationHolder == null - || doesAlreadyHaveAnnotation(annotationHolder, unMangledInterMethod, decaMC, reportedErrors)) { - continue; // skip this one... - } - annotationHolder.addAnnotation(decaMC.getAnnotation()); - isChanged = true; - AsmRelationshipProvider.addDeclareAnnotationRelationship(asmManager, decaMC.getSourceLocation(), - unMangledInterMethod.getSourceLocation(), false); - reportMethodCtorWeavingMessage(clazz, unMangledInterMethod, decaMC, -1); - modificationOccured = true; - } else { - // If an annotation is specified, it might be added by one of the other declare annotation statements - if (!decaMC.isStarredAnnotationPattern()) { - worthRetrying.add(decaMC); - } - } - } - - while (!worthRetrying.isEmpty() && modificationOccured) { - modificationOccured = false; - List forRemoval = new ArrayList(); - for (Iterator iter2 = worthRetrying.iterator(); iter2.hasNext();) { - DeclareAnnotation decaMC = iter2.next(); - if (decaMC.matches(unMangledInterMethod, world)) { - LazyMethodGen annotationHolder = locateAnnotationHolderForFieldMunger(clazz, methodctorMunger); - if (doesAlreadyHaveAnnotation(annotationHolder, unMangledInterMethod, decaMC, reportedErrors)) { - continue; // skip this one... - } - annotationHolder.addAnnotation(decaMC.getAnnotation()); - unMangledInterMethod.addAnnotation(decaMC.getAnnotation()); - AsmRelationshipProvider.addDeclareAnnotationRelationship(asmManager, decaMC.getSourceLocation(), - unMangledInterMethod.getSourceLocation(), false); - isChanged = true; - modificationOccured = true; - forRemoval.add(decaMC); - } - worthRetrying.removeAll(forRemoval); - } - } - } - return isChanged; - } - - private boolean dontAddTwice(DeclareAnnotation decaF, AnnotationAJ[] dontAddMeTwice) { - for (AnnotationAJ ann : dontAddMeTwice) { - if (ann != null && decaF.getAnnotation().getTypeName().equals(ann.getTypeName())) { - return true; - } - } - return false; - } - - /** - * Remove an annotation from the supplied array, if it is in there. - */ - private AnnotationAJ[] removeFromAnnotationsArray(AnnotationAJ[] annotations,AnnotationAJ annotation) { - for (int i=0;i reportedProblems = new ArrayList(); - List allDecafs = world.getDeclareAnnotationOnFields(); - if (allDecafs.isEmpty()) { - return false; - } - boolean typeIsChanged = false; - List relevantItdFields = getITDSubset(clazz, ResolvedTypeMunger.Field); - if (relevantItdFields != null) { - typeIsChanged = weaveAtFieldRepeatedly(allDecafs, relevantItdFields, reportedProblems); - } - - List decafs = getMatchingSubset(allDecafs, clazz.getType()); - if (decafs.isEmpty()) { - return typeIsChanged; - } - - List fields = clazz.getFieldGens(); - if (fields != null) { - Set unusedDecafs = new HashSet(); - unusedDecafs.addAll(decafs); - for (BcelField field : fields) { - if (!field.getName().startsWith(NameMangler.PREFIX)) { - // Single first pass - Set worthRetrying = new LinkedHashSet(); - boolean modificationOccured = false; - AnnotationAJ[] dontAddMeTwice = field.getAnnotations(); - - // go through all the declare @field statements - for (DeclareAnnotation decaf : decafs) { - if (decaf.getAnnotation() == null) { - return false; - } - if (decaf.matches(field, world)) { - if (decaf.isRemover()) { - AnnotationAJ annotation = decaf.getAnnotation(); - if (field.hasAnnotation(annotation.getType())) { - // something to remove - typeIsChanged = true; - field.removeAnnotation(annotation); - AsmRelationshipProvider.addDeclareAnnotationFieldRelationship(world.getModelAsAsmManager(), - decaf.getSourceLocation(), clazz.getName(), field, true); - reportFieldAnnotationWeavingMessage(clazz, field, decaf, true); - dontAddMeTwice = removeFromAnnotationsArray(dontAddMeTwice, annotation); - } else { - worthRetrying.add(decaf); - } - unusedDecafs.remove(decaf); - } else { - if (!dontAddTwice(decaf, dontAddMeTwice)) { - if (doesAlreadyHaveAnnotation(field, decaf, reportedProblems,true )) { - // remove the declare @field since don't want an error when the annotation is already there - unusedDecafs.remove(decaf); - continue; - } - field.addAnnotation(decaf.getAnnotation()); - } - AsmRelationshipProvider.addDeclareAnnotationFieldRelationship(world.getModelAsAsmManager(), - decaf.getSourceLocation(), clazz.getName(), field, false); - reportFieldAnnotationWeavingMessage(clazz, field, decaf, false); - typeIsChanged = true; - modificationOccured = true; - unusedDecafs.remove(decaf); - } - } else if (!decaf.isStarredAnnotationPattern() || decaf.isRemover()) { - worthRetrying.add(decaf); // an annotation is specified that might be put on by a subsequent decaf - } - } - - // Multiple secondary passes - while (!worthRetrying.isEmpty() && modificationOccured) { - modificationOccured = false; - // lets have another go with any remaining ones - List forRemoval = new ArrayList(); - for (Iterator iter = worthRetrying.iterator(); iter.hasNext();) { - DeclareAnnotation decaF = iter.next(); - - if (decaF.matches(field, world)) { - if (decaF.isRemover()) { - AnnotationAJ annotation = decaF.getAnnotation(); - if (field.hasAnnotation(annotation.getType())) { - // something to remove - typeIsChanged = modificationOccured = true; - forRemoval.add(decaF); - field.removeAnnotation(annotation); - AsmRelationshipProvider.addDeclareAnnotationFieldRelationship(world.getModelAsAsmManager(), - decaF.getSourceLocation(), clazz.getName(), field, true); - reportFieldAnnotationWeavingMessage(clazz, field, decaF, true); - } - } else { - // below code is for recursive things - unusedDecafs.remove(decaF); - if (doesAlreadyHaveAnnotation(field, decaF, reportedProblems,true)) { - continue; - } - field.addAnnotation(decaF.getAnnotation()); - AsmRelationshipProvider.addDeclareAnnotationFieldRelationship(world.getModelAsAsmManager(), - decaF.getSourceLocation(), clazz.getName(), field, false); - typeIsChanged = modificationOccured = true; - forRemoval.add(decaF); - } - } - } - worthRetrying.removeAll(forRemoval); - } - } - } - checkUnusedDeclareAts(unusedDecafs, true); - } - return typeIsChanged; - } - - // bug 99191 - put out an error message if the type doesn't exist - /** - * Report an error if the reason a "declare @method/ctor/field" was not used was because the member specified does not exist. - * This method is passed some set of declare statements that didn't match and a flag indicating whether the set contains declare @field - * or declare @method/ctor entries. - */ - private void checkUnusedDeclareAts(Set unusedDecaTs, boolean isDeclareAtField) { - for (DeclareAnnotation declA : unusedDecaTs) { - - // Error if an exact type pattern was specified - boolean shouldCheck = declA.isExactPattern() || declA.getSignaturePattern().getExactDeclaringTypes().size() != 0; - - if (shouldCheck && declA.getKind() != DeclareAnnotation.AT_CONSTRUCTOR) { - if (declA.getSignaturePattern().isMatchOnAnyName()) { - shouldCheck = false; - } else { - List declaringTypePatterns = declA.getSignaturePattern().getExactDeclaringTypes(); - if (declaringTypePatterns.size() == 0) { - shouldCheck = false; - } else { - for (ExactTypePattern exactTypePattern : declaringTypePatterns) { - if (exactTypePattern.isIncludeSubtypes()) { - shouldCheck = false; - break; - } - } - } - } - } - if (shouldCheck) { - // Quickly check if an ITD supplies the 'missing' member - boolean itdMatch = false; - List lst = clazz.getType().getInterTypeMungers(); - for (Iterator iterator = lst.iterator(); iterator.hasNext() && !itdMatch;) { - ConcreteTypeMunger element = iterator.next(); - if (element.getMunger() instanceof NewFieldTypeMunger) { - NewFieldTypeMunger nftm = (NewFieldTypeMunger) element.getMunger(); - itdMatch = declA.matches(nftm.getSignature(), world); - } else if (element.getMunger() instanceof NewMethodTypeMunger) { - NewMethodTypeMunger nmtm = (NewMethodTypeMunger) element.getMunger(); - itdMatch = declA.matches(nmtm.getSignature(), world); - } else if (element.getMunger() instanceof NewConstructorTypeMunger) { - NewConstructorTypeMunger nctm = (NewConstructorTypeMunger) element.getMunger(); - itdMatch = declA.matches(nctm.getSignature(), world); - } - } - if (!itdMatch) { - IMessage message = null; - if (isDeclareAtField) { - message = new Message("The field '" + declA.getSignaturePattern().toString() + "' does not exist", - declA.getSourceLocation(), true); - } else { - message = new Message("The method '" + declA.getSignaturePattern().toString() + "' does not exist", - declA.getSourceLocation(), true); - } - world.getMessageHandler().handleMessage(message); - } - } - } - } - - // TAG: WeavingMessage - private void reportFieldAnnotationWeavingMessage(LazyClassGen clazz, BcelField theField, DeclareAnnotation decaf, - boolean isRemove) { - if (!getWorld().getMessageHandler().isIgnoring(IMessage.WEAVEINFO)) { - world.getMessageHandler().handleMessage( - WeaveMessage.constructWeavingMessage( - isRemove ? WeaveMessage.WEAVEMESSAGE_REMOVES_ANNOTATION : WeaveMessage.WEAVEMESSAGE_ANNOTATES, - new String[] { theField.getFieldAsIs().toString() + "' of type '" + clazz.getName(), - clazz.getFileName(), decaf.getAnnotationString(), "field", decaf.getAspect().toString(), - Utility.beautifyLocation(decaf.getSourceLocation()) })); - } - } - - /** - * Check if a resolved member (field/method/ctor) already has an annotation, if it does then put out a warning and return true - */ - private boolean doesAlreadyHaveAnnotation(ResolvedMember rm, DeclareAnnotation deca, List reportedProblems, boolean reportError) { - if (rm.hasAnnotation(deca.getAnnotationType())) { - if (reportError && world.getLint().elementAlreadyAnnotated.isEnabled()) { - Integer uniqueID = new Integer(rm.hashCode() * deca.hashCode()); - if (!reportedProblems.contains(uniqueID)) { - reportedProblems.add(uniqueID); - world.getLint().elementAlreadyAnnotated.signal(new String[] { rm.toString(), - deca.getAnnotationType().toString() }, rm.getSourceLocation(), - new ISourceLocation[] { deca.getSourceLocation() }); - } - } - return true; - } - return false; - } - - private boolean doesAlreadyHaveAnnotation(LazyMethodGen rm, ResolvedMember itdfieldsig, DeclareAnnotation deca, - List reportedProblems) { - if (rm != null && rm.hasAnnotation(deca.getAnnotationType())) { - if (world.getLint().elementAlreadyAnnotated.isEnabled()) { - Integer uniqueID = new Integer(rm.hashCode() * deca.hashCode()); - if (!reportedProblems.contains(uniqueID)) { - reportedProblems.add(uniqueID); - reportedProblems.add(new Integer(itdfieldsig.hashCode() * deca.hashCode())); - world.getLint().elementAlreadyAnnotated.signal(new String[] { itdfieldsig.toString(), - deca.getAnnotationType().toString() }, rm.getSourceLocation(), - new ISourceLocation[] { deca.getSourceLocation() }); - } - } - return true; - } - return false; - } - - private Set findAspectsForMungers(LazyMethodGen mg) { - Set aspectsAffectingType = new HashSet(); - for (BcelShadow shadow : mg.matchedShadows) { - for (ShadowMunger munger : shadow.getMungers()) { - if (munger instanceof BcelAdvice) { - BcelAdvice bcelAdvice = (BcelAdvice) munger; - if (bcelAdvice.getConcreteAspect() != null) { - aspectsAffectingType.add(bcelAdvice.getConcreteAspect().getSignature()); - } - } else { - // It is a 'Checker' - we don't need to remember aspects - // that only contributed Checkers... - } - } - } - return aspectsAffectingType; - } - - private boolean inlineSelfConstructors(List methodGens, List recursiveCtors) { - boolean inlinedSomething = false; - List newRecursiveCtors = new ArrayList(); - for (LazyMethodGen methodGen : methodGens) { - if (!methodGen.getName().equals("")) { - continue; - } - InstructionHandle ih = findSuperOrThisCall(methodGen); - if (ih != null && isThisCall(ih)) { - LazyMethodGen donor = getCalledMethod(ih); - if (donor.equals(methodGen)) { - newRecursiveCtors.add(donor); - } else { - if (!recursiveCtors.contains(donor)) { - inlineMethod(donor, methodGen, ih); - inlinedSomething = true; - } - } - } - } - recursiveCtors.addAll(newRecursiveCtors); - return inlinedSomething; - } - - private void positionAndImplement(List initializationShadows) { - for (BcelShadow s : initializationShadows) { - positionInitializationShadow(s); - // s.getEnclosingMethod().print(); - s.implement(); - } - } - - private void positionInitializationShadow(BcelShadow s) { - LazyMethodGen mg = s.getEnclosingMethod(); - InstructionHandle call = findSuperOrThisCall(mg); - InstructionList body = mg.getBody(); - ShadowRange r = new ShadowRange(body); - r.associateWithShadow(s); - if (s.getKind() == Shadow.PreInitialization) { - // XXX assert first instruction is an ALOAD_0. - // a pre shadow goes from AFTER the first instruction (which we - // believe to - // be an ALOAD_0) to just before the call to super - r.associateWithTargets(Range.genStart(body, body.getStart().getNext()), Range.genEnd(body, call.getPrev())); - } else { - // assert s.getKind() == Shadow.Initialization - r.associateWithTargets(Range.genStart(body, call.getNext()), Range.genEnd(body)); - } - } - - private boolean isThisCall(InstructionHandle ih) { - InvokeInstruction inst = (InvokeInstruction) ih.getInstruction(); - return inst.getClassName(cpg).equals(clazz.getName()); - } - - /** - * inline a particular call in bytecode. - * - * @param donor the method we want to inline - * @param recipient the method containing the call we want to inline - * @param call the instructionHandle in recipient's body holding the call we want to inline. - */ - public static void inlineMethod(LazyMethodGen donor, LazyMethodGen recipient, InstructionHandle call) { - // assert recipient.contains(call) - - /* - * Implementation notes: - * - * We allocate two slots for every tempvar so we don't screw up longs and doubles which may share space. This could be - * conservatively avoided (no reference to a long/double instruction, don't do it) or packed later. Right now we don't - * bother to pack. - * - * Allocate a new var for each formal param of the inlined. Fill with stack contents. Then copy the inlined instructions in - * with the appropriate remap table. Any framelocs used by locals in inlined are reallocated to top of frame, - */ - final InstructionFactory fact = recipient.getEnclosingClass().getFactory(); - - IntMap frameEnv = new IntMap(); - - // this also sets up the initial environment - InstructionList argumentStores = genArgumentStores(donor, recipient, frameEnv, fact); - - InstructionList inlineInstructions = genInlineInstructions(donor, recipient, frameEnv, fact, false); - - inlineInstructions.insert(argumentStores); - - recipient.getBody().append(call, inlineInstructions); - Utility.deleteInstruction(call, recipient); - } - - // public BcelVar genTempVar(UnresolvedType typeX) { - // return new BcelVar(typeX.resolve(world), - // genTempVarIndex(typeX.getSize())); - // } - // - // private int genTempVarIndex(int size) { - // return enclosingMethod.allocateLocal(size); - // } - - /** - * Input method is a synchronized method, we remove the bit flag for synchronized and then insert a try..finally block - * - * Some jumping through firey hoops required - depending on the input code level (1.5 or not) we may or may not be able to use - * the LDC instruction that takes a class literal (doesnt on <1.5). - * - * FIXME asc Before promoting -Xjoinpoints:synchronization to be a standard option, this needs a bunch of tidying up - there is - * some duplication that can be removed. - */ - public static void transformSynchronizedMethod(LazyMethodGen synchronizedMethod) { - if (trace.isTraceEnabled()) { - trace.enter("transformSynchronizedMethod", synchronizedMethod); - } - // System.err.println("DEBUG: Transforming synchronized method: "+ - // synchronizedMethod.getName()); - final InstructionFactory fact = synchronizedMethod.getEnclosingClass().getFactory(); - InstructionList body = synchronizedMethod.getBody(); - InstructionList prepend = new InstructionList(); - Type enclosingClassType = BcelWorld.makeBcelType(synchronizedMethod.getEnclosingClass().getType()); - - // STATIC METHOD TRANSFORMATION - if (synchronizedMethod.isStatic()) { - - // What to do here depends on the level of the class file! - // LDC can handle class literals in Java5 and above *sigh* - if (synchronizedMethod.getEnclosingClass().isAtLeastJava5()) { - // MONITORENTER logic: - // 0: ldc #2; //class C - // 2: dup - // 3: astore_0 - // 4: monitorenter - int slotForLockObject = synchronizedMethod.allocateLocal(enclosingClassType); - prepend.append(fact.createConstant(enclosingClassType)); - prepend.append(InstructionFactory.createDup(1)); - prepend.append(InstructionFactory.createStore(enclosingClassType, slotForLockObject)); - prepend.append(InstructionFactory.MONITORENTER); - - // MONITOREXIT logic: - - // We basically need to wrap the code from the method in a - // finally block that - // will ensure monitorexit is called. Content on the finally - // block seems to - // be always: - // - // E1: ALOAD_1 - // MONITOREXIT - // ATHROW - // - // so lets build that: - InstructionList finallyBlock = new InstructionList(); - finallyBlock.append(InstructionFactory.createLoad(Type.getType(java.lang.Class.class), slotForLockObject)); - finallyBlock.append(InstructionConstants.MONITOREXIT); - finallyBlock.append(InstructionConstants.ATHROW); - - // finally -> E1 - // | GETSTATIC java.lang.System.out Ljava/io/PrintStream; (line - // 21) - // | LDC "hello" - // | INVOKEVIRTUAL java.io.PrintStream.println - // (Ljava/lang/String;)V - // | ALOAD_1 (line 20) - // | MONITOREXIT - // finally -> E1 - // GOTO L0 - // finally -> E1 - // | E1: ALOAD_1 - // | MONITOREXIT - // finally -> E1 - // ATHROW - // L0: RETURN (line 23) - - // search for 'returns' and make them jump to the - // aload_,monitorexit - InstructionHandle walker = body.getStart(); - List rets = new ArrayList(); - while (walker != null) { - if (walker.getInstruction().isReturnInstruction()) { - rets.add(walker); - } - walker = walker.getNext(); - } - if (!rets.isEmpty()) { - // need to ensure targeters for 'return' now instead target - // the load instruction - // (so we never jump over the monitorexit logic) - - for (Iterator iter = rets.iterator(); iter.hasNext();) { - InstructionHandle element = iter.next(); - InstructionList monitorExitBlock = new InstructionList(); - monitorExitBlock.append(InstructionFactory.createLoad(enclosingClassType, slotForLockObject)); - monitorExitBlock.append(InstructionConstants.MONITOREXIT); - // monitorExitBlock.append(Utility.copyInstruction(element - // .getInstruction())); - // element.setInstruction(InstructionFactory.createLoad( - // classType,slotForThis)); - InstructionHandle monitorExitBlockStart = body.insert(element, monitorExitBlock); - - // now move the targeters from the RET to the start of - // the monitorexit block - for (InstructionTargeter targeter : element.getTargetersCopy()) { - // what kinds are there? - if (targeter instanceof LocalVariableTag) { - // ignore - } else if (targeter instanceof LineNumberTag) { - // ignore - // } else if (targeter instanceof - // InstructionBranch && - // ((InstructionBranch)targeter).isGoto()) { - // // move it... - // targeter.updateTarget(element, - // monitorExitBlockStart); - } else if (targeter instanceof InstructionBranch) { - // move it - targeter.updateTarget(element, monitorExitBlockStart); - } else { - throw new BCException("Unexpected targeter encountered during transform: " + targeter); - } - } - } - } - - // now the magic, putting the finally block around the code - InstructionHandle finallyStart = finallyBlock.getStart(); - - InstructionHandle tryPosition = body.getStart(); - InstructionHandle catchPosition = body.getEnd(); - body.insert(body.getStart(), prepend); // now we can put the - // monitorenter stuff on - synchronizedMethod.getBody().append(finallyBlock); - synchronizedMethod.addExceptionHandler(tryPosition, catchPosition, finallyStart, null/* ==finally */, false); - synchronizedMethod.addExceptionHandler(finallyStart, finallyStart.getNext(), finallyStart, null, false); - } else { - - // TRANSFORMING STATIC METHOD ON PRE JAVA5 - - // Hideous nightmare, class literal references prior to Java5 - - // YIKES! this is just the code for MONITORENTER ! - // 0: getstatic #59; //Field class$1:Ljava/lang/Class; - // 3: dup - // 4: ifnonnull 32 - // 7: pop - // try - // 8: ldc #61; //String java.lang.String - // 10: invokestatic #44; //Method - // java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class; - // 13: dup - // catch - // 14: putstatic #59; //Field class$1:Ljava/lang/Class; - // 17: goto 32 - // 20: new #46; //class java/lang/NoClassDefFoundError - // 23: dup_x1 - // 24: swap - // 25: invokevirtual #52; //Method - // java/lang/Throwable.getMessage:()Ljava/lang/String; - // 28: invokespecial #54; //Method - // java/lang/NoClassDefFoundError."":(Ljava/lang/String;)V - // 31: athrow - // 32: dup <-- partTwo (branch target) - // 33: astore_0 - // 34: monitorenter - // - // plus exceptiontable entry! - // 8 13 20 Class java/lang/ClassNotFoundException - Type classType = BcelWorld.makeBcelType(synchronizedMethod.getEnclosingClass().getType()); - Type clazzType = Type.getType(Class.class); - - InstructionList parttwo = new InstructionList(); - parttwo.append(InstructionFactory.createDup(1)); - int slotForThis = synchronizedMethod.allocateLocal(classType); - parttwo.append(InstructionFactory.createStore(clazzType, slotForThis)); // ? should be the real type ? String or - // something? - parttwo.append(InstructionFactory.MONITORENTER); - - String fieldname = synchronizedMethod.getEnclosingClass().allocateField("class$"); - FieldGen f = new FieldGen(Modifier.STATIC | Modifier.PRIVATE, Type.getType(Class.class), fieldname, - synchronizedMethod.getEnclosingClass().getConstantPool()); - synchronizedMethod.getEnclosingClass().addField(f, null); - - // 10: invokestatic #44; //Method - // java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class; - // 13: dup - // 14: putstatic #59; //Field class$1:Ljava/lang/Class; - // 17: goto 32 - // 20: new #46; //class java/lang/NoClassDefFoundError - // 23: dup_x1 - // 24: swap - // 25: invokevirtual #52; //Method - // java/lang/Throwable.getMessage:()Ljava/lang/String; - // 28: invokespecial #54; //Method - // java/lang/NoClassDefFoundError."":(Ljava/lang/String;)V - // 31: athrow - String name = synchronizedMethod.getEnclosingClass().getName(); - - prepend.append(fact.createGetStatic(name, fieldname, Type.getType(Class.class))); - prepend.append(InstructionFactory.createDup(1)); - prepend.append(InstructionFactory.createBranchInstruction(Constants.IFNONNULL, parttwo.getStart())); - prepend.append(InstructionFactory.POP); - - prepend.append(fact.createConstant(name)); - InstructionHandle tryInstruction = prepend.getEnd(); - prepend.append(fact.createInvoke("java.lang.Class", "forName", clazzType, - new Type[] { Type.getType(String.class) }, Constants.INVOKESTATIC)); - InstructionHandle catchInstruction = prepend.getEnd(); - prepend.append(InstructionFactory.createDup(1)); - - prepend.append(fact.createPutStatic(synchronizedMethod.getEnclosingClass().getType().getName(), fieldname, - Type.getType(Class.class))); - prepend.append(InstructionFactory.createBranchInstruction(Constants.GOTO, parttwo.getStart())); - - // start of catch block - InstructionList catchBlockForLiteralLoadingFail = new InstructionList(); - catchBlockForLiteralLoadingFail.append(fact.createNew((ObjectType) Type.getType(NoClassDefFoundError.class))); - catchBlockForLiteralLoadingFail.append(InstructionFactory.createDup_1(1)); - catchBlockForLiteralLoadingFail.append(InstructionFactory.SWAP); - catchBlockForLiteralLoadingFail.append(fact.createInvoke("java.lang.Throwable", "getMessage", - Type.getType(String.class), new Type[] {}, Constants.INVOKEVIRTUAL)); - catchBlockForLiteralLoadingFail.append(fact.createInvoke("java.lang.NoClassDefFoundError", "", Type.VOID, - new Type[] { Type.getType(String.class) }, Constants.INVOKESPECIAL)); - catchBlockForLiteralLoadingFail.append(InstructionFactory.ATHROW); - InstructionHandle catchBlockStart = catchBlockForLiteralLoadingFail.getStart(); - prepend.append(catchBlockForLiteralLoadingFail); - prepend.append(parttwo); - // MONITORENTER - // pseudocode: load up 'this' (var0), dup it, store it in a new - // local var (for use with monitorexit) and call - // monitorenter: - // ALOAD_0, DUP, ASTORE_, MONITORENTER - // prepend.append(InstructionFactory.createLoad(classType,0)); - // prepend.append(InstructionFactory.createDup(1)); - // int slotForThis = - // synchronizedMethod.allocateLocal(classType); - // prepend.append(InstructionFactory.createStore(classType, - // slotForThis)); - // prepend.append(InstructionFactory.MONITORENTER); - - // MONITOREXIT - // here be dragons - - // We basically need to wrap the code from the method in a - // finally block that - // will ensure monitorexit is called. Content on the finally - // block seems to - // be always: - // - // E1: ALOAD_1 - // MONITOREXIT - // ATHROW - // - // so lets build that: - InstructionList finallyBlock = new InstructionList(); - finallyBlock.append(InstructionFactory.createLoad(Type.getType(java.lang.Class.class), slotForThis)); - finallyBlock.append(InstructionConstants.MONITOREXIT); - finallyBlock.append(InstructionConstants.ATHROW); - - // finally -> E1 - // | GETSTATIC java.lang.System.out Ljava/io/PrintStream; (line - // 21) - // | LDC "hello" - // | INVOKEVIRTUAL java.io.PrintStream.println - // (Ljava/lang/String;)V - // | ALOAD_1 (line 20) - // | MONITOREXIT - // finally -> E1 - // GOTO L0 - // finally -> E1 - // | E1: ALOAD_1 - // | MONITOREXIT - // finally -> E1 - // ATHROW - // L0: RETURN (line 23) - // frameEnv.put(donorFramePos, thisSlot); - - // search for 'returns' and make them to the - // aload_,monitorexit - InstructionHandle walker = body.getStart(); - List rets = new ArrayList(); - while (walker != null) { // !walker.equals(body.getEnd())) { - if (walker.getInstruction().isReturnInstruction()) { - rets.add(walker); - } - walker = walker.getNext(); - } - if (rets.size() > 0) { - // need to ensure targeters for 'return' now instead target - // the load instruction - // (so we never jump over the monitorexit logic) - - for (InstructionHandle ret : rets) { - // System.err.println("Adding monitor exit block at "+ - // element); - InstructionList monitorExitBlock = new InstructionList(); - monitorExitBlock.append(InstructionFactory.createLoad(classType, slotForThis)); - monitorExitBlock.append(InstructionConstants.MONITOREXIT); - // monitorExitBlock.append(Utility.copyInstruction(element - // .getInstruction())); - // element.setInstruction(InstructionFactory.createLoad( - // classType,slotForThis)); - InstructionHandle monitorExitBlockStart = body.insert(ret, monitorExitBlock); - - // now move the targeters from the RET to the start of - // the monitorexit block - for (InstructionTargeter targeter : ret.getTargetersCopy()) { - // what kinds are there? - if (targeter instanceof LocalVariableTag) { - // ignore - } else if (targeter instanceof LineNumberTag) { - // ignore - // } else if (targeter instanceof GOTO || - // targeter instanceof GOTO_W) { - // // move it... - // targeter.updateTarget(element, - // monitorExitBlockStart); - } else if (targeter instanceof InstructionBranch) { - // move it - targeter.updateTarget(ret, monitorExitBlockStart); - } else { - throw new BCException("Unexpected targeter encountered during transform: " + targeter); - } - } - } - } - // body = - // rewriteWithMonitorExitCalls(body,fact,true,slotForThis, - // classType); - // synchronizedMethod.setBody(body); - - // now the magic, putting the finally block around the code - InstructionHandle finallyStart = finallyBlock.getStart(); - - InstructionHandle tryPosition = body.getStart(); - InstructionHandle catchPosition = body.getEnd(); - body.insert(body.getStart(), prepend); // now we can put the - // monitorenter stuff on - - synchronizedMethod.getBody().append(finallyBlock); - synchronizedMethod.addExceptionHandler(tryPosition, catchPosition, finallyStart, null/* ==finally */, false); - synchronizedMethod.addExceptionHandler(tryInstruction, catchInstruction, catchBlockStart, - (ObjectType) Type.getType(ClassNotFoundException.class), true); - synchronizedMethod.addExceptionHandler(finallyStart, finallyStart.getNext(), finallyStart, null, false); - } - } else { - - // TRANSFORMING NON STATIC METHOD - Type classType = BcelWorld.makeBcelType(synchronizedMethod.getEnclosingClass().getType()); - // MONITORENTER - // pseudocode: load up 'this' (var0), dup it, store it in a new - // local var (for use with monitorexit) and call - // monitorenter: - // ALOAD_0, DUP, ASTORE_, MONITORENTER - prepend.append(InstructionFactory.createLoad(classType, 0)); - prepend.append(InstructionFactory.createDup(1)); - int slotForThis = synchronizedMethod.allocateLocal(classType); - prepend.append(InstructionFactory.createStore(classType, slotForThis)); - prepend.append(InstructionFactory.MONITORENTER); - // body.insert(body.getStart(),prepend); - - // MONITOREXIT - - // We basically need to wrap the code from the method in a finally - // block that - // will ensure monitorexit is called. Content on the finally block - // seems to - // be always: - // - // E1: ALOAD_1 - // MONITOREXIT - // ATHROW - // - // so lets build that: - InstructionList finallyBlock = new InstructionList(); - finallyBlock.append(InstructionFactory.createLoad(classType, slotForThis)); - finallyBlock.append(InstructionConstants.MONITOREXIT); - finallyBlock.append(InstructionConstants.ATHROW); - - // finally -> E1 - // | GETSTATIC java.lang.System.out Ljava/io/PrintStream; (line 21) - // | LDC "hello" - // | INVOKEVIRTUAL java.io.PrintStream.println (Ljava/lang/String;)V - // | ALOAD_1 (line 20) - // | MONITOREXIT - // finally -> E1 - // GOTO L0 - // finally -> E1 - // | E1: ALOAD_1 - // | MONITOREXIT - // finally -> E1 - // ATHROW - // L0: RETURN (line 23) - // frameEnv.put(donorFramePos, thisSlot); - - // search for 'returns' and make them to the aload_,monitorexit - InstructionHandle walker = body.getStart(); - List rets = new ArrayList(); - while (walker != null) { // !walker.equals(body.getEnd())) { - if (walker.getInstruction().isReturnInstruction()) { - rets.add(walker); - } - walker = walker.getNext(); - } - if (!rets.isEmpty()) { - // need to ensure targeters for 'return' now instead target the - // load instruction - // (so we never jump over the monitorexit logic) - - for (Iterator iter = rets.iterator(); iter.hasNext();) { - InstructionHandle element = iter.next(); - // System.err.println("Adding monitor exit block at "+element - // ); - InstructionList monitorExitBlock = new InstructionList(); - monitorExitBlock.append(InstructionFactory.createLoad(classType, slotForThis)); - monitorExitBlock.append(InstructionConstants.MONITOREXIT); - // monitorExitBlock.append(Utility.copyInstruction(element. - // getInstruction())); - // element.setInstruction(InstructionFactory.createLoad( - // classType,slotForThis)); - InstructionHandle monitorExitBlockStart = body.insert(element, monitorExitBlock); - - // now move the targeters from the RET to the start of the - // monitorexit block - for (InstructionTargeter targeter : element.getTargetersCopy()) { - // what kinds are there? - if (targeter instanceof LocalVariableTag) { - // ignore - } else if (targeter instanceof LineNumberTag) { - // ignore - // } else if (targeter instanceof GOTO || - // targeter instanceof GOTO_W) { - // // move it... - // targeter.updateTarget(element, - // monitorExitBlockStart); - } else if (targeter instanceof InstructionBranch) { - // move it - targeter.updateTarget(element, monitorExitBlockStart); - } else { - throw new BCException("Unexpected targeter encountered during transform: " + targeter); - } - } - } - } - - // now the magic, putting the finally block around the code - InstructionHandle finallyStart = finallyBlock.getStart(); - - InstructionHandle tryPosition = body.getStart(); - InstructionHandle catchPosition = body.getEnd(); - body.insert(body.getStart(), prepend); // now we can put the - // monitorenter stuff on - synchronizedMethod.getBody().append(finallyBlock); - synchronizedMethod.addExceptionHandler(tryPosition, catchPosition, finallyStart, null/* ==finally */, false); - synchronizedMethod.addExceptionHandler(finallyStart, finallyStart.getNext(), finallyStart, null, false); - // also the exception handling for the finally block jumps to itself - - // max locals will already have been modified in the allocateLocal() - // call - - // synchronized bit is removed on LazyMethodGen.pack() - } - - // gonna have to go through and change all aload_0s to load the var from - // a variable, - // going to add a new variable for the this var - - if (trace.isTraceEnabled()) { - trace.exit("transformSynchronizedMethod"); - } - } - - /** - * generate the instructions to be inlined. - * - * @param donor the method from which we will copy (and adjust frame and jumps) instructions. - * @param recipient the method the instructions will go into. Used to get the frame size so we can allocate new frame locations - * for locals in donor. - * @param frameEnv an environment to map from donor frame to recipient frame, initially populated with argument locations. - * @param fact an instruction factory for recipient - */ - static InstructionList genInlineInstructions(LazyMethodGen donor, LazyMethodGen recipient, IntMap frameEnv, - InstructionFactory fact, boolean keepReturns) { - InstructionList footer = new InstructionList(); - InstructionHandle end = footer.append(InstructionConstants.NOP); - - InstructionList ret = new InstructionList(); - InstructionList sourceList = donor.getBody(); - - Map srcToDest = new HashMap(); - ConstantPool donorCpg = donor.getEnclosingClass().getConstantPool(); - ConstantPool recipientCpg = recipient.getEnclosingClass().getConstantPool(); - - boolean isAcrossClass = donorCpg != recipientCpg; - BootstrapMethods bootstrapMethods = null; - // first pass: copy the instructions directly, populate the srcToDest - // map, - // fix frame instructions - for (InstructionHandle src = sourceList.getStart(); src != null; src = src.getNext()) { - Instruction fresh = Utility.copyInstruction(src.getInstruction()); - InstructionHandle dest; - - // OPTIMIZE optimize this stuff? - if (fresh.isConstantPoolInstruction()) { - // need to reset index to go to new constant pool. This is totally - // a computation leak... we're testing this LOTS of times. Sigh. - if (isAcrossClass) { - InstructionCP cpi = (InstructionCP) fresh; - cpi.setIndex(recipientCpg.addConstant(donorCpg.getConstant(cpi.getIndex()), donorCpg)); - } - // May need to copy bootstrapmethods across too. -// if (fresh instanceof InvokeDynamic) { -// InvokeDynamic id = (InvokeDynamic)fresh; -// ConstantInvokeDynamic cid = (ConstantInvokeDynamic)donorCpg.getConstant(src.getInstruction().getIndex()); -// int bmaIndex = cid.getBootstrapMethodAttrIndex(); -// if (bootstrapMethods == null) { -// Collection attributes = donor.getEnclosingClass().getAttributes(); -// if (attributes != null) { -// for (Attribute attribute: attributes) { -// if (attribute instanceof BootstrapMethods) { -// bootstrapMethods = (BootstrapMethods)attribute; -// } -// } -// } -// BootstrapMethods.BootstrapMethod bootstrapMethod = -// bootstrapMethods.getBootstrapMethods()[bmaIndex]; -// ConstantMethodHandle methodhandle = (ConstantMethodHandle)donorCpg.getConstant(bootstrapMethod.getBootstrapMethodRef()); -// int bootstrapMethodArguments[] = bootstrapMethod.getBootstrapArguments(); -// -// // Finally have all we need to build the new one... -// -// int newMethodHandleIndex = recipientCpg.addConstant(methodhandle, donorCpg); -// int[] newMethodArguments = new int[bootstrapMethodArguments.length]; -// for (int a=0; a newAttributes = recipient.getEnclosingClass().getAttributes(); -// BootstrapMethods newBootstrapMethods = null; -// for (Attribute attr: newAttributes) { -// if (attr instanceof BootstrapMethods) { -// newBootstrapMethods = (BootstrapMethods)newBootstrapMethods; -// } -// } -// if (newBootstrapMethods == null) { -// newBootstrapMethods = -// new BootstrapMethods(recipientCpg.addUtf8("BootstrapMethods"), -// 2+newBootstrapMethod.getLength(), -// new BootstrapMethods.BootstrapMethod[] {newBootstrapMethod}, -// recipientCpg); -// recipient.getEnclosingClass().addAttribute(newBootstrapMethods); -// } -// TODO need to copy over lambda$0 support methods too... -// } -// -// } - } - if (src.getInstruction() == Range.RANGEINSTRUCTION) { - dest = ret.append(Range.RANGEINSTRUCTION); - } else if (fresh.isReturnInstruction()) { - if (keepReturns) { - dest = ret.append(fresh); - } else { - dest = ret.append(InstructionFactory.createBranchInstruction(Constants.GOTO, end)); - } - } else if (fresh instanceof InstructionBranch) { - dest = ret.append((InstructionBranch) fresh); - } else if (fresh.isLocalVariableInstruction() || fresh instanceof RET) { - - // IndexedInstruction indexed = (IndexedInstruction) fresh; - int oldIndex = fresh.getIndex(); - int freshIndex; - if (!frameEnv.hasKey(oldIndex)) { - freshIndex = recipient.allocateLocal(2); - frameEnv.put(oldIndex, freshIndex); - } else { - freshIndex = frameEnv.get(oldIndex); - } - if (fresh instanceof RET) { - fresh.setIndex(freshIndex); - } else { - fresh = ((InstructionLV) fresh).setIndexAndCopyIfNecessary(freshIndex); - } - dest = ret.append(fresh); - } else { - dest = ret.append(fresh); - } - srcToDest.put(src, dest); - } - - // second pass: retarget branch instructions, copy ranges and tags - Map tagMap = new HashMap(); - Map shadowMap = new HashMap(); - for (InstructionHandle dest = ret.getStart(), src = sourceList.getStart(); dest != null; dest = dest.getNext(), src = src - .getNext()) { - Instruction inst = dest.getInstruction(); - - // retarget branches - if (inst instanceof InstructionBranch) { - InstructionBranch branch = (InstructionBranch) inst; - InstructionHandle oldTarget = branch.getTarget(); - InstructionHandle newTarget = srcToDest.get(oldTarget); - if (newTarget == null) { - // assert this is a GOTO - // this was a return instruction we previously replaced - } else { - branch.setTarget(newTarget); - if (branch instanceof InstructionSelect) { - InstructionSelect select = (InstructionSelect) branch; - InstructionHandle[] oldTargets = select.getTargets(); - for (int k = oldTargets.length - 1; k >= 0; k--) { - select.setTarget(k, srcToDest.get(oldTargets[k])); - } - } - } - } - - // copy over tags and range attributes - - Iterator tIter = src.getTargeters().iterator(); - while (tIter.hasNext()) { - InstructionTargeter old = tIter.next(); - if (old instanceof Tag) { - Tag oldTag = (Tag) old; - Tag fresh = tagMap.get(oldTag); - if (fresh == null) { - fresh = oldTag.copy(); - if (old instanceof LocalVariableTag) { - // LocalVariable - LocalVariableTag lvTag = (LocalVariableTag) old; - LocalVariableTag lvTagFresh = (LocalVariableTag) fresh; - if (lvTag.getSlot() == 0) { - fresh = new LocalVariableTag(lvTag.getRealType().getSignature(), "ajc$aspectInstance", - frameEnv.get(lvTag.getSlot()), 0); - } else { - // // Do not move it - when copying the code from the aspect to the affected target, 'this' is - // // going to change from aspect to affected type. So just fix the type - // System.out.println("For local variable tag at instruction " + src + " changing slot from " - // + lvTag.getSlot() + " > " + frameEnv.get(lvTag.getSlot())); - lvTagFresh.updateSlot(frameEnv.get(lvTag.getSlot())); - } - } - tagMap.put(oldTag, fresh); - } - dest.addTargeter(fresh); - } else if (old instanceof ExceptionRange) { - ExceptionRange er = (ExceptionRange) old; - if (er.getStart() == src) { - ExceptionRange freshEr = new ExceptionRange(recipient.getBody(), er.getCatchType(), er.getPriority()); - freshEr.associateWithTargets(dest, srcToDest.get(er.getEnd()), srcToDest.get(er.getHandler())); - } - } else if (old instanceof ShadowRange) { - ShadowRange oldRange = (ShadowRange) old; - if (oldRange.getStart() == src) { - BcelShadow oldShadow = oldRange.getShadow(); - BcelShadow freshEnclosing = oldShadow.getEnclosingShadow() == null ? null : (BcelShadow) shadowMap - .get(oldShadow.getEnclosingShadow()); - BcelShadow freshShadow = oldShadow.copyInto(recipient, freshEnclosing); - ShadowRange freshRange = new ShadowRange(recipient.getBody()); - freshRange.associateWithShadow(freshShadow); - freshRange.associateWithTargets(dest, srcToDest.get(oldRange.getEnd())); - shadowMap.put(oldShadow, freshShadow); // oldRange, freshRange - // recipient.matchedShadows.add(freshShadow); - // XXX should go through the NEW copied shadow and - // update - // the thisVar, targetVar, and argsVar - // ??? Might want to also go through at this time and - // add - // "extra" vars to the shadow. - } - } - } - } - if (!keepReturns) { - ret.append(footer); - } - return ret; - } - - // static InstructionList rewriteWithMonitorExitCalls(InstructionList - // sourceList,InstructionFactory fact,boolean keepReturns,int - // monitorVarSlot,Type monitorVarType) - // { - // InstructionList footer = new InstructionList(); - // InstructionHandle end = footer.append(InstructionConstants.NOP); - // - // InstructionList newList = new InstructionList(); - // - // Map srcToDest = new HashMap(); - // - // // first pass: copy the instructions directly, populate the srcToDest - // map, - // // fix frame instructions - // for (InstructionHandle src = sourceList.getStart(); src != null; src = - // src.getNext()) { - // Instruction fresh = Utility.copyInstruction(src.getInstruction()); - // InstructionHandle dest; - // if (src.getInstruction() == Range.RANGEINSTRUCTION) { - // dest = newList.append(Range.RANGEINSTRUCTION); - // } else if (fresh.isReturnInstruction()) { - // if (keepReturns) { - // newList.append(InstructionFactory.createLoad(monitorVarType,monitorVarSlot - // )); - // newList.append(InstructionConstants.MONITOREXIT); - // dest = newList.append(fresh); - // } else { - // dest = - // newList.append(InstructionFactory.createBranchInstruction(Constants.GOTO, - // end)); - // } - // } else if (fresh instanceof InstructionBranch) { - // dest = newList.append((InstructionBranch) fresh); - // } else if ( - // fresh.isLocalVariableInstruction() || fresh instanceof RET) { - // //IndexedInstruction indexed = (IndexedInstruction) fresh; - // int oldIndex = fresh.getIndex(); - // int freshIndex; - // // if (!frameEnv.hasKey(oldIndex)) { - // // freshIndex = recipient.allocateLocal(2); - // // frameEnv.put(oldIndex, freshIndex); - // // } else { - // freshIndex = oldIndex;//frameEnv.get(oldIndex); - // // } - // if (fresh instanceof RET) { - // fresh.setIndex(freshIndex); - // } else { - // fresh = ((InstructionLV)fresh).setIndexAndCopyIfNecessary(freshIndex); - // } - // dest = newList.append(fresh); - // } else { - // dest = newList.append(fresh); - // } - // srcToDest.put(src, dest); - // } - // - // // second pass: retarget branch instructions, copy ranges and tags - // Map tagMap = new HashMap(); - // for (InstructionHandle dest = newList.getStart(), src = - // sourceList.getStart(); - // dest != null; - // dest = dest.getNext(), src = src.getNext()) { - // Instruction inst = dest.getInstruction(); - // - // // retarget branches - // if (inst instanceof InstructionBranch) { - // InstructionBranch branch = (InstructionBranch) inst; - // InstructionHandle oldTarget = branch.getTarget(); - // InstructionHandle newTarget = - // (InstructionHandle) srcToDest.get(oldTarget); - // if (newTarget == null) { - // // assert this is a GOTO - // // this was a return instruction we previously replaced - // } else { - // branch.setTarget(newTarget); - // if (branch instanceof InstructionSelect) { - // InstructionSelect select = (InstructionSelect) branch; - // InstructionHandle[] oldTargets = select.getTargets(); - // for (int k = oldTargets.length - 1; k >= 0; k--) { - // select.setTarget( - // k, - // (InstructionHandle) srcToDest.get(oldTargets[k])); - // } - // } - // } - // } - // - // //copy over tags and range attributes - // Iterator tIter = src.getTargeters().iterator(); - // - // while (tIter.hasNext()) { - // InstructionTargeter old = (InstructionTargeter)tIter.next(); - // if (old instanceof Tag) { - // Tag oldTag = (Tag) old; - // Tag fresh = (Tag) tagMap.get(oldTag); - // if (fresh == null) { - // fresh = oldTag.copy(); - // tagMap.put(oldTag, fresh); - // } - // dest.addTargeter(fresh); - // } else if (old instanceof ExceptionRange) { - // ExceptionRange er = (ExceptionRange) old; - // if (er.getStart() == src) { - // ExceptionRange freshEr = - // new ExceptionRange(newList/*recipient.getBody()*/,er.getCatchType(),er. - // getPriority()); - // freshEr.associateWithTargets( - // dest, - // (InstructionHandle)srcToDest.get(er.getEnd()), - // (InstructionHandle)srcToDest.get(er.getHandler())); - // } - // } - // /*else if (old instanceof ShadowRange) { - // ShadowRange oldRange = (ShadowRange) old; - // if (oldRange.getStart() == src) { - // BcelShadow oldShadow = oldRange.getShadow(); - // BcelShadow freshEnclosing = - // oldShadow.getEnclosingShadow() == null - // ? null - // : (BcelShadow) shadowMap.get(oldShadow.getEnclosingShadow()); - // BcelShadow freshShadow = - // oldShadow.copyInto(recipient, freshEnclosing); - // ShadowRange freshRange = new ShadowRange(recipient.getBody()); - // freshRange.associateWithShadow(freshShadow); - // freshRange.associateWithTargets( - // dest, - // (InstructionHandle) srcToDest.get(oldRange.getEnd())); - // shadowMap.put(oldRange, freshRange); - // //recipient.matchedShadows.add(freshShadow); - // // XXX should go through the NEW copied shadow and update - // // the thisVar, targetVar, and argsVar - // // ??? Might want to also go through at this time and add - // // "extra" vars to the shadow. - // } - // }*/ - // } - // } - // if (!keepReturns) newList.append(footer); - // return newList; - // } - - /** - * generate the argument stores in preparation for inlining. - * - * @param donor the method we will inline from. Used to get the signature. - * @param recipient the method we will inline into. Used to get the frame size so we can allocate fresh locations. - * @param frameEnv an empty environment we populate with a map from donor frame to recipient frame. - * @param fact an instruction factory for recipient - */ - private static InstructionList genArgumentStores(LazyMethodGen donor, LazyMethodGen recipient, IntMap frameEnv, - InstructionFactory fact) { - InstructionList ret = new InstructionList(); - - int donorFramePos = 0; - - // writing ret back to front because we're popping. - if (!donor.isStatic()) { - int targetSlot = recipient.allocateLocal(Type.OBJECT); - ret.insert(InstructionFactory.createStore(Type.OBJECT, targetSlot)); - frameEnv.put(donorFramePos, targetSlot); - donorFramePos += 1; - } - Type[] argTypes = donor.getArgumentTypes(); - for (int i = 0, len = argTypes.length; i < len; i++) { - Type argType = argTypes[i]; - int argSlot = recipient.allocateLocal(argType); - ret.insert(InstructionFactory.createStore(argType, argSlot)); - frameEnv.put(donorFramePos, argSlot); - donorFramePos += argType.getSize(); - } - return ret; - } - - /** - * get a called method: Assumes the called method is in this class, and the reference to it is exact (a la INVOKESPECIAL). - * - * @param ih The InvokeInstruction instructionHandle pointing to the called method. - */ - private LazyMethodGen getCalledMethod(InstructionHandle ih) { - InvokeInstruction inst = (InvokeInstruction) ih.getInstruction(); - - String methodName = inst.getName(cpg); - String signature = inst.getSignature(cpg); - - return clazz.getLazyMethodGen(methodName, signature); - } - - private void weaveInAddedMethods() { - Collections.sort(addedLazyMethodGens, new Comparator() { - public int compare(LazyMethodGen aa, LazyMethodGen bb) { - int i = aa.getName().compareTo(bb.getName()); - if (i != 0) { - return i; - } - return aa.getSignature().compareTo(bb.getSignature()); - } - }); - - for (LazyMethodGen addedMember : addedLazyMethodGens) { - clazz.addMethodGen(addedMember); - } - } - - // void addPerSingletonField(Member field) { - // ObjectType aspectType = (ObjectType) - // BcelWorld.makeBcelType(field.getReturnType()); - // String aspectName = field.getReturnType().getName(); - // - // LazyMethodGen clinit = clazz.getStaticInitializer(); - // InstructionList setup = new InstructionList(); - // InstructionFactory fact = clazz.getFactory(); - // - // setup.append(fact.createNew(aspectType)); - // setup.append(InstructionFactory.createDup(1)); - // setup.append(fact.createInvoke(aspectName, "", Type.VOID, new - // Type[0], Constants.INVOKESPECIAL)); - // setup.append(fact.createFieldAccess(aspectName, field.getName(), - // aspectType, Constants.PUTSTATIC)); - // clinit.getBody().insert(setup); - // } - - /** - * Returns null if this is not a Java constructor, and then we won't weave into it at all - */ - private InstructionHandle findSuperOrThisCall(LazyMethodGen mg) { - int depth = 1; - InstructionHandle start = mg.getBody().getStart(); - while (true) { - if (start == null) { - return null; - } - - Instruction inst = start.getInstruction(); - if (inst.opcode == Constants.INVOKESPECIAL && ((InvokeInstruction) inst).getName(cpg).equals("")) { - depth--; - if (depth == 0) { - return start; - } - } else if (inst.opcode == Constants.NEW) { - depth++; - } - start = start.getNext(); - } - } - - // ---- - - private boolean match(LazyMethodGen mg) { - BcelShadow enclosingShadow; - List shadowAccumulator = new ArrayList(); - boolean isOverweaving = world.isOverWeaving(); - boolean startsAngly = mg.getName().charAt(0) == '<'; - // we want to match ajsynthetic constructors... - if (startsAngly && mg.getName().equals("")) { - return matchInit(mg, shadowAccumulator); - } else if (!shouldWeaveBody(mg)) { - return false; - } else { - if (startsAngly && mg.getName().equals("")) { - // clinitShadow = - enclosingShadow = BcelShadow.makeStaticInitialization(world, mg); - // System.err.println(enclosingShadow); - } else if (mg.isAdviceMethod()) { - enclosingShadow = BcelShadow.makeAdviceExecution(world, mg); - } else { - AjAttribute.EffectiveSignatureAttribute effective = mg.getEffectiveSignature(); - if (effective == null) { - // Don't want ajc$preClinit to be considered for matching - if (isOverweaving && mg.getName().startsWith(NameMangler.PREFIX)) { - return false; - } - enclosingShadow = BcelShadow.makeMethodExecution(world, mg, !canMatchBodyShadows); - } else if (effective.isWeaveBody()) { - ResolvedMember rm = effective.getEffectiveSignature(); - - // Annotations for things with effective signatures are - // never stored in the effective - // signature itself - we have to hunt for them. Storing them - // in the effective signature - // would mean keeping two sets up to date (no way!!) - - fixParameterNamesForResolvedMember(rm, mg.getMemberView()); - fixAnnotationsForResolvedMember(rm, mg.getMemberView()); - - enclosingShadow = BcelShadow.makeShadowForMethod(world, mg, effective.getShadowKind(), rm); - } else { - return false; - } - } - - if (canMatchBodyShadows) { - for (InstructionHandle h = mg.getBody().getStart(); h != null; h = h.getNext()) { - match(mg, h, enclosingShadow, shadowAccumulator); - } - } - // FIXME asc change from string match if we can, rather brittle. - // this check actually prevents field-exec jps - if (canMatch(enclosingShadow.getKind()) - && !(mg.getName().charAt(0) == 'a' && mg.getName().startsWith("ajc$interFieldInit"))) { - if (match(enclosingShadow, shadowAccumulator)) { - enclosingShadow.init(); - } - } - mg.matchedShadows = shadowAccumulator; - return !shadowAccumulator.isEmpty(); - } - } - - private boolean matchInit(LazyMethodGen mg, List shadowAccumulator) { - BcelShadow enclosingShadow; - // XXX the enclosing join point is wrong for things before ignoreMe. - InstructionHandle superOrThisCall = findSuperOrThisCall(mg); - - // we don't walk bodies of things where it's a wrong constructor thingie - if (superOrThisCall == null) { - return false; - } - - enclosingShadow = BcelShadow.makeConstructorExecution(world, mg, superOrThisCall); - if (mg.getEffectiveSignature() != null) { - enclosingShadow.setMatchingSignature(mg.getEffectiveSignature().getEffectiveSignature()); - } - - // walk the body - boolean beforeSuperOrThisCall = true; - if (shouldWeaveBody(mg)) { - if (canMatchBodyShadows) { - for (InstructionHandle h = mg.getBody().getStart(); h != null; h = h.getNext()) { - if (h == superOrThisCall) { - beforeSuperOrThisCall = false; - continue; - } - match(mg, h, beforeSuperOrThisCall ? null : enclosingShadow, shadowAccumulator); - } - } - if (canMatch(Shadow.ConstructorExecution)) { - match(enclosingShadow, shadowAccumulator); - } - } - - // XXX we don't do pre-inits of interfaces - - // now add interface inits - if (!isThisCall(superOrThisCall)) { - InstructionHandle curr = enclosingShadow.getRange().getStart(); - for (Iterator i = addedSuperInitializersAsList.iterator(); i.hasNext();) { - IfaceInitList l = i.next(); - - Member ifaceInitSig = AjcMemberMaker.interfaceConstructor(l.onType); - - BcelShadow initShadow = BcelShadow.makeIfaceInitialization(world, mg, ifaceInitSig); - - // insert code in place - InstructionList inits = genInitInstructions(l.list, false); - if (match(initShadow, shadowAccumulator) || !inits.isEmpty()) { - initShadow.initIfaceInitializer(curr); - initShadow.getRange().insert(inits, Range.OutsideBefore); - } - } - - // now we add our initialization code - InstructionList inits = genInitInstructions(addedThisInitializers, false); - enclosingShadow.getRange().insert(inits, Range.OutsideBefore); - } - - // actually, you only need to inline the self constructors that are - // in a particular group (partition the constructors into groups where - // members - // call or are called only by those in the group). Then only inline - // constructors - // in groups where at least one initialization jp matched. Future work. - boolean addedInitialization = match(BcelShadow.makeUnfinishedInitialization(world, mg), initializationShadows); - addedInitialization |= match(BcelShadow.makeUnfinishedPreinitialization(world, mg), initializationShadows); - mg.matchedShadows = shadowAccumulator; - return addedInitialization || !shadowAccumulator.isEmpty(); - } - - private boolean shouldWeaveBody(LazyMethodGen mg) { - if (mg.isBridgeMethod()) { - return false; - } - if (mg.isAjSynthetic()) { - return mg.getName().equals(""); - } - AjAttribute.EffectiveSignatureAttribute a = mg.getEffectiveSignature(); - if (a != null) { - return a.isWeaveBody(); - } - return true; - } - - /** - * first sorts the mungers, then gens the initializers in the right order - */ - private InstructionList genInitInstructions(List list, boolean isStatic) { - list = PartialOrder.sort(list); - if (list == null) { - throw new BCException("circularity in inter-types"); - } - - InstructionList ret = new InstructionList(); - - for (ConcreteTypeMunger cmunger : list) { - NewFieldTypeMunger munger = (NewFieldTypeMunger) cmunger.getMunger(); - ResolvedMember initMethod = munger.getInitMethod(cmunger.getAspectType()); - if (!isStatic) { - ret.append(InstructionConstants.ALOAD_0); - } - ret.append(Utility.createInvoke(fact, world, initMethod)); - } - return ret; - } - - private void match(LazyMethodGen mg, InstructionHandle ih, BcelShadow enclosingShadow, List shadowAccumulator) { - Instruction i = ih.getInstruction(); - - // Exception handlers (pr230817) - if (canMatch(Shadow.ExceptionHandler) && !Range.isRangeHandle(ih)) { - Set targeters = ih.getTargetersCopy(); - // If in Java7 there may be overlapping exception ranges for multi catch - we should recognize that - for (InstructionTargeter t : targeters) { - if (t instanceof ExceptionRange) { - // assert t.getHandler() == ih - ExceptionRange er = (ExceptionRange) t; - if (er.getCatchType() == null) { - continue; - } - if (isInitFailureHandler(ih)) { - return; - } - if (!ih.getInstruction().isStoreInstruction() && ih.getInstruction().getOpcode() != Constants.NOP) { - // If using cobertura, the catch block stats with - // INVOKESTATIC rather than ASTORE, in order that the ranges - // for the methodcall and exceptionhandler shadows - // that occur at this same - // line, we need to modify the instruction list to - // split them - adding a - // NOP before the invokestatic that gets all the targeters - // that were aimed at the INVOKESTATIC - mg.getBody().insert(ih, InstructionConstants.NOP); - InstructionHandle newNOP = ih.getPrev(); - // what about a try..catch that starts at the start - // of the exception handler? need to only include - // certain targeters really. - er.updateTarget(ih, newNOP, mg.getBody()); - for (InstructionTargeter t2 : targeters) { - newNOP.addTargeter(t2); - } - ih.removeAllTargeters(); - match(BcelShadow.makeExceptionHandler(world, er, mg, newNOP, enclosingShadow), shadowAccumulator); - } else { - match(BcelShadow.makeExceptionHandler(world, er, mg, ih, enclosingShadow), shadowAccumulator); - } - } - } - } - - if ((i instanceof FieldInstruction) && (canMatch(Shadow.FieldGet) || canMatch(Shadow.FieldSet))) { - FieldInstruction fi = (FieldInstruction) i; - - if (fi.opcode == Constants.PUTFIELD || fi.opcode == Constants.PUTSTATIC) { - // check for sets of constant fields. We first check the - // previous - // instruction. If the previous instruction is a LD_WHATEVER - // (push - // constant on the stack) then we must resolve the field to - // determine - // if it's final. If it is final, then we don't generate a - // shadow. - InstructionHandle prevHandle = ih.getPrev(); - Instruction prevI = prevHandle.getInstruction(); - if (Utility.isConstantPushInstruction(prevI)) { - Member field = BcelWorld.makeFieldJoinPointSignature(clazz, (FieldInstruction) i); - ResolvedMember resolvedField = field.resolve(world); - if (resolvedField == null) { - // we can't find the field, so it's not a join point. - } else if (Modifier.isFinal(resolvedField.getModifiers())) { - // it's final, so it's the set of a final constant, so - // it's - // not a join point according to 1.0.6 and 1.1. - } else { - if (canMatch(Shadow.FieldSet)) { - matchSetInstruction(mg, ih, enclosingShadow, shadowAccumulator); - } - } - } else { - if (canMatch(Shadow.FieldSet)) { - matchSetInstruction(mg, ih, enclosingShadow, shadowAccumulator); - } - } - } else { - if (canMatch(Shadow.FieldGet)) { - matchGetInstruction(mg, ih, enclosingShadow, shadowAccumulator); - } - } - } else if (i instanceof InvokeInstruction) { - InvokeInstruction ii = (InvokeInstruction) i; - if (ii.getMethodName(clazz.getConstantPool()).equals("")) { - if (canMatch(Shadow.ConstructorCall)) { - match(BcelShadow.makeConstructorCall(world, mg, ih, enclosingShadow), shadowAccumulator); - } - } else if (ii.opcode == Constants.INVOKESPECIAL) { - String onTypeName = ii.getClassName(cpg); - if (onTypeName.equals(mg.getEnclosingClass().getName())) { - // we are private - matchInvokeInstruction(mg, ih, ii, enclosingShadow, shadowAccumulator); - } else { - // we are a super call, and this is not a join point in - // AspectJ-1.{0,1} - } - } else { - if (ii.getOpcode()!=Constants.INVOKEDYNAMIC) { - matchInvokeInstruction(mg, ih, ii, enclosingShadow, shadowAccumulator); - } - } - } else if (world.isJoinpointArrayConstructionEnabled() && i.isArrayCreationInstruction()) { - if (canMatch(Shadow.ConstructorCall)) { - if (i.opcode == Constants.ANEWARRAY) { - // ANEWARRAY arrayInstruction = (ANEWARRAY)i; - // ObjectType arrayType = i.getLoadClassType(clazz.getConstantPool()); - BcelShadow ctorCallShadow = BcelShadow.makeArrayConstructorCall(world, mg, ih, enclosingShadow); - match(ctorCallShadow, shadowAccumulator); - } else if (i.opcode == Constants.NEWARRAY) { - // NEWARRAY arrayInstruction = (NEWARRAY)i; - // Type arrayType = i.getType(); - BcelShadow ctorCallShadow = BcelShadow.makeArrayConstructorCall(world, mg, ih, enclosingShadow); - match(ctorCallShadow, shadowAccumulator); - } else if (i instanceof MULTIANEWARRAY) { - // MULTIANEWARRAY arrayInstruction = (MULTIANEWARRAY) i; - // ObjectType arrayType = arrayInstruction.getLoadClassType(clazz.getConstantPool()); - BcelShadow ctorCallShadow = BcelShadow.makeArrayConstructorCall(world, mg, ih, enclosingShadow); - match(ctorCallShadow, shadowAccumulator); - } - } - // see pr77166 if you are thinking about implementing this - // } else if (i instanceof AALOAD ) { - // AALOAD arrayLoad = (AALOAD)i; - // Type arrayType = arrayLoad.getType(clazz.getConstantPoolGen()); - // BcelShadow arrayLoadShadow = - // BcelShadow.makeArrayLoadCall(world,mg,ih,enclosingShadow); - // match(arrayLoadShadow,shadowAccumulator); - // } else if (i instanceof AASTORE) { - // // ... magic required - } else if (world.isJoinpointSynchronizationEnabled() - && ((i.getOpcode() == Constants.MONITORENTER) || (i.getOpcode() == Constants.MONITOREXIT))) { - // if (canMatch(Shadow.Monitoring)) { - if (i.getOpcode() == Constants.MONITORENTER) { - BcelShadow monitorEntryShadow = BcelShadow.makeMonitorEnter(world, mg, ih, enclosingShadow); - match(monitorEntryShadow, shadowAccumulator); - } else { - BcelShadow monitorExitShadow = BcelShadow.makeMonitorExit(world, mg, ih, enclosingShadow); - match(monitorExitShadow, shadowAccumulator); - } - // } - } - - } - - private boolean isInitFailureHandler(InstructionHandle ih) { - // Skip the astore_0 and aload_0 at the start of the handler and - // then check if the instruction following these is - // 'putstatic ajc$initFailureCause'. If it is then we are - // in the handler we created in AspectClinit.generatePostSyntheticCode() - InstructionHandle twoInstructionsAway = ih.getNext().getNext(); - if (twoInstructionsAway.getInstruction().opcode == Constants.PUTSTATIC) { - String name = ((FieldInstruction) twoInstructionsAway.getInstruction()).getFieldName(cpg); - if (name.equals(NameMangler.INITFAILURECAUSE_FIELD_NAME)) { - return true; - } - } - return false; - } - - private void matchSetInstruction(LazyMethodGen mg, InstructionHandle ih, BcelShadow enclosingShadow, - List shadowAccumulator) { - FieldInstruction fi = (FieldInstruction) ih.getInstruction(); - Member field = BcelWorld.makeFieldJoinPointSignature(clazz, fi); - - // synthetic fields are never join points - if (field.getName().startsWith(NameMangler.PREFIX)) { - return; - } - - ResolvedMember resolvedField = field.resolve(world); - if (resolvedField == null) { - // we can't find the field, so it's not a join point. - return; - } else if (Modifier.isFinal(resolvedField.getModifiers()) - && Utility.isConstantPushInstruction(ih.getPrev().getInstruction())) { - // it's the set of a final constant, so it's - // not a join point according to 1.0.6 and 1.1. - return; - } else if (resolvedField.isSynthetic()) { - // sets of synthetics aren't join points in 1.1 - return; - } else { - // Fix for bug 172107 (similar the "get" fix for bug 109728) - BcelShadow bs = BcelShadow.makeFieldSet(world, resolvedField, mg, ih, enclosingShadow); - String cname = fi.getClassName(cpg); - if (!resolvedField.getDeclaringType().getName().equals(cname)) { - bs.setActualTargetType(cname); - } - match(bs, shadowAccumulator); - } - } - - private void matchGetInstruction(LazyMethodGen mg, InstructionHandle ih, BcelShadow enclosingShadow, - List shadowAccumulator) { - FieldInstruction fi = (FieldInstruction) ih.getInstruction(); - Member field = BcelWorld.makeFieldJoinPointSignature(clazz, fi); - - // synthetic fields are never join points - if (field.getName().startsWith(NameMangler.PREFIX)) { - return; - } - - ResolvedMember resolvedField = field.resolve(world); - if (resolvedField == null) { - // we can't find the field, so it's not a join point. - return; - } else if (resolvedField.isSynthetic()) { - // sets of synthetics aren't join points in 1.1 - return; - } else { - BcelShadow bs = BcelShadow.makeFieldGet(world, resolvedField, mg, ih, enclosingShadow); - String cname = fi.getClassName(cpg); - if (!resolvedField.getDeclaringType().getName().equals(cname)) { - bs.setActualTargetType(cname); - } - match(bs, shadowAccumulator); - } - } - - /** - * For some named resolved type, this method looks for a member with a particular name - it should only be used when you truly - * believe there is only one member with that name in the type as it returns the first one it finds. - */ - private ResolvedMember findResolvedMemberNamed(ResolvedType type, String methodName) { - ResolvedMember[] allMethods = type.getDeclaredMethods(); - for (int i = 0; i < allMethods.length; i++) { - ResolvedMember member = allMethods[i]; - if (member.getName().equals(methodName)) { - return member; - } - } - return null; - } - - /** - * Find the specified member in the specified type. - * - * @param type the type to search for the member - * @param methodName the name of the method to find - * @param params the method parameters that the discovered method should have - */ - private ResolvedMember findResolvedMemberNamed(ResolvedType type, String methodName, UnresolvedType[] params) { - ResolvedMember[] allMethods = type.getDeclaredMethods(); - List candidates = new ArrayList(); - for (int i = 0; i < allMethods.length; i++) { - ResolvedMember candidate = allMethods[i]; - if (candidate.getName().equals(methodName)) { - if (candidate.getArity() == params.length) { - candidates.add(candidate); - } - } - } - - if (candidates.size() == 0) { - return null; - } else if (candidates.size() == 1) { - return candidates.get(0); - } else { - // multiple candidates - for (ResolvedMember candidate : candidates) { - // These checks will break down with generics... but that would need two ITDs with the same name, same arity and - // generics - boolean allOK = true; - UnresolvedType[] candidateParams = candidate.getParameterTypes(); - for (int p = 0; p < candidateParams.length; p++) { - if (!candidateParams[p].getErasureSignature().equals(params[p].getErasureSignature())) { - allOK = false; - break; - } - } - if (allOK) { - return candidate; - } - } - } - return null; - } - - /** - * For a given resolvedmember, this will discover the real annotations for it. Should only be used when the resolvedmember is - * the contents of an effective signature attribute, as thats the only time when the annotations aren't stored directly in the - * resolvedMember - * - * @param rm the sig we want it to pretend to be 'int A.m()' or somesuch ITD like thing - * @param declaredSig the real sig 'blah.ajc$xxx' - */ - private void fixParameterNamesForResolvedMember(ResolvedMember rm, ResolvedMember declaredSig) { - - UnresolvedType memberHostType = declaredSig.getDeclaringType(); - String methodName = declaredSig.getName(); - String[] pnames = null; - if (rm.getKind() == Member.METHOD && !rm.isAbstract()) { - if (methodName.startsWith("ajc$inlineAccessMethod") || methodName.startsWith("ajc$superDispatch")) { - ResolvedMember resolvedDooberry = world.resolve(declaredSig); - pnames = resolvedDooberry.getParameterNames(); - } else { - ResolvedMember realthing = AjcMemberMaker.interMethodDispatcher(rm.resolve(world), memberHostType).resolve(world); - ResolvedMember theRealMember = findResolvedMemberNamed(memberHostType.resolve(world), realthing.getName()); - if (theRealMember != null) { - pnames = theRealMember.getParameterNames(); - // static ITDs don't need any parameter shifting - if (pnames.length > 0 && pnames[0].equals("ajc$this_")) { - String[] pnames2 = new String[pnames.length - 1]; - System.arraycopy(pnames, 1, pnames2, 0, pnames2.length); - pnames = pnames2; - } - } - } - // i think ctors are missing from here... copy code from below... - } - rm.setParameterNames(pnames); - } - - /** - * For a given resolvedmember, this will discover the real annotations for it. Should only be used when the resolvedmember is - * the contents of an effective signature attribute, as thats the only time when the annotations aren't stored directly in the - * resolvedMember - * - * @param rm the sig we want it to pretend to be 'int A.m()' or somesuch ITD like thing - * @param declaredSig the real sig 'blah.ajc$xxx' - */ - private void fixAnnotationsForResolvedMember(ResolvedMember rm, ResolvedMember declaredSig) { - try { - UnresolvedType memberHostType = declaredSig.getDeclaringType(); - boolean containsKey = mapToAnnotationHolder.containsKey(rm); - ResolvedMember realAnnotationHolder = mapToAnnotationHolder.get(rm); - String methodName = declaredSig.getName(); - // FIXME asc shouldnt really rely on string names ! - if (!containsKey) { - if (rm.getKind() == Member.FIELD) { - if (methodName.startsWith("ajc$inlineAccessField")) { - realAnnotationHolder = world.resolve(rm); - } else { - ResolvedMember realthing = AjcMemberMaker.interFieldInitializer(rm, memberHostType); - realAnnotationHolder = world.resolve(realthing); - } - } else if (rm.getKind() == Member.METHOD && !rm.isAbstract()) { - if (methodName.startsWith("ajc$inlineAccessMethod") || methodName.startsWith("ajc$superDispatch")) { - realAnnotationHolder = world.resolve(declaredSig); - } else { - ResolvedMember realthing = AjcMemberMaker.interMethodDispatcher(rm.resolve(world), memberHostType).resolve(world); - realAnnotationHolder = findResolvedMemberNamed(memberHostType.resolve(world), realthing.getName(),realthing.getParameterTypes()); - if (realAnnotationHolder == null) { - throw new UnsupportedOperationException( - "Known limitation in M4 - can't find ITD members when type variable is used as an argument and has upper bound specified"); - } - } - } else if (rm.getKind() == Member.CONSTRUCTOR) { - ResolvedMember realThing = AjcMemberMaker.postIntroducedConstructor(memberHostType.resolve(world),rm.getDeclaringType(), rm.getParameterTypes()); - realAnnotationHolder = world.resolve(realThing); - // AMC temp guard for M4 - if (realAnnotationHolder == null) { - throw new UnsupportedOperationException("Known limitation in M4 - can't find ITD members when type variable is used as an argument and has upper bound specified"); - } - } - mapToAnnotationHolder.put(rm, realAnnotationHolder); - } - ResolvedType[] annotationTypes; - AnnotationAJ[] annotations; - if (realAnnotationHolder!=null) { - annotationTypes = realAnnotationHolder.getAnnotationTypes(); - annotations = realAnnotationHolder.getAnnotations(); - if (annotationTypes==null) { - annotationTypes = ResolvedType.EMPTY_ARRAY; - } - if (annotations==null) { - annotations = AnnotationAJ.EMPTY_ARRAY; - } - } else { - annotations = AnnotationAJ.EMPTY_ARRAY; - annotationTypes = ResolvedType.EMPTY_ARRAY; - } - rm.setAnnotations(annotations); - rm.setAnnotationTypes(annotationTypes); - } catch (UnsupportedOperationException ex) { - throw ex; - } catch (Throwable t) { - // FIXME asc remove this catch after more testing has confirmed the - // above stuff is OK - throw new BCException("Unexpectedly went bang when searching for annotations on " + rm, t); - } - } - - private void matchInvokeInstruction(LazyMethodGen mg, InstructionHandle ih, InvokeInstruction invoke, - BcelShadow enclosingShadow, List shadowAccumulator) { - String methodName = invoke.getName(cpg); - if (methodName.startsWith(NameMangler.PREFIX)) { - Member jpSig = world.makeJoinPointSignatureForMethodInvocation(clazz, invoke); - ResolvedMember declaredSig = jpSig.resolve(world); - // System.err.println(method + ", declaredSig: " +declaredSig); - if (declaredSig == null) { - return; - } - - if (declaredSig.getKind() == Member.FIELD) { - Shadow.Kind kind; - if (jpSig.getReturnType().equals(UnresolvedType.VOID)) { - kind = Shadow.FieldSet; - } else { - kind = Shadow.FieldGet; - } - - if (canMatch(Shadow.FieldGet) || canMatch(Shadow.FieldSet)) { - match(BcelShadow.makeShadowForMethodCall(world, mg, ih, enclosingShadow, kind, declaredSig), shadowAccumulator); - } - } else if (!declaredSig.getName().startsWith(NameMangler.PREFIX)) { - // 307147 - resolution above may have found the real method directly rather - // than needing to go through the effective signature attribute - if (canMatch(Shadow.MethodCall)) { - match(BcelShadow.makeShadowForMethodCall(world, mg, ih, enclosingShadow, Shadow.MethodCall, declaredSig), - shadowAccumulator); - } - } else { - AjAttribute.EffectiveSignatureAttribute effectiveSig = declaredSig.getEffectiveSignature(); - if (effectiveSig == null) { - return; - } - // System.err.println("call to inter-type member: " + - // effectiveSig); - if (effectiveSig.isWeaveBody()) { - return; - } - - ResolvedMember rm = effectiveSig.getEffectiveSignature(); - fixParameterNamesForResolvedMember(rm, declaredSig); - fixAnnotationsForResolvedMember(rm, declaredSig); // abracadabra - - if (canMatch(effectiveSig.getShadowKind())) { - match(BcelShadow.makeShadowForMethodCall(world, mg, ih, enclosingShadow, effectiveSig.getShadowKind(), rm), - shadowAccumulator); - } - } - } else { - if (canMatch(Shadow.MethodCall)) { - boolean proceed = true; - // overweaving needs to ignore some calls added by the previous weave - if (world.isOverWeaving()) { - String s = invoke.getClassName(mg.getConstantPool()); - // skip all the inc/dec/isValid/etc - if (s.length() > 4 - && s.charAt(4) == 'a' - && (s.equals("org.aspectj.runtime.internal.CFlowCounter") - || s.equals("org.aspectj.runtime.internal.CFlowStack") || s - .equals("org.aspectj.runtime.reflect.Factory"))) { - proceed = false; - } else { - if (methodName.equals("aspectOf")) { - proceed = false; - } - } - } - if (proceed) { - match(BcelShadow.makeMethodCall(world, mg, ih, enclosingShadow), shadowAccumulator); - } - } - } - } - - // static ... so all worlds will share the config for the first one - // created... - private static boolean checkedXsetForLowLevelContextCapturing = false; - private static boolean captureLowLevelContext = false; - - private boolean match(BcelShadow shadow, List shadowAccumulator) { - // Duplicate blocks - one with context one without, seems faster than multiple 'ifs' - if (captureLowLevelContext) { - ContextToken shadowMatchToken = CompilationAndWeavingContext.enteringPhase( - CompilationAndWeavingContext.MATCHING_SHADOW, shadow); - boolean isMatched = false; - - Shadow.Kind shadowKind = shadow.getKind(); - List candidateMungers = indexedShadowMungers[shadowKind.getKey()]; - - // System.out.println("Candidates " + candidateMungers); - if (candidateMungers != null) { - for (ShadowMunger munger : candidateMungers) { - - ContextToken mungerMatchToken = CompilationAndWeavingContext.enteringPhase( - CompilationAndWeavingContext.MATCHING_POINTCUT, munger.getPointcut()); - if (munger.match(shadow, world)) { - shadow.addMunger(munger); - isMatched = true; - if (shadow.getKind() == Shadow.StaticInitialization) { - clazz.warnOnAddedStaticInitializer(shadow, munger.getSourceLocation()); - } - } - CompilationAndWeavingContext.leavingPhase(mungerMatchToken); - } - - if (isMatched) { - shadowAccumulator.add(shadow); - } - } - CompilationAndWeavingContext.leavingPhase(shadowMatchToken); - return isMatched; - } else { - boolean isMatched = false; - - Shadow.Kind shadowKind = shadow.getKind(); - List candidateMungers = indexedShadowMungers[shadowKind.getKey()]; - - // System.out.println("Candidates at " + shadowKind + " are " + candidateMungers); - if (candidateMungers != null) { - for (ShadowMunger munger : candidateMungers) { - if (munger.match(shadow, world)) { - shadow.addMunger(munger); - isMatched = true; - if (shadow.getKind() == Shadow.StaticInitialization) { - clazz.warnOnAddedStaticInitializer(shadow, munger.getSourceLocation()); - } - } - } - if (isMatched) { - shadowAccumulator.add(shadow); - } - } - return isMatched; - } - } - - // ---- - - private void implement(LazyMethodGen mg) { - List shadows = mg.matchedShadows; - if (shadows == null) { - return; - } - // We depend on a partial order such that inner shadows are earlier on - // the list than outer shadows. That's fine. This order is preserved if: - - // A preceeds B iff B.getStart() is LATER THAN A.getStart(). - - for (BcelShadow shadow : shadows) { - ContextToken tok = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.IMPLEMENTING_ON_SHADOW, - shadow); - shadow.implement(); - CompilationAndWeavingContext.leavingPhase(tok); - } - // int ii = - mg.getMaxLocals(); - mg.matchedShadows = null; - } - - // ---- - - public LazyClassGen getLazyClassGen() { - return clazz; - } - - public BcelWorld getWorld() { - return world; - } - - public void setReweavableMode(boolean mode) { - inReweavableMode = mode; - } - - public boolean getReweavableMode() { - return inReweavableMode; - } - - @Override - public String toString() { - return "BcelClassWeaver instance for : " + clazz; - } - -} diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelConstantPoolReader.java b/weaver/src/org/aspectj/weaver/bcel/BcelConstantPoolReader.java deleted file mode 100644 index 2deaf57c1..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/BcelConstantPoolReader.java +++ /dev/null @@ -1,34 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2010 Contributors - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Andy Clement (SpringSource) - * ******************************************************************/ -package org.aspectj.weaver.bcel; - -import org.aspectj.apache.bcel.classfile.ConstantPool; -import org.aspectj.weaver.ConstantPoolReader; - -/** - * An implementation of the constant pool reader that speaks Bcel. - * - * @author Andy Clement - */ -public class BcelConstantPoolReader implements ConstantPoolReader { - - private ConstantPool constantPool; - - public BcelConstantPoolReader(ConstantPool constantPool) { - this.constantPool = constantPool; - } - - public String readUtf8(int cpIndex) { - return constantPool.getConstantUtf8(cpIndex).getValue(); - } - -} diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelConstantPoolWriter.java b/weaver/src/org/aspectj/weaver/bcel/BcelConstantPoolWriter.java deleted file mode 100644 index 634764901..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/BcelConstantPoolWriter.java +++ /dev/null @@ -1,34 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2010 Contributors - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Andy Clement (SpringSource) - * ******************************************************************/ -package org.aspectj.weaver.bcel; - -import org.aspectj.apache.bcel.classfile.ConstantPool; -import org.aspectj.weaver.ConstantPoolWriter; - -/** - * An implementation of the constant pool writer that speaks Bcel. - * - * @author Andy Clement - */ -class BcelConstantPoolWriter implements ConstantPoolWriter { - - ConstantPool pool; - - public BcelConstantPoolWriter(ConstantPool pool) { - this.pool = pool; - } - - public int writeUtf8(String name) { - return pool.addUtf8(name); - } - -} \ No newline at end of file diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelField.java b/weaver/src/org/aspectj/weaver/bcel/BcelField.java deleted file mode 100644 index c88e8519f..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/BcelField.java +++ /dev/null @@ -1,307 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - -package org.aspectj.weaver.bcel; - -import java.util.List; - -import org.aspectj.apache.bcel.classfile.Attribute; -import org.aspectj.apache.bcel.classfile.ConstantPool; -import org.aspectj.apache.bcel.classfile.Field; -import org.aspectj.apache.bcel.classfile.Synthetic; -import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; -import org.aspectj.apache.bcel.generic.FieldGen; -import org.aspectj.util.GenericSignature; -import org.aspectj.util.GenericSignatureParser; -import org.aspectj.weaver.AjAttribute; -import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; -import org.aspectj.weaver.AnnotationAJ; -import org.aspectj.weaver.ISourceContext; -import org.aspectj.weaver.ResolvedMemberImpl; -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.UnresolvedType; -import org.aspectj.weaver.World; -import org.aspectj.weaver.bcel.BcelGenericSignatureToTypeXConverter.GenericSignatureFormatException; - -/** - * An AspectJ Field object that is backed by a Bcel Field object. - * - * @author PARC - * @author Andy Clement - */ -final class BcelField extends ResolvedMemberImpl { - - public static int AccSynthetic = 0x1000; - - private Field field; - private boolean isAjSynthetic; - private boolean isSynthetic = false; - private AnnotationAJ[] annotations; - private final World world; - private final BcelObjectType bcelObjectType; - private UnresolvedType genericFieldType = null; - private boolean unpackedGenericSignature = false; - private boolean annotationsOnFieldObjectAreOutOfDate = false; - - BcelField(BcelObjectType declaringType, Field field) { - super(FIELD, declaringType.getResolvedTypeX(), field.getModifiers(), field.getName(), field.getSignature()); - this.field = field; - this.world = declaringType.getResolvedTypeX().getWorld(); - this.bcelObjectType = declaringType; - unpackAttributes(world); - checkedExceptions = UnresolvedType.NONE; - } - - /** - * Constructs an instance that wrappers a Field object, but where we do not (yet) have a BcelObjectType - usually because the - * containing type (and this field) are being constructed at runtime (so there is no .class file to retrieve). - */ - BcelField(String declaringTypeName, Field field, World world) { - super(FIELD, UnresolvedType.forName(declaringTypeName), field.getModifiers(), field.getName(), field.getSignature()); - this.field = field; - this.world = world; - this.bcelObjectType = null; - unpackAttributes(world); - checkedExceptions = UnresolvedType.NONE; - } - - private void unpackAttributes(World world) { - Attribute[] attrs = field.getAttributes(); - if (attrs != null && attrs.length > 0) { - ISourceContext sourceContext = getSourceContext(world); - List as = Utility.readAjAttributes(getDeclaringType().getClassName(), attrs, sourceContext, world, - (bcelObjectType != null ? bcelObjectType.getWeaverVersionAttribute() : WeaverVersionInfo.CURRENT), - new BcelConstantPoolReader(field.getConstantPool())); - as.addAll(AtAjAttributes.readAj5FieldAttributes(field, this, world.resolve(getDeclaringType()), sourceContext, - world.getMessageHandler())); - - // FIXME this code has no effect!!!??? it is set to false immediately after the block - // for (AjAttribute a : as) { - // if (a instanceof AjAttribute.AjSynthetic) { - // isAjSynthetic = true; - // } else { - // throw new BCException("weird field attribute " + a); - // } - // } - } - isAjSynthetic = false; - - for (int i = attrs.length - 1; i >= 0; i--) { - if (attrs[i] instanceof Synthetic) { - isSynthetic = true; - } - } - // in 1.5, synthetic is a modifier, not an attribute - if ((field.getModifiers() & AccSynthetic) != 0) { - isSynthetic = true; - } - - } - - @Override - public boolean isAjSynthetic() { - return isAjSynthetic; - } - - @Override - public boolean isSynthetic() { - return isSynthetic; - } - - @Override - public boolean hasAnnotation(UnresolvedType ofType) { - ensureAnnotationTypesRetrieved(); - for (ResolvedType aType : annotationTypes) { - if (aType.equals(ofType)) { - return true; - } - } - return false; - } - - @Override - public ResolvedType[] getAnnotationTypes() { - ensureAnnotationTypesRetrieved(); - return annotationTypes; - } - - @Override - public AnnotationAJ[] getAnnotations() { - ensureAnnotationTypesRetrieved(); - return annotations; - } - - @Override - public AnnotationAJ getAnnotationOfType(UnresolvedType ofType) { - ensureAnnotationTypesRetrieved(); - for (AnnotationAJ annotation : annotations) { - if (annotation.getTypeName().equals(ofType.getName())) { - return annotation; - } - } - return null; - } - - private void ensureAnnotationTypesRetrieved() { - if (annotationTypes == null) { - AnnotationGen annos[] = field.getAnnotations(); - if (annos.length == 0) { - annotationTypes = ResolvedType.EMPTY_ARRAY; - annotations = AnnotationAJ.EMPTY_ARRAY; - } else { - int annosCount = annos.length; - annotationTypes = new ResolvedType[annosCount]; - annotations = new AnnotationAJ[annosCount]; - for (int i = 0; i < annosCount; i++) { - AnnotationGen anno = annos[i]; - annotations[i] = new BcelAnnotation(anno, world); - annotationTypes[i] = annotations[i].getType(); - } - } - } - } - - @Override - public void addAnnotation(AnnotationAJ annotation) { - ensureAnnotationTypesRetrieved(); - int len = annotations.length; - AnnotationAJ[] ret = new AnnotationAJ[len + 1]; - System.arraycopy(annotations, 0, ret, 0, len); - ret[len] = annotation; - annotations = ret; - - ResolvedType[] newAnnotationTypes = new ResolvedType[len + 1]; - System.arraycopy(annotationTypes, 0, newAnnotationTypes, 0, len); - newAnnotationTypes[len] = annotation.getType(); - annotationTypes = newAnnotationTypes; - - annotationsOnFieldObjectAreOutOfDate = true; - } - - public void removeAnnotation(AnnotationAJ annotation) { - ensureAnnotationTypesRetrieved(); - - int len = annotations.length; - AnnotationAJ[] ret = new AnnotationAJ[len - 1]; - int p = 0; - for (AnnotationAJ anno : annotations) { - if (!anno.getType().equals(annotation.getType())) { - ret[p++] = anno; - } - } - annotations = ret; - - ResolvedType[] newAnnotationTypes = new ResolvedType[len - 1]; - p = 0; - for (ResolvedType anno : annotationTypes) { - if (!anno.equals(annotation.getType())) { - newAnnotationTypes[p++] = anno; - } - } - annotationTypes = newAnnotationTypes; - - annotationsOnFieldObjectAreOutOfDate = true; - } - - /** - * Unpack the generic signature attribute if there is one and we haven't already done so, then find the true field type of this - * field (eg. List). - */ - @Override - public UnresolvedType getGenericReturnType() { - unpackGenericSignature(); - return genericFieldType; - } - - public Field getFieldAsIs() { - return field; - } - - public Field getField(ConstantPool cpool) { - if (!annotationsOnFieldObjectAreOutOfDate) { - return field; - } - FieldGen newFieldGen = new FieldGen(field, cpool); - newFieldGen.removeAnnotations(); - // List alreadyHas = fg.getAnnotations(); - // if (annotations != null) { - // fg.removeAnnotations(); - for (AnnotationAJ annotation : annotations) { - newFieldGen.addAnnotation(new AnnotationGen(((BcelAnnotation) annotation).getBcelAnnotation(), cpool, true)); - } - // for (int i = 0; i < annotations.length; i++) { - // AnnotationAJ array_element = annotations[i]; - // boolean alreadyHasIt = false; - // for (AnnotationGen gen : alreadyHas) { - // if (gen.getTypeName().equals(array_element.getTypeName())) { - // alreadyHasIt = true; - // break; - // } - // } - // if (!alreadyHasIt) { - // fg.addAnnotation(new AnnotationGen(((BcelAnnotation) array_element).getBcelAnnotation(), cpg, true)); - // // } - // // } - // } - field = newFieldGen.getField(); - annotationsOnFieldObjectAreOutOfDate = false; // we are now correct again - return field; - } - - private void unpackGenericSignature() { - if (unpackedGenericSignature) { - return; - } - if (!world.isInJava5Mode()) { - this.genericFieldType = getReturnType(); - return; - } - unpackedGenericSignature = true; - String gSig = field.getGenericSignature(); - if (gSig != null) { - // get from generic - GenericSignature.FieldTypeSignature fts = new GenericSignatureParser().parseAsFieldSignature(gSig); - GenericSignature.ClassSignature genericTypeSig = bcelObjectType.getGenericClassTypeSignature(); - - GenericSignature.FormalTypeParameter[] parentFormals = bcelObjectType.getAllFormals(); - GenericSignature.FormalTypeParameter[] typeVars = ((genericTypeSig == null) ? new GenericSignature.FormalTypeParameter[0] - : genericTypeSig.formalTypeParameters); - GenericSignature.FormalTypeParameter[] formals = new GenericSignature.FormalTypeParameter[parentFormals.length - + typeVars.length]; - // put method formal in front of type formals for overriding in - // lookup - System.arraycopy(typeVars, 0, formals, 0, typeVars.length); - System.arraycopy(parentFormals, 0, formals, typeVars.length, parentFormals.length); - - try { - genericFieldType = BcelGenericSignatureToTypeXConverter.fieldTypeSignature2TypeX(fts, formals, world); - } catch (GenericSignatureFormatException e) { - // development bug, fail fast with good info - throw new IllegalStateException("While determing the generic field type of " + this.toString() - + " with generic signature " + gSig + " the following error was detected: " + e.getMessage()); - } - } else { - genericFieldType = getReturnType(); - } - } - - @Override - public void evictWeavingState() { - if (field != null) { - unpackGenericSignature(); - unpackAttributes(world); - ensureAnnotationTypesRetrieved(); - // this.sourceContext = SourceContextImpl.UNKNOWN_SOURCE_CONTEXT; - field = null; - } - } -} \ No newline at end of file diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelFieldRef.java b/weaver/src/org/aspectj/weaver/bcel/BcelFieldRef.java deleted file mode 100644 index a5a2a79ec..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/BcelFieldRef.java +++ /dev/null @@ -1,100 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - -package org.aspectj.weaver.bcel; - -import org.aspectj.apache.bcel.Constants; -import org.aspectj.apache.bcel.generic.Instruction; -import org.aspectj.apache.bcel.generic.InstructionFactory; -import org.aspectj.apache.bcel.generic.InstructionList; -import org.aspectj.weaver.ResolvedType; - -/** - * XXX Erik and I need to discuss this hierarchy. Having FieldRef extend Var is convenient, but hopefully there's a better design. - * - * This is always a static reference. - */ -public class BcelFieldRef extends BcelVar { - - private String className, fieldName; - - public BcelFieldRef(ResolvedType type, String className, String fieldName) { - super(type, 0); - this.className = className; - this.fieldName = fieldName; - } - - public String toString() { - return "BcelFieldRef(" + getType() + " " + className + "." + fieldName + ")"; - } - - // public int getSlot() { return slot; } - - public Instruction createLoad(InstructionFactory fact) { - return fact.createFieldAccess(className, fieldName, BcelWorld.makeBcelType(getType()), Constants.GETSTATIC); - } - - public Instruction createStore(InstructionFactory fact) { - return fact.createFieldAccess(className, fieldName, BcelWorld.makeBcelType(getType()), Constants.PUTSTATIC); - } - - public InstructionList createCopyFrom(InstructionFactory fact, int oldSlot) { - throw new RuntimeException("unimplemented"); - } - - // this is an array var - // void appendConvertableArrayLoad( - // InstructionList il, - // InstructionFactory fact, - // int index, - // ResolvedType convertTo) - // { - // ResolvedType convertFromType = getType().getResolvedComponentType(); - // appendLoad(il, fact); - // il.append(Utility.createConstant(fact, index)); - // il.append(fact.createArrayLoad(BcelWorld.makeBcelType(convertFromType))); - // Utility.appendConversion(il, fact, convertFromType, convertTo); - // } - // - // void appendConvertableArrayStore( - // InstructionList il, - // InstructionFactory fact, - // int index, - // BcelFieldRef storee) - // { - // ResolvedType convertToType = getType().getResolvedComponentType(); - // appendLoad(il, fact); - // il.append(Utility.createConstant(fact, index)); - // storee.appendLoad(il, fact); - // Utility.appendConversion(il, fact, storee.getType(), convertToType); - // il.append(fact.createArrayStore(BcelWorld.makeBcelType(convertToType))); - // } - // - // InstructionList createConvertableArrayStore( - // InstructionFactory fact, - // int index, - // BcelFieldRef storee) - // { - // InstructionList il = new InstructionList(); - // appendConvertableArrayStore(il, fact, index, storee); - // return il; - // } - // InstructionList createConvertableArrayLoad( - // InstructionFactory fact, - // int index, - // ResolvedType convertTo) - // { - // InstructionList il = new InstructionList(); - // appendConvertableArrayLoad(il, fact, index, convertTo); - // return il; - // } -} diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelGenericSignatureToTypeXConverter.java b/weaver/src/org/aspectj/weaver/bcel/BcelGenericSignatureToTypeXConverter.java deleted file mode 100644 index 9b4c90cbc..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/BcelGenericSignatureToTypeXConverter.java +++ /dev/null @@ -1,278 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2005 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * Adrian Colyer Initial implementation - * ******************************************************************/ -package org.aspectj.weaver.bcel; - -import java.util.HashMap; -import java.util.Map; - -import org.aspectj.util.GenericSignature; -import org.aspectj.util.GenericSignature.SimpleClassTypeSignature; -import org.aspectj.weaver.BoundedReferenceType; -import org.aspectj.weaver.ReferenceType; -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.TypeFactory; -import org.aspectj.weaver.TypeVariable; -import org.aspectj.weaver.TypeVariableReferenceType; -import org.aspectj.weaver.UnresolvedType; -import org.aspectj.weaver.World; -import org.aspectj.weaver.tools.Trace; -import org.aspectj.weaver.tools.TraceFactory; - -/** - * A utility class that assists in unpacking constituent parts of generic signature attributes and returning their equivalents in - * UnresolvedType world. - */ -public class BcelGenericSignatureToTypeXConverter { - - private static Trace trace = TraceFactory.getTraceFactory().getTrace(BcelGenericSignatureToTypeXConverter.class); - - public static ResolvedType classTypeSignature2TypeX(GenericSignature.ClassTypeSignature aClassTypeSignature, - GenericSignature.FormalTypeParameter[] typeParams, World world) throws GenericSignatureFormatException { - Map typeMap = new HashMap(); - ResolvedType ret = classTypeSignature2TypeX(aClassTypeSignature, typeParams, world, typeMap); - fixUpCircularDependencies(ret, typeMap); - return ret; - } - - private static ResolvedType classTypeSignature2TypeX(GenericSignature.ClassTypeSignature aClassTypeSignature, - GenericSignature.FormalTypeParameter[] typeParams, World world, - Map inProgressTypeVariableResolutions) - throws GenericSignatureFormatException { - // class type sig consists of an outer type, and zero or more nested types - // the fully qualified name is outer-type.nested-type1.nested-type2.... - // each type in the hierarchy may have type arguments - - // first build the 'raw type' signature - StringBuffer sig = new StringBuffer(); - sig.append(aClassTypeSignature.outerType.identifier.replace(';', ' ').trim()); - for (int i = 0; i < aClassTypeSignature.nestedTypes.length; i++) { - sig.append("$"); - sig.append(aClassTypeSignature.nestedTypes[i].identifier.replace(';', ' ').trim()); - } - sig.append(";"); - - // now look for any type parameters. - // I *think* we only need to worry about the 'right-most' type... - SimpleClassTypeSignature innerType = aClassTypeSignature.outerType; - if (aClassTypeSignature.nestedTypes.length > 0) { - innerType = aClassTypeSignature.nestedTypes[aClassTypeSignature.nestedTypes.length - 1]; - } - if (innerType.typeArguments.length > 0) { - // we have to create a parameterized type - // type arguments may be array types, class types, or typevariable types - ResolvedType theBaseType = UnresolvedType.forSignature(sig.toString()).resolve(world); - - // Sometimes we may find that when the code is being load-time woven that the types have changed. - // Perhaps an old form of a library jar is being used - this can mean we discover right here - // that a type is not parameterizable (is that a word?). I think in these cases it is ok to - // just return with what we know (the base type). (see pr152848) - if (!(theBaseType.isGenericType() || theBaseType.isRawType())) { - if (trace.isTraceEnabled()) { - trace.event("classTypeSignature2TypeX: this type is not a generic type:", null, new Object[] { theBaseType }); - } - return theBaseType; - } - - ResolvedType[] typeArgumentTypes = new ResolvedType[innerType.typeArguments.length]; - for (int i = 0; i < typeArgumentTypes.length; i++) { - typeArgumentTypes[i] = typeArgument2TypeX(innerType.typeArguments[i], typeParams, world, - inProgressTypeVariableResolutions); - } - return TypeFactory.createParameterizedType(theBaseType, typeArgumentTypes, world); - - // world.resolve(UnresolvedType.forParameterizedTypes( - // UnresolvedType.forSignature(sig.toString()).resolve(world), - // typeArgumentTypes)); - } else { - // we have a non-parameterized type - return world.resolve(UnresolvedType.forSignature(sig.toString())); - } - } - - public static ResolvedType fieldTypeSignature2TypeX(GenericSignature.FieldTypeSignature aFieldTypeSignature, - GenericSignature.FormalTypeParameter[] typeParams, World world) throws GenericSignatureFormatException { - Map typeMap = new HashMap(); - ResolvedType ret = fieldTypeSignature2TypeX(aFieldTypeSignature, typeParams, world, typeMap); - fixUpCircularDependencies(ret, typeMap); - return ret; - } - - private static ResolvedType fieldTypeSignature2TypeX(GenericSignature.FieldTypeSignature aFieldTypeSignature, - GenericSignature.FormalTypeParameter[] typeParams, World world, - Map inProgressTypeVariableResolutions) - throws GenericSignatureFormatException { - if (aFieldTypeSignature.isClassTypeSignature()) { - return classTypeSignature2TypeX((GenericSignature.ClassTypeSignature) aFieldTypeSignature, typeParams, world, - inProgressTypeVariableResolutions); - } else if (aFieldTypeSignature.isArrayTypeSignature()) { - int dims = 0; - GenericSignature.TypeSignature ats = aFieldTypeSignature; - while (ats instanceof GenericSignature.ArrayTypeSignature) { - dims++; - ats = ((GenericSignature.ArrayTypeSignature) ats).typeSig; - } - return world.resolve(UnresolvedType.makeArray( - typeSignature2TypeX(ats, typeParams, world, inProgressTypeVariableResolutions), dims)); - } else if (aFieldTypeSignature.isTypeVariableSignature()) { - ResolvedType rtx = typeVariableSignature2TypeX((GenericSignature.TypeVariableSignature) aFieldTypeSignature, - typeParams, world, inProgressTypeVariableResolutions); - return rtx; - } else { - throw new GenericSignatureFormatException("Cant understand field type signature: " + aFieldTypeSignature); - } - } - - public static TypeVariable formalTypeParameter2TypeVariable(GenericSignature.FormalTypeParameter aFormalTypeParameter, - GenericSignature.FormalTypeParameter[] typeParams, World world) throws GenericSignatureFormatException { - Map typeMap = new HashMap(); - return formalTypeParameter2TypeVariable(aFormalTypeParameter, typeParams, world, typeMap); - } - - private static TypeVariable formalTypeParameter2TypeVariable(GenericSignature.FormalTypeParameter aFormalTypeParameter, - GenericSignature.FormalTypeParameter[] typeParams, World world, - Map inProgressTypeVariableResolutions) - throws GenericSignatureFormatException { - UnresolvedType upperBound = fieldTypeSignature2TypeX(aFormalTypeParameter.classBound, typeParams, world, - inProgressTypeVariableResolutions); - UnresolvedType[] ifBounds = new UnresolvedType[aFormalTypeParameter.interfaceBounds.length]; - for (int i = 0; i < ifBounds.length; i++) { - ifBounds[i] = fieldTypeSignature2TypeX(aFormalTypeParameter.interfaceBounds[i], typeParams, world, - inProgressTypeVariableResolutions); - } - return new TypeVariable(aFormalTypeParameter.identifier, upperBound, ifBounds); - } - - private static ResolvedType typeArgument2TypeX(GenericSignature.TypeArgument aTypeArgument, - GenericSignature.FormalTypeParameter[] typeParams, World world, - Map inProgressTypeVariableResolutions) - throws GenericSignatureFormatException { - if (aTypeArgument.isWildcard) { - return UnresolvedType.SOMETHING.resolve(world); - } - if (aTypeArgument.isMinus) { - UnresolvedType bound = fieldTypeSignature2TypeX(aTypeArgument.signature, typeParams, world, - inProgressTypeVariableResolutions); - ResolvedType resolvedBound = world.resolve(bound); - if (resolvedBound.isMissing()) { - world.getLint().cantFindType.signal("Unable to find type (for bound): " + resolvedBound.getName(), null); - resolvedBound = world.resolve(UnresolvedType.OBJECT); - } - ReferenceType rBound = (ReferenceType) resolvedBound; - return new BoundedReferenceType(rBound, false, world); - } else if (aTypeArgument.isPlus) { - UnresolvedType bound = fieldTypeSignature2TypeX(aTypeArgument.signature, typeParams, world, - inProgressTypeVariableResolutions); - ResolvedType resolvedBound = world.resolve(bound); - if (resolvedBound.isMissing()) { - world.getLint().cantFindType.signal("Unable to find type (for bound): " + resolvedBound.getName(), null); - resolvedBound = world.resolve(UnresolvedType.OBJECT); - } - ReferenceType rBound = (ReferenceType) resolvedBound; - return new BoundedReferenceType(rBound, true, world); - } else { - return fieldTypeSignature2TypeX(aTypeArgument.signature, typeParams, world, inProgressTypeVariableResolutions); - } - } - - public static ResolvedType typeSignature2TypeX(GenericSignature.TypeSignature aTypeSig, - GenericSignature.FormalTypeParameter[] typeParams, World world) throws GenericSignatureFormatException { - Map typeMap = new HashMap(); - ResolvedType ret = typeSignature2TypeX(aTypeSig, typeParams, world, typeMap); - fixUpCircularDependencies(ret, typeMap); - return ret; - } - - private static ResolvedType typeSignature2TypeX(GenericSignature.TypeSignature aTypeSig, - GenericSignature.FormalTypeParameter[] typeParams, World world, - Map inProgressTypeVariableResolutions) - throws GenericSignatureFormatException { - if (aTypeSig.isBaseType()) { - return world.resolve(UnresolvedType.forSignature(((GenericSignature.BaseTypeSignature) aTypeSig).toString())); - } else { - return fieldTypeSignature2TypeX((GenericSignature.FieldTypeSignature) aTypeSig, typeParams, world, - inProgressTypeVariableResolutions); - } - } - - private static ResolvedType typeVariableSignature2TypeX(GenericSignature.TypeVariableSignature aTypeVarSig, - GenericSignature.FormalTypeParameter[] typeParams, World world, - Map inProgressTypeVariableResolutions) - throws GenericSignatureFormatException { - GenericSignature.FormalTypeParameter typeVarBounds = null; - for (int i = 0; i < typeParams.length; i++) { - if (typeParams[i].identifier.equals(aTypeVarSig.typeVariableName)) { - typeVarBounds = typeParams[i]; - break; - } - } - if (typeVarBounds == null) { - // blowing up here breaks the situation with ITDs where the type variable is mentioned in the - // declaring type and used somewhere in the signature. Temporary change to allow it to return just a - // 'dumb' typevariablereference. - return new TypeVariableReferenceType(new TypeVariable(aTypeVarSig.typeVariableName), world); - // throw new GenericSignatureFormatException("Undeclared type variable in signature: " + aTypeVarSig.typeVariableName); - } - if (inProgressTypeVariableResolutions.containsKey(typeVarBounds)) { - return inProgressTypeVariableResolutions.get(typeVarBounds); - } - inProgressTypeVariableResolutions.put(typeVarBounds, new FTPHolder(typeVarBounds, world)); - ReferenceType ret = new TypeVariableReferenceType(formalTypeParameter2TypeVariable(typeVarBounds, typeParams, world, - inProgressTypeVariableResolutions), world); - inProgressTypeVariableResolutions.put(typeVarBounds, ret); - return ret; - } - - private static void fixUpCircularDependencies(ResolvedType aTypeX, - Map typeVariableResolutions) { - if (!(aTypeX instanceof ReferenceType)) { - return; - } - - ReferenceType rt = (ReferenceType) aTypeX; - TypeVariable[] typeVars = rt.getTypeVariables(); - if (typeVars != null) { - for (int i = 0; i < typeVars.length; i++) { - if (typeVars[i].getUpperBound() instanceof FTPHolder) { - GenericSignature.FormalTypeParameter key = ((FTPHolder) typeVars[i].getUpperBound()).ftpToBeSubstituted; - typeVars[i].setUpperBound(typeVariableResolutions.get(key)); - } - } - } - } - - private static class FTPHolder extends ReferenceType { - public GenericSignature.FormalTypeParameter ftpToBeSubstituted; - - public FTPHolder(GenericSignature.FormalTypeParameter ftp, World world) { - super("Ljava/lang/Object;", world); - this.ftpToBeSubstituted = ftp; - } - - public String toString() { - return "placeholder for TypeVariable of " + ftpToBeSubstituted.toString(); - } - - public ResolvedType resolve(World world) { - return this; - } - - public boolean isCacheable() { - return false; - } - } - - public static class GenericSignatureFormatException extends Exception { - public GenericSignatureFormatException(String explanation) { - super(explanation); - } - } -} diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelMethod.java b/weaver/src/org/aspectj/weaver/bcel/BcelMethod.java deleted file mode 100644 index d1e60e1c7..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/BcelMethod.java +++ /dev/null @@ -1,714 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - -package org.aspectj.weaver.bcel; - -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.List; -import java.util.StringTokenizer; - -import org.aspectj.apache.bcel.classfile.AnnotationDefault; -import org.aspectj.apache.bcel.classfile.Attribute; -import org.aspectj.apache.bcel.classfile.ExceptionTable; -import org.aspectj.apache.bcel.classfile.JavaClass; -import org.aspectj.apache.bcel.classfile.LineNumber; -import org.aspectj.apache.bcel.classfile.LineNumberTable; -import org.aspectj.apache.bcel.classfile.LocalVariable; -import org.aspectj.apache.bcel.classfile.LocalVariableTable; -import org.aspectj.apache.bcel.classfile.Method; -import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; -import org.aspectj.apache.bcel.classfile.annotation.NameValuePair; -import org.aspectj.bridge.ISourceLocation; -import org.aspectj.bridge.SourceLocation; -import org.aspectj.util.GenericSignature; -import org.aspectj.util.GenericSignature.TypeVariableSignature; -import org.aspectj.util.GenericSignatureParser; -import org.aspectj.weaver.AjAttribute; -import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; -import org.aspectj.weaver.AnnotationAJ; -import org.aspectj.weaver.BCException; -import org.aspectj.weaver.ISourceContext; -import org.aspectj.weaver.MemberKind; -import org.aspectj.weaver.ResolvedMemberImpl; -import org.aspectj.weaver.ResolvedPointcutDefinition; -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.ShadowMunger; -import org.aspectj.weaver.TypeVariable; -import org.aspectj.weaver.UnresolvedType; -import org.aspectj.weaver.World; -import org.aspectj.weaver.bcel.BcelGenericSignatureToTypeXConverter.GenericSignatureFormatException; - -//public final -class BcelMethod extends ResolvedMemberImpl { - - private final static String ASPECTJ_ANNOTATION_PACKAGE = "org.aspectj.lang.annotation"; - private final static char PACKAGE_INITIAL_CHAR = ASPECTJ_ANNOTATION_PACKAGE.charAt(0); - - private Method method; - - // these fields are not set for many BcelMethods... - private ShadowMunger associatedShadowMunger; - private ResolvedPointcutDefinition preResolvedPointcut; // used when ajc has pre-resolved the pointcut of some @Advice - private AjAttribute.EffectiveSignatureAttribute effectiveSignature; - - private AjAttribute.MethodDeclarationLineNumberAttribute declarationLineNumber; - private final BcelObjectType bcelObjectType; - - private int bitflags; - private static final int KNOW_IF_SYNTHETIC = 0x0001; - private static final int PARAMETER_NAMES_INITIALIZED = 0x0002; - private static final int CAN_BE_PARAMETERIZED = 0x0004; - private static final int UNPACKED_GENERIC_SIGNATURE = 0x0008; - private static final int IS_AJ_SYNTHETIC = 0x0040; - private static final int IS_SYNTHETIC = 0x0080; - private static final int IS_SYNTHETIC_INVERSE = 0x7f7f; // all bits but - // IS_SYNTHETIC (and - // topmost bit) - private static final int HAS_ANNOTATIONS = 0x0400; - private static final int HAVE_DETERMINED_ANNOTATIONS = 0x0800; - - // genericized version of return and parameter types - private UnresolvedType genericReturnType = null; - private UnresolvedType[] genericParameterTypes = null; - - BcelMethod(BcelObjectType declaringType, Method method) { - super(method.getName().equals("") ? CONSTRUCTOR : (method.getName().equals("") ? STATIC_INITIALIZATION - : METHOD), declaringType.getResolvedTypeX(), method.getModifiers(), method.getName(), method.getSignature()); - this.method = method; - sourceContext = declaringType.getResolvedTypeX().getSourceContext(); - bcelObjectType = declaringType; - unpackJavaAttributes(); - unpackAjAttributes(bcelObjectType.getWorld()); - } - - /** - * This constructor expects to be passed the attributes, rather than deserializing them. - */ - BcelMethod(BcelObjectType declaringType, Method method, List attributes) { - super(method.getName().equals("") ? CONSTRUCTOR : (method.getName().equals("") ? STATIC_INITIALIZATION - : METHOD), declaringType.getResolvedTypeX(), method.getModifiers(), method.getName(), method.getSignature()); - this.method = method; - sourceContext = declaringType.getResolvedTypeX().getSourceContext(); - bcelObjectType = declaringType; - unpackJavaAttributes(); - processAttributes(bcelObjectType.getWorld(), attributes); - } - - // ---- - - private void unpackJavaAttributes() { - ExceptionTable exnTable = method.getExceptionTable(); - checkedExceptions = (exnTable == null) ? UnresolvedType.NONE : UnresolvedType.forNames(exnTable.getExceptionNames()); - } - - @Override - public String[] getParameterNames() { - determineParameterNames(); - return super.getParameterNames(); - } - - public int getLineNumberOfFirstInstruction() { - LineNumberTable lnt = method.getLineNumberTable(); - if (lnt == null) { - return -1; - } - LineNumber[] lns = lnt.getLineNumberTable(); - if (lns == null || lns.length == 0) { - return -1; - } - return lns[0].getLineNumber(); - } - - public void determineParameterNames() { - if ((bitflags & PARAMETER_NAMES_INITIALIZED) != 0) { - return; - } - bitflags |= PARAMETER_NAMES_INITIALIZED; - LocalVariableTable varTable = method.getLocalVariableTable(); - int len = getArity(); - if (varTable == null) { - // do we have an annotation with the argNames value specified... - AnnotationAJ[] annos = getAnnotations(); - if (annos != null && annos.length != 0) { - AnnotationAJ[] axs = getAnnotations(); - for (int i = 0; i < axs.length; i++) { - AnnotationAJ annotationX = axs[i]; - String typename = annotationX.getTypeName(); - if (typename.charAt(0) == PACKAGE_INITIAL_CHAR) { - if (typename.equals("org.aspectj.lang.annotation.Pointcut") - || typename.equals("org.aspectj.lang.annotation.Before") - || typename.equals("org.aspectj.lang.annotation.Around") - || typename.startsWith("org.aspectj.lang.annotation.After")) { - AnnotationGen a = ((BcelAnnotation) annotationX).getBcelAnnotation(); - if (a != null) { - List values = a.getValues(); - for (NameValuePair nvPair : values) { - if (nvPair.getNameString().equals("argNames")) { - String argNames = nvPair.getValue().stringifyValue(); - StringTokenizer argNameTokenizer = new StringTokenizer(argNames, " ,"); - List argsList = new ArrayList(); - while (argNameTokenizer.hasMoreTokens()) { - argsList.add(argNameTokenizer.nextToken()); - } - int requiredCount = getParameterTypes().length; - while (argsList.size() < requiredCount) { - argsList.add("arg" + argsList.size()); - } - setParameterNames(argsList.toArray(new String[] {})); - return; - } - } - } - } - } - } - } - setParameterNames(Utility.makeArgNames(len)); - } else { - UnresolvedType[] paramTypes = getParameterTypes(); - String[] paramNames = new String[len]; - int index = Modifier.isStatic(modifiers) ? 0 : 1; - for (int i = 0; i < len; i++) { - LocalVariable lv = varTable.getLocalVariable(index); - if (lv == null) { - paramNames[i] = "arg" + i; - } else { - paramNames[i] = lv.getName(); - } - index += paramTypes[i].getSize(); - } - setParameterNames(paramNames); - } - } - - private void unpackAjAttributes(World world) { - associatedShadowMunger = null; - ResolvedType resolvedDeclaringType = getDeclaringType().resolve(world); - WeaverVersionInfo wvinfo = bcelObjectType.getWeaverVersionAttribute(); - List as = Utility.readAjAttributes(resolvedDeclaringType.getClassName(), method.getAttributes(), - resolvedDeclaringType.getSourceContext(), world, wvinfo, new BcelConstantPoolReader(method.getConstantPool())); - processAttributes(world, as); - as = AtAjAttributes.readAj5MethodAttributes(method, this, resolvedDeclaringType, preResolvedPointcut, - resolvedDeclaringType.getSourceContext(), world.getMessageHandler()); - processAttributes(world, as); - } - - private void processAttributes(World world, List as) { - for (AjAttribute attr : as) { - if (attr instanceof AjAttribute.MethodDeclarationLineNumberAttribute) { - declarationLineNumber = (AjAttribute.MethodDeclarationLineNumberAttribute) attr; - } else if (attr instanceof AjAttribute.AdviceAttribute) { - associatedShadowMunger = ((AjAttribute.AdviceAttribute) attr).reify(this, world, (ResolvedType) getDeclaringType()); - } else if (attr instanceof AjAttribute.AjSynthetic) { - bitflags |= IS_AJ_SYNTHETIC; - } else if (attr instanceof AjAttribute.EffectiveSignatureAttribute) { - effectiveSignature = (AjAttribute.EffectiveSignatureAttribute) attr; - } else if (attr instanceof AjAttribute.PointcutDeclarationAttribute) { - // this is an @AspectJ annotated advice method, with pointcut pre-resolved by ajc - preResolvedPointcut = ((AjAttribute.PointcutDeclarationAttribute) attr).reify(); - } else { - throw new BCException("weird method attribute " + attr); - } - } - } - - // - // // for testing - if we have this attribute, return it - will return null - // if - // // it doesnt know anything - // public AjAttribute[] getAttributes(String name) { - // List results = new ArrayList(); - // List l = Utility.readAjAttributes(getDeclaringType().getClassName(), - // method.getAttributes(), - // getSourceContext(bcelObjectType.getWorld()), bcelObjectType.getWorld(), - // bcelObjectType.getWeaverVersionAttribute()); - // for (Iterator iter = l.iterator(); iter.hasNext();) { - // AjAttribute element = (AjAttribute) iter.next(); - // if (element.getNameString().equals(name)) - // results.add(element); - // } - // if (results.size() > 0) { - // return (AjAttribute[]) results.toArray(new AjAttribute[] {}); - // } - // return null; - // } - - @Override - public String getAnnotationDefaultValue() { - Attribute[] attrs = method.getAttributes(); - for (int i = 0; i < attrs.length; i++) { - Attribute attribute = attrs[i]; - if (attribute.getName().equals("AnnotationDefault")) { - AnnotationDefault def = (AnnotationDefault) attribute; - return def.getElementValue().stringifyValue(); - } - } - return null; - } - - // for testing - use with the method above - public String[] getAttributeNames(boolean onlyIncludeAjOnes) { - Attribute[] as = method.getAttributes(); - List names = new ArrayList(); - // String[] strs = new String[as.length]; - for (int j = 0; j < as.length; j++) { - if (!onlyIncludeAjOnes || as[j].getName().startsWith(AjAttribute.AttributePrefix)) { - names.add(as[j].getName()); - } - } - return names.toArray(new String[] {}); - } - - @Override - public boolean isAjSynthetic() { - return (bitflags & IS_AJ_SYNTHETIC) != 0; - } - - @Override - public ShadowMunger getAssociatedShadowMunger() { - return associatedShadowMunger; - } - - @Override - public AjAttribute.EffectiveSignatureAttribute getEffectiveSignature() { - return effectiveSignature; - } - - public boolean hasDeclarationLineNumberInfo() { - return declarationLineNumber != null; - } - - public int getDeclarationLineNumber() { - if (declarationLineNumber != null) { - return declarationLineNumber.getLineNumber(); - } else { - return -1; - } - } - - public int getDeclarationOffset() { - if (declarationLineNumber != null) { - return declarationLineNumber.getOffset(); - } else { - return -1; - } - } - - @Override - public ISourceLocation getSourceLocation() { - ISourceLocation ret = super.getSourceLocation(); - if ((ret == null || ret.getLine() == 0) && hasDeclarationLineNumberInfo()) { - // lets see if we can do better - ISourceContext isc = getSourceContext(); - if (isc != null) { - ret = isc.makeSourceLocation(getDeclarationLineNumber(), getDeclarationOffset()); - } else { - ret = new SourceLocation(null, getDeclarationLineNumber()); - } - } - return ret; - } - - @Override - public MemberKind getKind() { - if (associatedShadowMunger != null) { - return ADVICE; - } else { - return super.getKind(); - } - } - - @Override - public boolean hasAnnotation(UnresolvedType ofType) { - ensureAnnotationsRetrieved(); - for (ResolvedType aType : annotationTypes) { - if (aType.equals(ofType)) { - return true; - } - } - return false; - } - - @Override - public AnnotationAJ[] getAnnotations() { - ensureAnnotationsRetrieved(); - if ((bitflags & HAS_ANNOTATIONS) != 0) { - return annotations; - } else { - return AnnotationAJ.EMPTY_ARRAY; - } - } - - @Override - public ResolvedType[] getAnnotationTypes() { - ensureAnnotationsRetrieved(); - return annotationTypes; - } - - @Override - public AnnotationAJ getAnnotationOfType(UnresolvedType ofType) { - ensureAnnotationsRetrieved(); - if ((bitflags & HAS_ANNOTATIONS) == 0) { - return null; - } - for (int i = 0; i < annotations.length; i++) { - if (annotations[i].getTypeName().equals(ofType.getName())) { - return annotations[i]; - } - } - return null; - } - - @Override - public void addAnnotation(AnnotationAJ annotation) { - ensureAnnotationsRetrieved(); - if ((bitflags & HAS_ANNOTATIONS) == 0) { - annotations = new AnnotationAJ[1]; - annotations[0] = annotation; - annotationTypes = new ResolvedType[1]; - annotationTypes[0] = annotation.getType(); - } else { - // Add it to the set of annotations - int len = annotations.length; - AnnotationAJ[] ret = new AnnotationAJ[len + 1]; - System.arraycopy(annotations, 0, ret, 0, len); - ret[len] = annotation; - annotations = ret; - ResolvedType[] newAnnotationTypes = new ResolvedType[len + 1]; - System.arraycopy(annotationTypes, 0, newAnnotationTypes, 0, len); - newAnnotationTypes[len] = annotation.getType(); - annotationTypes = newAnnotationTypes; - } - bitflags |= HAS_ANNOTATIONS; - } - - public void removeAnnotation(ResolvedType annotationType) { - ensureAnnotationsRetrieved(); - if ((bitflags & HAS_ANNOTATIONS) == 0) { - // nothing to do, why did we get called? - } else { - int len = annotations.length; - if (len == 1) { - bitflags &= ~HAS_ANNOTATIONS; - annotations = null; - annotationTypes = null; - return; - } - AnnotationAJ[] ret = new AnnotationAJ[len - 1]; - int p = 0; - for (AnnotationAJ annotation : annotations) { - if (!annotation.getType().equals(annotationType)) { - ret[p++] = annotation; - } - } - annotations = ret; - - ResolvedType[] newAnnotationTypes = new ResolvedType[len - 1]; - p = 0; - for (AnnotationAJ annotation : annotations) { - if (!annotation.getType().equals(annotationType)) { - newAnnotationTypes[p++] = annotationType; - } - } - annotationTypes = newAnnotationTypes; - } - bitflags |= HAS_ANNOTATIONS; - } - - public static final AnnotationAJ[] NO_PARAMETER_ANNOTATIONS = new AnnotationAJ[] {}; - - public void addParameterAnnotation(int param, AnnotationAJ anno) { - ensureParameterAnnotationsRetrieved(); - if (parameterAnnotations == NO_PARAMETER_ANNOTATIONXS) { - // First time we've added any, so lets set up the array - parameterAnnotations = new AnnotationAJ[getArity()][]; - for (int i = 0; i < getArity(); i++) { - parameterAnnotations[i] = NO_PARAMETER_ANNOTATIONS; - } - } - int existingCount = parameterAnnotations[param].length; - if (existingCount == 0) { - AnnotationAJ[] annoArray = new AnnotationAJ[1]; - annoArray[0] = anno; - parameterAnnotations[param] = annoArray; - } else { - AnnotationAJ[] newAnnoArray = new AnnotationAJ[existingCount + 1]; - System.arraycopy(parameterAnnotations[param], 0, newAnnoArray, 0, existingCount); - newAnnoArray[existingCount] = anno; - parameterAnnotations[param] = newAnnoArray; - } - } - - private void ensureAnnotationsRetrieved() { - if (method == null) { - return; // must be ok, we have evicted it - } - if ((bitflags & HAVE_DETERMINED_ANNOTATIONS) != 0) { - return; - } - bitflags |= HAVE_DETERMINED_ANNOTATIONS; - AnnotationGen annos[] = method.getAnnotations(); - if (annos.length == 0) { - annotationTypes = ResolvedType.NONE; - annotations = AnnotationAJ.EMPTY_ARRAY; - } else { - int annoCount = annos.length; - annotationTypes = new ResolvedType[annoCount]; - annotations = new AnnotationAJ[annoCount]; - for (int i = 0; i < annoCount; i++) { - AnnotationGen annotation = annos[i]; - annotations[i] = new BcelAnnotation(annotation, bcelObjectType.getWorld()); - annotationTypes[i] = annotations[i].getType(); - } - bitflags |= HAS_ANNOTATIONS; - } - } - - private void ensureParameterAnnotationsRetrieved() { - if (method == null) { - return; // must be ok, we have evicted it - } - AnnotationGen[][] pAnns = method.getParameterAnnotations(); - if (parameterAnnotationTypes == null || pAnns.length != parameterAnnotationTypes.length) { - if (pAnns == Method.NO_PARAMETER_ANNOTATIONS) { - parameterAnnotationTypes = BcelMethod.NO_PARAMETER_ANNOTATION_TYPES; - parameterAnnotations = BcelMethod.NO_PARAMETER_ANNOTATIONXS; - } else { - AnnotationGen annos[][] = method.getParameterAnnotations(); - parameterAnnotations = new AnnotationAJ[annos.length][]; - parameterAnnotationTypes = new ResolvedType[annos.length][]; - for (int i = 0; i < annos.length; i++) { - AnnotationGen[] annosOnThisParam = annos[i]; - if (annos[i].length == 0) { - parameterAnnotations[i] = AnnotationAJ.EMPTY_ARRAY; - parameterAnnotationTypes[i] = ResolvedType.NONE; - } else { - parameterAnnotations[i] = new AnnotationAJ[annosOnThisParam.length]; - parameterAnnotationTypes[i] = new ResolvedType[annosOnThisParam.length]; - for (int j = 0; j < annosOnThisParam.length; j++) { - parameterAnnotations[i][j] = new BcelAnnotation(annosOnThisParam[j], bcelObjectType.getWorld()); - parameterAnnotationTypes[i][j] = bcelObjectType.getWorld().resolve( - UnresolvedType.forSignature(annosOnThisParam[j].getTypeSignature())); - } - } - } - } - } - } - - @Override - public AnnotationAJ[][] getParameterAnnotations() { - ensureParameterAnnotationsRetrieved(); - return parameterAnnotations; - } - - @Override - public ResolvedType[][] getParameterAnnotationTypes() { - ensureParameterAnnotationsRetrieved(); - return parameterAnnotationTypes; - } - - /** - * A method can be parameterized if it has one or more generic parameters. A generic parameter (type variable parameter) is - * identified by the prefix "T" - */ - @Override - public boolean canBeParameterized() { - unpackGenericSignature(); - return (bitflags & CAN_BE_PARAMETERIZED) != 0; - } - - @Override - public UnresolvedType[] getGenericParameterTypes() { - unpackGenericSignature(); - return genericParameterTypes; - } - - /** - * Return the parameterized/generic return type or the normal return type if the method is not generic. - */ - @Override - public UnresolvedType getGenericReturnType() { - unpackGenericSignature(); - return genericReturnType; - } - - /** For testing only */ - public Method getMethod() { - return method; - } - - private void unpackGenericSignature() { - if ((bitflags & UNPACKED_GENERIC_SIGNATURE) != 0) { - return; - } - bitflags |= UNPACKED_GENERIC_SIGNATURE; - if (!bcelObjectType.getWorld().isInJava5Mode()) { - genericReturnType = getReturnType(); - genericParameterTypes = getParameterTypes(); - return; - } - String gSig = method.getGenericSignature(); - if (gSig != null) { - GenericSignature.MethodTypeSignature mSig = new GenericSignatureParser().parseAsMethodSignature(gSig);// method - // . - // getGenericSignature - // ()); - if (mSig.formalTypeParameters.length > 0) { - // generic method declaration - bitflags |= CAN_BE_PARAMETERIZED; - } - - typeVariables = new TypeVariable[mSig.formalTypeParameters.length]; - for (int i = 0; i < typeVariables.length; i++) { - GenericSignature.FormalTypeParameter methodFtp = mSig.formalTypeParameters[i]; - try { - typeVariables[i] = BcelGenericSignatureToTypeXConverter.formalTypeParameter2TypeVariable(methodFtp, - mSig.formalTypeParameters, bcelObjectType.getWorld()); - } catch (GenericSignatureFormatException e) { - // this is a development bug, so fail fast with good info - throw new IllegalStateException("While getting the type variables for method " + this.toString() - + " with generic signature " + mSig + " the following error condition was detected: " + e.getMessage()); - } - } - - GenericSignature.FormalTypeParameter[] parentFormals = bcelObjectType.getAllFormals(); - GenericSignature.FormalTypeParameter[] formals = new GenericSignature.FormalTypeParameter[parentFormals.length - + mSig.formalTypeParameters.length]; - // put method formal in front of type formals for overriding in - // lookup - System.arraycopy(mSig.formalTypeParameters, 0, formals, 0, mSig.formalTypeParameters.length); - System.arraycopy(parentFormals, 0, formals, mSig.formalTypeParameters.length, parentFormals.length); - GenericSignature.TypeSignature returnTypeSignature = mSig.returnType; - try { - genericReturnType = BcelGenericSignatureToTypeXConverter.typeSignature2TypeX(returnTypeSignature, formals, - bcelObjectType.getWorld()); - } catch (GenericSignatureFormatException e) { - // development bug, fail fast with good info - throw new IllegalStateException("While determing the generic return type of " + this.toString() - + " with generic signature " + gSig + " the following error was detected: " + e.getMessage()); - } - GenericSignature.TypeSignature[] paramTypeSigs = mSig.parameters; - if (paramTypeSigs.length == 0) { - genericParameterTypes = UnresolvedType.NONE; - } else { - genericParameterTypes = new UnresolvedType[paramTypeSigs.length]; - } - for (int i = 0; i < paramTypeSigs.length; i++) { - try { - genericParameterTypes[i] = BcelGenericSignatureToTypeXConverter.typeSignature2TypeX(paramTypeSigs[i], formals, - bcelObjectType.getWorld()); - } catch (GenericSignatureFormatException e) { - // development bug, fail fast with good info - throw new IllegalStateException("While determining the generic parameter types of " + this.toString() - + " with generic signature " + gSig + " the following error was detected: " + e.getMessage()); - } - if (paramTypeSigs[i] instanceof TypeVariableSignature) { - bitflags |= CAN_BE_PARAMETERIZED; - } - } - } else { - genericReturnType = getReturnType(); - genericParameterTypes = getParameterTypes(); - } - } - - @Override - public void evictWeavingState() { - if (method != null) { - unpackGenericSignature(); - unpackJavaAttributes(); - ensureAnnotationsRetrieved(); - ensureParameterAnnotationsRetrieved(); - determineParameterNames(); - // this.sourceContext = SourceContextImpl.UNKNOWN_SOURCE_CONTEXT; - method = null; - } - } - - @Override - public boolean isSynthetic() { - if ((bitflags & KNOW_IF_SYNTHETIC) == 0) { - workOutIfSynthetic(); - } - return (bitflags & IS_SYNTHETIC) != 0;// isSynthetic; - } - - // Pre Java5 synthetic is an attribute 'Synthetic', post Java5 it is a - // modifier (4096 or 0x1000) - private void workOutIfSynthetic() { - if ((bitflags & KNOW_IF_SYNTHETIC) != 0) { - return; - } - bitflags |= KNOW_IF_SYNTHETIC; - JavaClass jc = bcelObjectType.getJavaClass(); - bitflags &= IS_SYNTHETIC_INVERSE; // unset the bit - if (jc == null) { - return; // what the hell has gone wrong? - } - if (jc.getMajor() < 49/* Java5 */) { - // synthetic is an attribute - String[] synthetics = getAttributeNames(false); - if (synthetics != null) { - for (int i = 0; i < synthetics.length; i++) { - if (synthetics[i].equals("Synthetic")) { - bitflags |= IS_SYNTHETIC; - break; - } - } - } - } else { - // synthetic is a modifier (4096) - if ((modifiers & 4096) != 0) { - bitflags |= IS_SYNTHETIC; - } - } - } - - /** - * Returns whether or not the given object is equivalent to the current one. Returns true if - * getMethod().getCode().getCodeString() are equal. Allows for different line number tables. - */ - // bug 154054: is similar to equals(Object) however - // doesn't require implementing equals in Method and Code - // which proved expensive. Currently used within - // CrosscuttingMembers.replaceWith() to decide if we need - // to do a full build - @Override - public boolean isEquivalentTo(Object other) { - if (!(other instanceof BcelMethod)) { - return false; - } - BcelMethod o = (BcelMethod) other; - return getMethod().getCode().getCodeString().equals(o.getMethod().getCode().getCodeString()); - } - - /** - * Return true if the method represents the default constructor. Hard to determine this from bytecode, but the existence of the - * MethodDeclarationLineNumber attribute should tell us. - * - * @return true if this BcelMethod represents the default constructor - */ - @Override - public boolean isDefaultConstructor() { - boolean mightBe = !hasDeclarationLineNumberInfo() && name.equals("") && parameterTypes.length == 0; - if (mightBe) { - // TODO would be nice to do a check to see if the file was compiled with javac or ajc? - // maybe by checking the constant pool for aspectj strings? - return true; - } else { - return false; - } - } - -} \ No newline at end of file diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelObjectType.java b/weaver/src/org/aspectj/weaver/bcel/BcelObjectType.java deleted file mode 100644 index 710eb6dc7..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/BcelObjectType.java +++ /dev/null @@ -1,1023 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Contributors - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * RonBodkin/AndyClement optimizations for memory consumption/speed - * ******************************************************************/ - -package org.aspectj.weaver.bcel; - -import java.io.PrintStream; -import java.lang.ref.WeakReference; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Set; - -import org.aspectj.apache.bcel.classfile.Attribute; -import org.aspectj.apache.bcel.classfile.AttributeUtils; -import org.aspectj.apache.bcel.classfile.ConstantClass; -import org.aspectj.apache.bcel.classfile.ConstantPool; -import org.aspectj.apache.bcel.classfile.EnclosingMethod; -import org.aspectj.apache.bcel.classfile.Field; -import org.aspectj.apache.bcel.classfile.InnerClass; -import org.aspectj.apache.bcel.classfile.InnerClasses; -import org.aspectj.apache.bcel.classfile.JavaClass; -import org.aspectj.apache.bcel.classfile.Method; -import org.aspectj.apache.bcel.classfile.Signature; -import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; -import org.aspectj.apache.bcel.classfile.annotation.EnumElementValue; -import org.aspectj.apache.bcel.classfile.annotation.NameValuePair; -import org.aspectj.asm.AsmManager; -import org.aspectj.bridge.IMessageHandler; -import org.aspectj.bridge.MessageUtil; -import org.aspectj.util.GenericSignature; -import org.aspectj.util.GenericSignature.FormalTypeParameter; -import org.aspectj.weaver.AbstractReferenceTypeDelegate; -import org.aspectj.weaver.AjAttribute; -import org.aspectj.weaver.AjcMemberMaker; -import org.aspectj.weaver.AnnotationAJ; -import org.aspectj.weaver.AnnotationTargetKind; -import org.aspectj.weaver.BCException; -import org.aspectj.weaver.BindingScope; -import org.aspectj.weaver.ConcreteTypeMunger; -import org.aspectj.weaver.ISourceContext; -import org.aspectj.weaver.ReferenceType; -import org.aspectj.weaver.ResolvedMember; -import org.aspectj.weaver.ResolvedPointcutDefinition; -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.SourceContextImpl; -import org.aspectj.weaver.TypeVariable; -import org.aspectj.weaver.UnresolvedType; -import org.aspectj.weaver.WeaverStateInfo; -import org.aspectj.weaver.World; -import org.aspectj.weaver.bcel.BcelGenericSignatureToTypeXConverter.GenericSignatureFormatException; -import org.aspectj.weaver.patterns.Declare; -import org.aspectj.weaver.patterns.DeclareErrorOrWarning; -import org.aspectj.weaver.patterns.DeclarePrecedence; -import org.aspectj.weaver.patterns.FormalBinding; -import org.aspectj.weaver.patterns.IScope; -import org.aspectj.weaver.patterns.PerClause; - -public class BcelObjectType extends AbstractReferenceTypeDelegate { - public JavaClass javaClass; - private boolean artificial; // Was the BcelObject built from an artificial set of bytes? Or from the real ondisk stuff? - private LazyClassGen lazyClassGen = null; // set lazily if it's an aspect - - private int modifiers; - private String className; - - private String superclassSignature; - private String superclassName; - private String[] interfaceSignatures; - - private ResolvedMember[] fields = null; - private ResolvedMember[] methods = null; - private ResolvedType[] annotationTypes = null; - private AnnotationAJ[] annotations = null; - private TypeVariable[] typeVars = null; - private String retentionPolicy; - private AnnotationTargetKind[] annotationTargetKinds; - - // Aspect related stuff (pointcuts *could* be in a java class) - private AjAttribute.WeaverVersionInfo wvInfo = AjAttribute.WeaverVersionInfo.UNKNOWN; - private ResolvedPointcutDefinition[] pointcuts = null; - private ResolvedMember[] privilegedAccess = null; - private WeaverStateInfo weaverState = null; - private PerClause perClause = null; - private List typeMungers = Collections.emptyList(); - private List declares = Collections.emptyList(); - - private GenericSignature.FormalTypeParameter[] formalsForResolution = null; - private String declaredSignature = null; - - private boolean hasBeenWoven = false; - private boolean isGenericType = false; - private boolean isInterface; - private boolean isEnum; - private boolean isAnnotation; - private boolean isAnonymous; - private boolean isNested; - private boolean isObject = false; // set upon construction - private boolean isAnnotationStyleAspect = false;// set upon construction - private boolean isCodeStyleAspect = false; // not redundant with field - // above! - - private WeakReference superTypeReference = new WeakReference(null); - private WeakReference superInterfaceReferences = new WeakReference(null); - - private int bitflag = 0x0000; - - // discovery bits - private static final int DISCOVERED_ANNOTATION_RETENTION_POLICY = 0x0001; - private static final int UNPACKED_GENERIC_SIGNATURE = 0x0002; - private static final int UNPACKED_AJATTRIBUTES = 0x0004; // see note(1) - // below - private static final int DISCOVERED_ANNOTATION_TARGET_KINDS = 0x0008; - private static final int DISCOVERED_DECLARED_SIGNATURE = 0x0010; - private static final int DISCOVERED_WHETHER_ANNOTATION_STYLE = 0x0020; - - private static final int ANNOTATION_UNPACK_IN_PROGRESS = 0x0100; - - private static final String[] NO_INTERFACE_SIGS = new String[] {}; - - /* - * Notes: note(1): in some cases (perclause inheritance) we encounter unpacked state when calling getPerClause - * - * note(2): A BcelObjectType is 'damaged' if it has been modified from what was original constructed from the bytecode. This - * currently happens if the parents are modified or an annotation is added - ideally BcelObjectType should be immutable but - * that's a bigger piece of work. XXX - */ - - BcelObjectType(ReferenceType resolvedTypeX, JavaClass javaClass, boolean artificial, boolean exposedToWeaver) { - super(resolvedTypeX, exposedToWeaver); - this.javaClass = javaClass; - this.artificial = artificial; - initializeFromJavaclass(); - - // ATAJ: set the delegate right now for @AJ pointcut, else it is done - // too late to lookup - // @AJ pc refs annotation in class hierarchy - resolvedTypeX.setDelegate(this); - - ISourceContext sourceContext = resolvedTypeX.getSourceContext(); - if (sourceContext == SourceContextImpl.UNKNOWN_SOURCE_CONTEXT) { - sourceContext = new SourceContextImpl(this); - setSourceContext(sourceContext); - } - - // this should only ever be java.lang.Object which is - // the only class in Java-1.4 with no superclasses - isObject = (javaClass.getSuperclassNameIndex() == 0); - ensureAspectJAttributesUnpacked(); - // if (sourceContext instanceof SourceContextImpl) { - // ((SourceContextImpl)sourceContext).setSourceFileName(javaClass. - // getSourceFileName()); - // } - setSourcefilename(javaClass.getSourceFileName()); - } - - // repeat initialization - public void setJavaClass(JavaClass newclass, boolean artificial) { - this.javaClass = newclass; - this.artificial = artificial; - resetState(); - initializeFromJavaclass(); - } - - @Override - public boolean isCacheable() { - return true; - } - - private void initializeFromJavaclass() { - isInterface = javaClass.isInterface(); - isEnum = javaClass.isEnum(); - isAnnotation = javaClass.isAnnotation(); - isAnonymous = javaClass.isAnonymous(); - isNested = javaClass.isNested(); - modifiers = javaClass.getModifiers(); - superclassName = javaClass.getSuperclassName(); - className = javaClass.getClassName(); - cachedGenericClassTypeSignature = null; - } - - // --- getters - - // Java related - public boolean isInterface() { - return isInterface; - } - - public boolean isEnum() { - return isEnum; - } - - public boolean isAnnotation() { - return isAnnotation; - } - - public boolean isAnonymous() { - return isAnonymous; - } - - public boolean isNested() { - return isNested; - } - - public int getModifiers() { - return modifiers; - } - - /** - * Must take into account generic signature - */ - public ResolvedType getSuperclass() { - if (isObject) { - return null; - } - ResolvedType supertype = superTypeReference.get(); - if (supertype == null) { - ensureGenericSignatureUnpacked(); - if (superclassSignature == null) { - if (superclassName == null) { - superclassName = javaClass.getSuperclassName(); - } - superclassSignature = getResolvedTypeX().getWorld().resolve(UnresolvedType.forName(superclassName)).getSignature(); - } - World world = getResolvedTypeX().getWorld(); - supertype = world.resolve(UnresolvedType.forSignature(superclassSignature)); - superTypeReference = new WeakReference(supertype); - } - return supertype; - } - - public World getWorld() { - return getResolvedTypeX().getWorld(); - } - - /** - * Retrieves the declared interfaces - this allows for the generic signature on a type. If specified then the generic signature - * is used to work out the types - this gets around the results of erasure when the class was originally compiled. - */ - public ResolvedType[] getDeclaredInterfaces() { - - ResolvedType[] cachedInterfaceTypes = superInterfaceReferences.get(); - if (cachedInterfaceTypes == null) { - ensureGenericSignatureUnpacked(); - ResolvedType[] interfaceTypes = null; - if (interfaceSignatures == null) { - String[] names = javaClass.getInterfaceNames(); - if (names.length == 0) { - interfaceSignatures = NO_INTERFACE_SIGS; - interfaceTypes = ResolvedType.NONE; - } else { - interfaceSignatures = new String[names.length]; - interfaceTypes = new ResolvedType[names.length]; - for (int i = 0, len = names.length; i < len; i++) { - interfaceTypes[i] = getResolvedTypeX().getWorld().resolve(UnresolvedType.forName(names[i])); - interfaceSignatures[i] = interfaceTypes[i].getSignature(); - } - } - } else { - interfaceTypes = new ResolvedType[interfaceSignatures.length]; - for (int i = 0, len = interfaceSignatures.length; i < len; i++) { - interfaceTypes[i] = getResolvedTypeX().getWorld().resolve(UnresolvedType.forSignature(interfaceSignatures[i])); - } - } - superInterfaceReferences = new WeakReference(interfaceTypes); - return interfaceTypes; - } else { - return cachedInterfaceTypes; - } - } - - public ResolvedMember[] getDeclaredMethods() { - ensureGenericSignatureUnpacked(); - if (methods == null) { - Method[] ms = javaClass.getMethods(); - ResolvedMember[] newMethods = new ResolvedMember[ms.length]; - for (int i = ms.length - 1; i >= 0; i--) { - newMethods[i] = new BcelMethod(this, ms[i]); - } - methods = newMethods; - } - return methods; - } - - public ResolvedMember[] getDeclaredFields() { - ensureGenericSignatureUnpacked(); - if (fields == null) { - Field[] fs = javaClass.getFields(); - ResolvedMember[] newfields = new ResolvedMember[fs.length]; - for (int i = 0, len = fs.length; i < len; i++) { - newfields[i] = new BcelField(this, fs[i]); - } - fields = newfields; - } - return fields; - } - - public TypeVariable[] getTypeVariables() { - if (!isGeneric()) { - return TypeVariable.NONE; - } - - if (typeVars == null) { - GenericSignature.ClassSignature classSig = getGenericClassTypeSignature(); - typeVars = new TypeVariable[classSig.formalTypeParameters.length]; - for (int i = 0; i < typeVars.length; i++) { - GenericSignature.FormalTypeParameter ftp = classSig.formalTypeParameters[i]; - try { - typeVars[i] = BcelGenericSignatureToTypeXConverter.formalTypeParameter2TypeVariable(ftp, - classSig.formalTypeParameters, getResolvedTypeX().getWorld()); - } catch (GenericSignatureFormatException e) { - // this is a development bug, so fail fast with good info - throw new IllegalStateException("While getting the type variables for type " + this.toString() - + " with generic signature " + classSig + " the following error condition was detected: " - + e.getMessage()); - } - } - } - return typeVars; - } - - public Collection getTypeMungers() { - return typeMungers; - } - - public Collection getDeclares() { - return declares; - } - - public Collection getPrivilegedAccesses() { - if (privilegedAccess == null) { - return Collections.emptyList(); - } - return Arrays.asList(privilegedAccess); - } - - public ResolvedMember[] getDeclaredPointcuts() { - return pointcuts; - } - - public boolean isAspect() { - return perClause != null; - } - - /** - * Check if the type is an @AJ aspect (no matter if used from an LTW point of view). Such aspects are annotated with @Aspect - * - * @return true for @AJ aspect - */ - public boolean isAnnotationStyleAspect() { - if ((bitflag & DISCOVERED_WHETHER_ANNOTATION_STYLE) == 0) { - bitflag |= DISCOVERED_WHETHER_ANNOTATION_STYLE; - isAnnotationStyleAspect = !isCodeStyleAspect && hasAnnotation(AjcMemberMaker.ASPECT_ANNOTATION); - } - return isAnnotationStyleAspect; - } - - /** - * Process any org.aspectj.weaver attributes stored against the class. - */ - private void ensureAspectJAttributesUnpacked() { - if ((bitflag & UNPACKED_AJATTRIBUTES) != 0) { - return; - } - bitflag |= UNPACKED_AJATTRIBUTES; - IMessageHandler msgHandler = getResolvedTypeX().getWorld().getMessageHandler(); - // Pass in empty list that can store things for readAj5 to process - List l = null; - try { - l = Utility.readAjAttributes(className, javaClass.getAttributes(), getResolvedTypeX().getSourceContext(), - getResolvedTypeX().getWorld(), AjAttribute.WeaverVersionInfo.UNKNOWN, - new BcelConstantPoolReader(javaClass.getConstantPool())); - } catch (RuntimeException re) { - throw new RuntimeException("Problem processing attributes in " + javaClass.getFileName(), re); - } - List pointcuts = new ArrayList(); - typeMungers = new ArrayList(); - declares = new ArrayList(); - processAttributes(l, pointcuts, false); - ReferenceType type = getResolvedTypeX(); - AsmManager asmManager = ((BcelWorld) type.getWorld()).getModelAsAsmManager(); - l = AtAjAttributes.readAj5ClassAttributes(asmManager, javaClass, type, type.getSourceContext(), msgHandler, - isCodeStyleAspect); - AjAttribute.Aspect deferredAspectAttribute = processAttributes(l, pointcuts, true); - - if (pointcuts.size() == 0) { - this.pointcuts = ResolvedPointcutDefinition.NO_POINTCUTS; - } else { - this.pointcuts = pointcuts.toArray(new ResolvedPointcutDefinition[pointcuts.size()]); - } - - resolveAnnotationDeclares(l); - - if (deferredAspectAttribute != null) { - // we can finally process the aspect and its associated perclause... - perClause = deferredAspectAttribute.reifyFromAtAspectJ(this.getResolvedTypeX()); - } - if (isAspect() && !Modifier.isAbstract(getModifiers()) && isGeneric()) { - msgHandler.handleMessage(MessageUtil.error("The generic aspect '" + getResolvedTypeX().getName() - + "' must be declared abstract", getResolvedTypeX().getSourceLocation())); - } - - } - - private AjAttribute.Aspect processAttributes(List attributeList, List pointcuts, - boolean fromAnnotations) { - AjAttribute.Aspect deferredAspectAttribute = null; - for (AjAttribute a : attributeList) { - if (a instanceof AjAttribute.Aspect) { - if (fromAnnotations) { - deferredAspectAttribute = (AjAttribute.Aspect) a; - } else { - perClause = ((AjAttribute.Aspect) a).reify(this.getResolvedTypeX()); - isCodeStyleAspect = true; - } - } else if (a instanceof AjAttribute.PointcutDeclarationAttribute) { - pointcuts.add(((AjAttribute.PointcutDeclarationAttribute) a).reify()); - } else if (a instanceof AjAttribute.WeaverState) { - weaverState = ((AjAttribute.WeaverState) a).reify(); - } else if (a instanceof AjAttribute.TypeMunger) { - typeMungers.add(((AjAttribute.TypeMunger) a).reify(getResolvedTypeX().getWorld(), getResolvedTypeX())); - } else if (a instanceof AjAttribute.DeclareAttribute) { - declares.add(((AjAttribute.DeclareAttribute) a).getDeclare()); - } else if (a instanceof AjAttribute.PrivilegedAttribute) { - AjAttribute.PrivilegedAttribute privAttribute = (AjAttribute.PrivilegedAttribute) a; - privilegedAccess = privAttribute.getAccessedMembers(); - } else if (a instanceof AjAttribute.SourceContextAttribute) { - if (getResolvedTypeX().getSourceContext() instanceof SourceContextImpl) { - AjAttribute.SourceContextAttribute sca = (AjAttribute.SourceContextAttribute) a; - ((SourceContextImpl) getResolvedTypeX().getSourceContext()).configureFromAttribute(sca.getSourceFileName(), - sca.getLineBreaks()); - - setSourcefilename(sca.getSourceFileName()); - } - } else if (a instanceof AjAttribute.WeaverVersionInfo) { - // Set the weaver version used to build this type - wvInfo = (AjAttribute.WeaverVersionInfo) a; - } else { - throw new BCException("bad attribute " + a); - } - } - return deferredAspectAttribute; - } - - /** - * Extra processing step needed because declares that come from annotations are not pre-resolved. We can't do the resolution - * until *after* the pointcuts have been resolved. - */ - private void resolveAnnotationDeclares(List attributeList) { - FormalBinding[] bindings = new org.aspectj.weaver.patterns.FormalBinding[0]; - IScope bindingScope = new BindingScope(getResolvedTypeX(), getResolvedTypeX().getSourceContext(), bindings); - for (Iterator iter = attributeList.iterator(); iter.hasNext();) { - AjAttribute a = iter.next(); - if (a instanceof AjAttribute.DeclareAttribute) { - Declare decl = (((AjAttribute.DeclareAttribute) a).getDeclare()); - if (decl instanceof DeclareErrorOrWarning) { - decl.resolve(bindingScope); - } else if (decl instanceof DeclarePrecedence) { - ((DeclarePrecedence) decl).setScopeForResolution(bindingScope); - } - } - } - } - - public PerClause getPerClause() { - ensureAspectJAttributesUnpacked(); - return perClause; - } - - public JavaClass getJavaClass() { - return javaClass; - } - - /** - * @return true if built from bytes obtained from somewhere. False if built from bytes retrieved from disk. - */ - public boolean isArtificial() { - return artificial; - } - - public void resetState() { - if (javaClass == null) { - // we might store the classname and allow reloading? - // At this point we are relying on the world to not evict if it - // might want to reweave multiple times - throw new BCException("can't weave evicted type"); - } - - bitflag = 0x0000; - - this.annotationTypes = null; - this.annotations = null; - this.interfaceSignatures = null; - this.superclassSignature = null; - this.superclassName = null; - this.fields = null; - this.methods = null; - this.pointcuts = null; - this.perClause = null; - this.weaverState = null; - this.lazyClassGen = null; - hasBeenWoven = false; - - isObject = (javaClass.getSuperclassNameIndex() == 0); - isAnnotationStyleAspect = false; - ensureAspectJAttributesUnpacked(); - } - - public void finishedWith() { - // memory usage experiments.... - // this.interfaces = null; - // this.superClass = null; - // this.fields = null; - // this.methods = null; - // this.pointcuts = null; - // this.perClause = null; - // this.weaverState = null; - // this.lazyClassGen = null; - // this next line frees up memory, but need to understand incremental - // implications - // before leaving it in. - // getResolvedTypeX().setSourceContext(null); - } - - public WeaverStateInfo getWeaverState() { - return weaverState; - } - - void setWeaverState(WeaverStateInfo weaverState) { - this.weaverState = weaverState; - } - - public void printWackyStuff(PrintStream out) { - if (typeMungers.size() > 0) { - out.println(" TypeMungers: " + typeMungers); - } - if (declares.size() > 0) { - out.println(" declares: " + declares); - } - } - - /** - * Return the lazyClassGen associated with this type. For aspect types, this value will be cached, since it is used to inline - * advice. For non-aspect types, this lazyClassGen is always newly constructed. - */ - public LazyClassGen getLazyClassGen() { - LazyClassGen ret = lazyClassGen; - if (ret == null) { - // System.err.println("creating lazy class gen for: " + this); - ret = new LazyClassGen(this); - // ret.print(System.err); - // System.err.println("made LCG from : " + - // this.getJavaClass().getSuperclassName ); - if (isAspect()) { - lazyClassGen = ret; - } - } - return ret; - } - - public boolean isSynthetic() { - return getResolvedTypeX().isSynthetic(); - } - - public AjAttribute.WeaverVersionInfo getWeaverVersionAttribute() { - return wvInfo; - } - - // -- annotation related - - public ResolvedType[] getAnnotationTypes() { - ensureAnnotationsUnpacked(); - return annotationTypes; - } - - public AnnotationAJ[] getAnnotations() { - ensureAnnotationsUnpacked(); - return annotations; - } - - public boolean hasAnnotations() { - ensureAnnotationsUnpacked(); - return annotations.length != 0; - } - - public boolean hasAnnotation(UnresolvedType ofType) { - // Due to re-entrancy we may be in the middle of unpacking the annotations already... in which case use this slow - // alternative until the stack unwinds itself - if (isUnpackingAnnotations()) { - AnnotationGen annos[] = javaClass.getAnnotations(); - if (annos == null || annos.length == 0) { - return false; - } else { - String lookingForSignature = ofType.getSignature(); - for (int a = 0; a < annos.length; a++) { - AnnotationGen annotation = annos[a]; - if (lookingForSignature.equals(annotation.getTypeSignature())) { - return true; - } - } - } - return false; - } - ensureAnnotationsUnpacked(); - for (int i = 0, max = annotationTypes.length; i < max; i++) { - UnresolvedType ax = annotationTypes[i]; - if (ax == null) { - throw new RuntimeException("Annotation entry " + i + " on type " + this.getResolvedTypeX().getName() + " is null!"); - } - if (ax.equals(ofType)) { - return true; - } - } - return false; - } - - public boolean isAnnotationWithRuntimeRetention() { - return (getRetentionPolicy() == null ? false : getRetentionPolicy().equals("RUNTIME")); - } - - public String getRetentionPolicy() { - if ((bitflag & DISCOVERED_ANNOTATION_RETENTION_POLICY) == 0) { - bitflag |= DISCOVERED_ANNOTATION_RETENTION_POLICY; - retentionPolicy = null; // null means we have no idea - if (isAnnotation()) { - ensureAnnotationsUnpacked(); - for (int i = annotations.length - 1; i >= 0; i--) { - AnnotationAJ ax = annotations[i]; - if (ax.getTypeName().equals(UnresolvedType.AT_RETENTION.getName())) { - List values = ((BcelAnnotation) ax).getBcelAnnotation().getValues(); - for (Iterator it = values.iterator(); it.hasNext();) { - NameValuePair element = it.next(); - EnumElementValue v = (EnumElementValue) element.getValue(); - retentionPolicy = v.getEnumValueString(); - return retentionPolicy; - } - } - } - } - } - return retentionPolicy; - } - - public boolean canAnnotationTargetType() { - AnnotationTargetKind[] targetKinds = getAnnotationTargetKinds(); - if (targetKinds == null) { - return true; - } - for (int i = 0; i < targetKinds.length; i++) { - if (targetKinds[i].equals(AnnotationTargetKind.TYPE)) { - return true; - } - } - return false; - } - - public AnnotationTargetKind[] getAnnotationTargetKinds() { - if ((bitflag & DISCOVERED_ANNOTATION_TARGET_KINDS) != 0) { - return annotationTargetKinds; - } - bitflag |= DISCOVERED_ANNOTATION_TARGET_KINDS; - annotationTargetKinds = null; // null means we have no idea or the - // @Target annotation hasn't been used - List targetKinds = new ArrayList(); - if (isAnnotation()) { - AnnotationAJ[] annotationsOnThisType = getAnnotations(); - for (int i = 0; i < annotationsOnThisType.length; i++) { - AnnotationAJ a = annotationsOnThisType[i]; - if (a.getTypeName().equals(UnresolvedType.AT_TARGET.getName())) { - Set targets = a.getTargets(); - if (targets != null) { - for (String targetKind : targets) { - if (targetKind.equals("ANNOTATION_TYPE")) { - targetKinds.add(AnnotationTargetKind.ANNOTATION_TYPE); - } else if (targetKind.equals("CONSTRUCTOR")) { - targetKinds.add(AnnotationTargetKind.CONSTRUCTOR); - } else if (targetKind.equals("FIELD")) { - targetKinds.add(AnnotationTargetKind.FIELD); - } else if (targetKind.equals("LOCAL_VARIABLE")) { - targetKinds.add(AnnotationTargetKind.LOCAL_VARIABLE); - } else if (targetKind.equals("METHOD")) { - targetKinds.add(AnnotationTargetKind.METHOD); - } else if (targetKind.equals("PACKAGE")) { - targetKinds.add(AnnotationTargetKind.PACKAGE); - } else if (targetKind.equals("PARAMETER")) { - targetKinds.add(AnnotationTargetKind.PARAMETER); - } else if (targetKind.equals("TYPE")) { - targetKinds.add(AnnotationTargetKind.TYPE); - } - } - } - } - } - if (!targetKinds.isEmpty()) { - annotationTargetKinds = new AnnotationTargetKind[targetKinds.size()]; - return targetKinds.toArray(annotationTargetKinds); - } - } - return annotationTargetKinds; - } - - // --- unpacking methods - - private boolean isUnpackingAnnotations() { - return (bitflag & ANNOTATION_UNPACK_IN_PROGRESS) != 0; - } - - private void ensureAnnotationsUnpacked() { - if (isUnpackingAnnotations()) { - throw new BCException("Re-entered weaver instance whilst unpacking annotations on " + this.className); - } - if (annotationTypes == null) { - try { - bitflag |= ANNOTATION_UNPACK_IN_PROGRESS; - AnnotationGen annos[] = javaClass.getAnnotations(); - if (annos == null || annos.length == 0) { - annotationTypes = ResolvedType.NONE; - annotations = AnnotationAJ.EMPTY_ARRAY; - } else { - World w = getResolvedTypeX().getWorld(); - annotationTypes = new ResolvedType[annos.length]; - annotations = new AnnotationAJ[annos.length]; - for (int i = 0; i < annos.length; i++) { - AnnotationGen annotation = annos[i]; - String typeSignature = annotation.getTypeSignature(); - ResolvedType rType = w.resolve(UnresolvedType.forSignature(typeSignature)); - if (rType == null) { - throw new RuntimeException("Whilst unpacking annotations on '" + getResolvedTypeX().getName() - + "', failed to resolve type '" + typeSignature + "'"); - } - annotationTypes[i] = rType; - annotations[i] = new BcelAnnotation(annotation, rType); - } - } - } finally { - bitflag &= ~ANNOTATION_UNPACK_IN_PROGRESS; - } - } - } - - // --- - - public String getDeclaredGenericSignature() { - ensureGenericInfoProcessed(); - return declaredSignature; - } - - private void ensureGenericSignatureUnpacked() { - if ((bitflag & UNPACKED_GENERIC_SIGNATURE) != 0) { - return; - } - bitflag |= UNPACKED_GENERIC_SIGNATURE; - if (!getResolvedTypeX().getWorld().isInJava5Mode()) { - return; - } - GenericSignature.ClassSignature cSig = getGenericClassTypeSignature(); - if (cSig != null) { - formalsForResolution = cSig.formalTypeParameters; - if (isNested()) { - // we have to find any type variables from the outer type before - // proceeding with resolution. - GenericSignature.FormalTypeParameter[] extraFormals = getFormalTypeParametersFromOuterClass(); - if (extraFormals.length > 0) { - List allFormals = new ArrayList(); - for (int i = 0; i < formalsForResolution.length; i++) { - allFormals.add(formalsForResolution[i]); - } - for (int i = 0; i < extraFormals.length; i++) { - allFormals.add(extraFormals[i]); - } - formalsForResolution = new GenericSignature.FormalTypeParameter[allFormals.size()]; - allFormals.toArray(formalsForResolution); - } - } - GenericSignature.ClassTypeSignature superSig = cSig.superclassSignature; - try { - // this.superClass = - // BcelGenericSignatureToTypeXConverter.classTypeSignature2TypeX( - // superSig, formalsForResolution, - // getResolvedTypeX().getWorld()); - - ResolvedType rt = BcelGenericSignatureToTypeXConverter.classTypeSignature2TypeX(superSig, formalsForResolution, - getResolvedTypeX().getWorld()); - this.superclassSignature = rt.getSignature(); - this.superclassName = rt.getName(); - - } catch (GenericSignatureFormatException e) { - // development bug, fail fast with good info - throw new IllegalStateException("While determining the generic superclass of " + this.className - + " with generic signature " + getDeclaredGenericSignature() + " the following error was detected: " - + e.getMessage()); - } - // this.interfaces = new - // ResolvedType[cSig.superInterfaceSignatures.length]; - if (cSig.superInterfaceSignatures.length == 0) { - this.interfaceSignatures = NO_INTERFACE_SIGS; - } else { - this.interfaceSignatures = new String[cSig.superInterfaceSignatures.length]; - for (int i = 0; i < cSig.superInterfaceSignatures.length; i++) { - try { - // this.interfaces[i] = - // BcelGenericSignatureToTypeXConverter. - // classTypeSignature2TypeX( - // cSig.superInterfaceSignatures[i], - // formalsForResolution, - // getResolvedTypeX().getWorld()); - this.interfaceSignatures[i] = BcelGenericSignatureToTypeXConverter.classTypeSignature2TypeX( - cSig.superInterfaceSignatures[i], formalsForResolution, getResolvedTypeX().getWorld()) - .getSignature(); - } catch (GenericSignatureFormatException e) { - // development bug, fail fast with good info - throw new IllegalStateException("While determing the generic superinterfaces of " + this.className - + " with generic signature " + getDeclaredGenericSignature() - + " the following error was detected: " + e.getMessage()); - } - } - } - } - if (isGeneric()) { - // update resolved typex to point at generic type not raw type. - ReferenceType genericType = (ReferenceType) this.resolvedTypeX.getGenericType(); - // genericType.setSourceContext(this.resolvedTypeX.getSourceContext()); - // Can be null if unpacking whilst building the bcel delegate (in call hierarchy from BcelWorld.addSourceObjectType() - // line 453) - see 317139 - if (genericType != null) { - genericType.setStartPos(this.resolvedTypeX.getStartPos()); - this.resolvedTypeX = genericType; - } - } - } - - public GenericSignature.FormalTypeParameter[] getAllFormals() { - ensureGenericSignatureUnpacked(); - if (formalsForResolution == null) { - return new GenericSignature.FormalTypeParameter[0]; - } else { - return formalsForResolution; - } - } - - public ResolvedType getOuterClass() { - if (!isNested()) { - throw new IllegalStateException("Can't get the outer class of non-nested type: " + className); - } - - // try finding outer class name from InnerClasses attribute assigned to this class - for (Attribute attr : javaClass.getAttributes()) { - if (attr instanceof InnerClasses) { - // search for InnerClass entry that has current class as inner and some other class as outer - InnerClass[] innerClss = ((InnerClasses) attr).getInnerClasses(); - ConstantPool cpool = javaClass.getConstantPool(); - for (InnerClass innerCls : innerClss) { - - // skip entries that miss any necessary component, 0 index means "undefined", from JVM Spec 2nd ed. par. 4.7.5 - if (innerCls.getInnerClassIndex() == 0 || innerCls.getOuterClassIndex() == 0) { - continue; - } - - // resolve inner class name, check if it matches current class name - ConstantClass innerClsInfo = (ConstantClass) cpool.getConstant(innerCls.getInnerClassIndex()); - - // class names in constant pool use '/' instead of '.', from JVM Spec 2nd ed. par. 4.2 - String innerClsName = cpool.getConstantUtf8(innerClsInfo.getNameIndex()).getValue().replace('/', '.'); - - if (innerClsName.compareTo(className) == 0) { - // resolve outer class name - ConstantClass outerClsInfo = (ConstantClass) cpool.getConstant(innerCls.getOuterClassIndex()); - - // class names in constant pool use '/' instead of '.', from JVM Spec 2nd ed. par. 4.2 - String outerClsName = cpool.getConstantUtf8(outerClsInfo.getNameIndex()).getValue().replace('/', '.'); - - UnresolvedType outer = UnresolvedType.forName(outerClsName); - return outer.resolve(getResolvedTypeX().getWorld()); - } - } - } - } - - for (Attribute attr : javaClass.getAttributes()) { // bug339300 - ConstantPool cpool = javaClass.getConstantPool(); - if (attr instanceof EnclosingMethod) { - EnclosingMethod enclosingMethodAttribute = (EnclosingMethod) attr; - if (enclosingMethodAttribute.getEnclosingClassIndex() != 0) { - ConstantClass outerClassInfo = enclosingMethodAttribute.getEnclosingClass(); - String outerClassName = cpool.getConstantUtf8(outerClassInfo.getNameIndex()).getValue().replace('/', '.'); - UnresolvedType outer = UnresolvedType.forName(outerClassName); - return outer.resolve(getResolvedTypeX().getWorld()); - } - } - } - - // try finding outer class name by assuming standard class name mangling convention of javac for this class - int lastDollar = className.lastIndexOf('$'); - if (lastDollar == -1) { - // Is this class damaged/obfuscated? Why did we think it was nested but couldn't find the parent using - // the attributes above. For now just ignore it... I wonder when ignoring this will come back to bite! - return null; - } - String superClassName = className.substring(0, lastDollar); - UnresolvedType outer = UnresolvedType.forName(superClassName); - return outer.resolve(getResolvedTypeX().getWorld()); - } - - private void ensureGenericInfoProcessed() { - if ((bitflag & DISCOVERED_DECLARED_SIGNATURE) != 0) { - return; - } - bitflag |= DISCOVERED_DECLARED_SIGNATURE; - Signature sigAttr = AttributeUtils.getSignatureAttribute(javaClass.getAttributes()); - declaredSignature = (sigAttr == null ? null : sigAttr.getSignature()); - if (declaredSignature != null) { - isGenericType = (declaredSignature.charAt(0) == '<'); - } - } - - public boolean isGeneric() { - ensureGenericInfoProcessed(); - return isGenericType; - } - - @Override - public String toString() { - return (javaClass == null ? "BcelObjectType" : "BcelObjectTypeFor:" + className); - } - - // --- state management - - public void evictWeavingState() { - // Can't chuck all this away - if (getResolvedTypeX().getWorld().couldIncrementalCompileFollow()) { - return; - } - - if (javaClass != null) { - // Force retrieval of any lazy information - ensureAnnotationsUnpacked(); - ensureGenericInfoProcessed(); - - getDeclaredInterfaces(); - getDeclaredFields(); - getDeclaredMethods(); - // The lazyClassGen is preserved for aspects - it exists to enable - // around advice - // inlining since the method will need 'injecting' into the affected - // class. If - // XnoInline is on, we can chuck away the lazyClassGen since it - // won't be required - // later. - if (getResolvedTypeX().getWorld().isXnoInline()) { - lazyClassGen = null; - } - - // discard expensive bytecode array containing reweavable info - if (weaverState != null) { - weaverState.setReweavable(false); - weaverState.setUnwovenClassFileData(null); - } - for (int i = methods.length - 1; i >= 0; i--) { - methods[i].evictWeavingState(); - } - for (int i = fields.length - 1; i >= 0; i--) { - fields[i].evictWeavingState(); - } - javaClass = null; - this.artificial = true; - // setSourceContext(SourceContextImpl.UNKNOWN_SOURCE_CONTEXT); // - // bit naughty - // interfaces=null; // force reinit - may get us the right - // instances! - // superClass=null; - } - } - - public void weavingCompleted() { - hasBeenWoven = true; - if (getResolvedTypeX().getWorld().isRunMinimalMemory()) { - evictWeavingState(); - } - if (getSourceContext() != null && !getResolvedTypeX().isAspect()) { - getSourceContext().tidy(); - } - } - - public boolean hasBeenWoven() { - return hasBeenWoven; - } - - @Override - public boolean copySourceContext() { - return false; - } - - public void setExposedToWeaver(boolean b) { - exposedToWeaver = b; - } - - @Override - public int getCompilerVersion() { - return wvInfo.getMajorVersion(); - } - - public void ensureConsistent() { - superTypeReference.clear(); - superInterfaceReferences.clear(); - } - - public boolean isWeavable() { - return true; - } -} diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelPerClauseAspectAdder.java b/weaver/src/org/aspectj/weaver/bcel/BcelPerClauseAspectAdder.java deleted file mode 100644 index b8ede0a9a..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/BcelPerClauseAspectAdder.java +++ /dev/null @@ -1,560 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2005 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * initial implementation Alexandre Vasseur - *******************************************************************************/ -package org.aspectj.weaver.bcel; - -import org.aspectj.apache.bcel.Constants; -import org.aspectj.apache.bcel.generic.InstructionBranch; -import org.aspectj.apache.bcel.generic.InstructionConstants; -import org.aspectj.apache.bcel.generic.InstructionFactory; -import org.aspectj.apache.bcel.generic.InstructionHandle; -import org.aspectj.apache.bcel.generic.InstructionList; -import org.aspectj.apache.bcel.generic.ObjectType; -import org.aspectj.apache.bcel.generic.ReferenceType; -import org.aspectj.apache.bcel.generic.Type; -import org.aspectj.weaver.AjAttribute; -import org.aspectj.weaver.AjcMemberMaker; -import org.aspectj.weaver.Member; -import org.aspectj.weaver.NameMangler; -import org.aspectj.weaver.ResolvedMember; -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.UnresolvedType; -import org.aspectj.weaver.patterns.PerClause; - -/** - * Adds aspectOf(), hasAspect() etc to the annotation defined aspects - * - * @author Alexandre Vasseur - * @author Andy Clement - */ -public class BcelPerClauseAspectAdder extends BcelTypeMunger { - - private final PerClause.Kind kind; - - private boolean hasGeneratedInner = false; - - public BcelPerClauseAspectAdder(ResolvedType aspect, PerClause.Kind kind) { - super(null, aspect); - this.kind = kind; - if (kind == PerClause.SINGLETON || kind == PerClause.PERTYPEWITHIN || kind == PerClause.PERCFLOW) { - // no inner needed - hasGeneratedInner = true; - } - } - - public boolean munge(BcelClassWeaver weaver) { - LazyClassGen gen = weaver.getLazyClassGen(); - - doAggressiveInner(gen); - - // Only munge the aspect type - if (!gen.getType().equals(aspectType)) { - return false; - } - - return doMunge(gen, true); - } - - public boolean forceMunge(LazyClassGen gen, boolean checkAlreadyThere) { - doAggressiveInner(gen); - return doMunge(gen, checkAlreadyThere); - } - - private void doAggressiveInner(LazyClassGen gen) { - // agressively generate the inner interface if any - // Note: we do so because of the bug #75442 that leads to have this interface implemented by all classes and not - // only those matched by the per clause, which fails under LTW since the very first class - // gets weaved and impl this interface that is still not defined. - if (!hasGeneratedInner) { - if (kind == PerClause.PEROBJECT) {// redundant test - see constructor, but safer - // inner class - UnresolvedType interfaceTypeX = AjcMemberMaker.perObjectInterfaceType(aspectType); - LazyClassGen interfaceGen = new LazyClassGen(interfaceTypeX.getName(), "java.lang.Object", null, - Constants.ACC_INTERFACE + Constants.ACC_PUBLIC + Constants.ACC_ABSTRACT, new String[0], getWorld()); - interfaceGen.addMethodGen(makeMethodGen(interfaceGen, AjcMemberMaker.perObjectInterfaceGet(aspectType))); - interfaceGen.addMethodGen(makeMethodGen(interfaceGen, AjcMemberMaker.perObjectInterfaceSet(aspectType))); - // not really an inner class of it but that does not matter, we pass back to the LTW - gen.addGeneratedInner(interfaceGen); - } - hasGeneratedInner = true; - } - } - - private boolean doMunge(LazyClassGen gen, boolean checkAlreadyThere) { - if (checkAlreadyThere && hasPerClauseMembersAlready(gen)) { - return false; - } - - generatePerClauseMembers(gen); - - if (kind == PerClause.SINGLETON) { - generatePerSingletonAspectOfMethod(gen); - generatePerSingletonHasAspectMethod(gen); - generatePerSingletonAjcClinitMethod(gen); - } else if (kind == PerClause.PEROBJECT) { - generatePerObjectAspectOfMethod(gen); - generatePerObjectHasAspectMethod(gen); - generatePerObjectBindMethod(gen); - // these will be added by the PerObjectInterface munger that affects the type - pr144602 - // generatePerObjectGetSetMethods(gen); - } else if (kind == PerClause.PERCFLOW) { - generatePerCflowAspectOfMethod(gen); - generatePerCflowHasAspectMethod(gen); - generatePerCflowPushMethod(gen); - generatePerCflowAjcClinitMethod(gen); - } else if (kind == PerClause.PERTYPEWITHIN) { - generatePerTWAspectOfMethod(gen); - generatePerTWHasAspectMethod(gen); - generatePerTWGetInstanceMethod(gen); - generatePerTWCreateAspectInstanceMethod(gen); - generatePerTWGetWithinTypeNameMethod(gen); - } else { - throw new Error("should not happen - not such kind " + kind.getName()); - } - return true; - } - - public ResolvedMember getMatchingSyntheticMember(Member member) { - return null; - } - - public ResolvedMember getSignature() { - return null; - } - - public boolean matches(ResolvedType onType) { - // cannot always do the right thing because may need to eagerly generate ajcMightHaveAspect interface for LTW (says Alex) - if (hasGeneratedInner) { // pr237419 - not always going to generate the marker interface - return aspectType.equals(onType); - } else { - return true; - } - } - - private boolean hasPerClauseMembersAlready(LazyClassGen classGen) { - ResolvedMember[] methods = classGen.getBcelObjectType().getDeclaredMethods(); - for (int i = 0; i < methods.length; i++) { - ResolvedMember method = methods[i]; - if ("aspectOf".equals(method.getName())) { - if ("()".equals(method.getParameterSignature()) && (kind == PerClause.SINGLETON || kind == PerClause.PERCFLOW)) { - return true; - } else if ("(Ljava/lang/Object;)".equals(method.getParameterSignature()) && kind == PerClause.PEROBJECT) { - return true; - } else if ("(Ljava/lang/Class;)".equals(method.getParameterSignature()) && kind == PerClause.PERTYPEWITHIN) { - return true; - } - } - } - return false; - } - - private void generatePerClauseMembers(LazyClassGen classGen) { - // FIXME Alex handle when field already there - or handle it with / similar to isAnnotationDefinedAspect() - // for that use aspectType and iterate on the fields. - - // FIXME Alex percflowX is not using this one but AJ code style does generate it so.. - ResolvedMember failureFieldInfo = AjcMemberMaker.initFailureCauseField(aspectType); - if (kind == PerClause.SINGLETON) { - classGen.addField(makeFieldGen(classGen, failureFieldInfo), null); - } - - if (kind == PerClause.SINGLETON) { - ResolvedMember perSingletonFieldInfo = AjcMemberMaker.perSingletonField(aspectType); - classGen.addField(makeFieldGen(classGen, perSingletonFieldInfo), null); - // pr144602 - don't need to do this, PerObjectInterface munger will do it - // } else if (kind == PerClause.PEROBJECT) { - // ResolvedMember perObjectFieldInfo = AjcMemberMaker.perObjectField(aspectType, aspectType); - // classGen.addField(makeFieldGen(classGen, perObjectFieldInfo).(), null); - // // if lazy generation of the inner interface MayHaveAspect works on LTW (see previous note) - // // it should be done here. - } else if (kind == PerClause.PERCFLOW) { - ResolvedMember perCflowFieldInfo = AjcMemberMaker.perCflowField(aspectType); - classGen.addField(makeFieldGen(classGen, perCflowFieldInfo), null); - } else if (kind == PerClause.PERTYPEWITHIN) { - ResolvedMember perTypeWithinForField = AjcMemberMaker.perTypeWithinWithinTypeField(aspectType, aspectType); - classGen.addField(makeFieldGen(classGen, perTypeWithinForField), null); - } - } - - private void generatePerSingletonAspectOfMethod(LazyClassGen classGen) { - InstructionFactory factory = classGen.getFactory(); - LazyMethodGen method = makeMethodGen(classGen, AjcMemberMaker.perSingletonAspectOfMethod(aspectType)); - flagAsSynthetic(method, false); - classGen.addMethodGen(method); - - InstructionList il = method.getBody(); - il.append(Utility.createGet(factory, AjcMemberMaker.perSingletonField(aspectType))); - InstructionBranch ifNotNull = InstructionFactory.createBranchInstruction(Constants.IFNONNULL, null); - il.append(ifNotNull); - il.append(factory.createNew(AjcMemberMaker.NO_ASPECT_BOUND_EXCEPTION.getName())); - il.append(InstructionConstants.DUP); - il.append(InstructionFactory.PUSH(classGen.getConstantPool(), aspectType.getName())); - il.append(Utility.createGet(factory, AjcMemberMaker.initFailureCauseField(aspectType))); - il.append(factory.createInvoke(AjcMemberMaker.NO_ASPECT_BOUND_EXCEPTION.getName(), "", Type.VOID, new Type[] { - Type.STRING, new ObjectType("java.lang.Throwable") }, Constants.INVOKESPECIAL)); - il.append(InstructionConstants.ATHROW); - InstructionHandle ifElse = il.append(Utility.createGet(factory, AjcMemberMaker.perSingletonField(aspectType))); - il.append(InstructionFactory.createReturn(Type.OBJECT)); - ifNotNull.setTarget(ifElse); - } - - private void generatePerSingletonHasAspectMethod(LazyClassGen classGen) { - InstructionFactory factory = classGen.getFactory(); - LazyMethodGen method = makeMethodGen(classGen, AjcMemberMaker.perSingletonHasAspectMethod(aspectType)); - flagAsSynthetic(method, false); - classGen.addMethodGen(method); - - InstructionList il = method.getBody(); - il.append(Utility.createGet(factory, AjcMemberMaker.perSingletonField(aspectType))); - InstructionBranch ifNull = InstructionFactory.createBranchInstruction(Constants.IFNULL, null); - il.append(ifNull); - il.append(InstructionFactory.PUSH(classGen.getConstantPool(), true)); - il.append(InstructionFactory.createReturn(Type.INT)); - InstructionHandle ifElse = il.append(InstructionFactory.PUSH(classGen.getConstantPool(), false)); - il.append(InstructionFactory.createReturn(Type.INT)); - ifNull.setTarget(ifElse); - } - - private void generatePerSingletonAjcClinitMethod(LazyClassGen classGen) { - InstructionFactory factory = classGen.getFactory(); - LazyMethodGen method = makeMethodGen(classGen, AjcMemberMaker.ajcPostClinitMethod(aspectType)); - flagAsSynthetic(method, true); - classGen.addMethodGen(method); - - InstructionList il = method.getBody(); - il.append(factory.createNew(aspectType.getName())); - il.append(InstructionConstants.DUP); - il.append(factory.createInvoke(aspectType.getName(), "", Type.VOID, Type.NO_ARGS, Constants.INVOKESPECIAL)); - il.append(Utility.createSet(factory, AjcMemberMaker.perSingletonField(aspectType))); - il.append(InstructionFactory.createReturn(Type.VOID)); - - // patch to delegate to ajc$postClinit at the end - LazyMethodGen clinit = classGen.getStaticInitializer(); - il = new InstructionList(); - InstructionHandle tryStart = il.append(factory.createInvoke(aspectType.getName(), NameMangler.AJC_POST_CLINIT_NAME, - Type.VOID, Type.NO_ARGS, Constants.INVOKESTATIC)); - InstructionBranch tryEnd = InstructionFactory.createBranchInstruction(Constants.GOTO, null); - il.append(tryEnd); - InstructionHandle handler = il.append(InstructionConstants.ASTORE_0); - il.append(InstructionConstants.ALOAD_0); - il.append(Utility.createSet(factory, AjcMemberMaker.initFailureCauseField(aspectType))); - il.append(InstructionFactory.createReturn(Type.VOID)); - tryEnd.setTarget(il.getEnd()); - - // replace the original "return" with a "nop" - // TODO AV - a bit odd, looks like Bcel alters bytecode and has a IMPDEP1 in its representation - if (clinit.getBody().getEnd().getInstruction().opcode == Constants.IMPDEP1) { - clinit.getBody().getEnd().getPrev().setInstruction(InstructionConstants.NOP); - } - clinit.getBody().getEnd().setInstruction(InstructionConstants.NOP); - clinit.getBody().append(il); - - clinit.addExceptionHandler(tryStart, handler.getPrev(), handler, new ObjectType("java.lang.Throwable"), false); - } - - private void generatePerObjectAspectOfMethod(LazyClassGen classGen) { - InstructionFactory factory = classGen.getFactory(); - ReferenceType interfaceType = (ReferenceType) BcelWorld.makeBcelType(AjcMemberMaker.perObjectInterfaceType(aspectType)); - LazyMethodGen method = makeMethodGen(classGen, AjcMemberMaker.perObjectAspectOfMethod(aspectType)); - flagAsSynthetic(method, false); - classGen.addMethodGen(method); - - InstructionList il = method.getBody(); - il.append(InstructionConstants.ALOAD_0); - il.append(factory.createInstanceOf(interfaceType)); - InstructionBranch ifEq = InstructionFactory.createBranchInstruction(Constants.IFEQ, null); - il.append(ifEq); - il.append(InstructionConstants.ALOAD_0); - il.append(factory.createCheckCast(interfaceType)); - il.append(Utility.createInvoke(factory, Constants.INVOKEINTERFACE, AjcMemberMaker.perObjectInterfaceGet(aspectType))); - il.append(InstructionConstants.DUP); - InstructionBranch ifNull = InstructionFactory.createBranchInstruction(Constants.IFNULL, null); - il.append(ifNull); - il.append(InstructionFactory.createReturn(BcelWorld.makeBcelType(aspectType))); - InstructionHandle ifNullElse = il.append(InstructionConstants.POP); - ifNull.setTarget(ifNullElse); - InstructionHandle ifEqElse = il.append(factory.createNew(AjcMemberMaker.NO_ASPECT_BOUND_EXCEPTION.getName())); - ifEq.setTarget(ifEqElse); - il.append(InstructionConstants.DUP); - il.append(factory.createInvoke(AjcMemberMaker.NO_ASPECT_BOUND_EXCEPTION.getName(), "", Type.VOID, Type.NO_ARGS, - Constants.INVOKESPECIAL)); - il.append(InstructionConstants.ATHROW); - } - - private void generatePerObjectHasAspectMethod(LazyClassGen classGen) { - InstructionFactory factory = classGen.getFactory(); - ReferenceType interfaceType = (ReferenceType) BcelWorld.makeBcelType(AjcMemberMaker.perObjectInterfaceType(aspectType)); - LazyMethodGen method = makeMethodGen(classGen, AjcMemberMaker.perObjectHasAspectMethod(aspectType)); - flagAsSynthetic(method, false); - classGen.addMethodGen(method); - - InstructionList il = method.getBody(); - il.append(InstructionConstants.ALOAD_0); - il.append(factory.createInstanceOf(interfaceType)); - InstructionBranch ifEq = InstructionFactory.createBranchInstruction(Constants.IFEQ, null); - il.append(ifEq); - il.append(InstructionConstants.ALOAD_0); - il.append(factory.createCheckCast(interfaceType)); - il.append(Utility.createInvoke(factory, Constants.INVOKEINTERFACE, AjcMemberMaker.perObjectInterfaceGet(aspectType))); - InstructionBranch ifNull = InstructionFactory.createBranchInstruction(Constants.IFNULL, null); - il.append(ifNull); - il.append(InstructionConstants.ICONST_1); - il.append(InstructionFactory.createReturn(Type.INT)); - InstructionHandle ifEqElse = il.append(InstructionConstants.ICONST_0); - ifEq.setTarget(ifEqElse); - ifNull.setTarget(ifEqElse); - il.append(InstructionFactory.createReturn(Type.INT)); - } - - private void generatePerObjectBindMethod(LazyClassGen classGen) { - InstructionFactory factory = classGen.getFactory(); - ReferenceType interfaceType = (ReferenceType) BcelWorld.makeBcelType(AjcMemberMaker.perObjectInterfaceType(aspectType)); - LazyMethodGen method = makeMethodGen(classGen, AjcMemberMaker.perObjectBind(aspectType)); - flagAsSynthetic(method, true); - classGen.addMethodGen(method); - - InstructionList il = method.getBody(); - il.append(InstructionConstants.ALOAD_0); - il.append(factory.createInstanceOf(interfaceType)); - InstructionBranch ifEq = InstructionFactory.createBranchInstruction(Constants.IFEQ, null); - il.append(ifEq); - il.append(InstructionConstants.ALOAD_0); - il.append(factory.createCheckCast(interfaceType)); - il.append(Utility.createInvoke(factory, Constants.INVOKEINTERFACE, AjcMemberMaker.perObjectInterfaceGet(aspectType))); - InstructionBranch ifNonNull = InstructionFactory.createBranchInstruction(Constants.IFNONNULL, null); - il.append(ifNonNull); - il.append(InstructionConstants.ALOAD_0); - il.append(factory.createCheckCast(interfaceType)); - il.append(factory.createNew(aspectType.getName())); - il.append(InstructionConstants.DUP); - il.append(factory.createInvoke(aspectType.getName(), "", Type.VOID, Type.NO_ARGS, Constants.INVOKESPECIAL)); - il.append(Utility.createInvoke(factory, Constants.INVOKEINTERFACE, AjcMemberMaker.perObjectInterfaceSet(aspectType))); - InstructionHandle end = il.append(InstructionFactory.createReturn(Type.VOID)); - ifEq.setTarget(end); - ifNonNull.setTarget(end); - } - - // private void generatePerObjectGetSetMethods(LazyClassGen classGen) { - // InstructionFactory factory = classGen.getFactory(); - // - // LazyMethodGen methodGet = makeMethodGen(classGen, AjcMemberMaker.perObjectInterfaceGet(aspectType)); - // flagAsSynthetic(methodGet, true); - // classGen.addMethodGen(methodGet); - // InstructionList ilGet = methodGet.getBody(); - // ilGet = new InstructionList(); - // ilGet.append(InstructionConstants.ALOAD_0); - // ilGet.append(Utility.createGet(factory, AjcMemberMaker.perObjectField(aspectType, aspectType))); - // ilGet.append(InstructionFactory.createReturn(Type.OBJECT)); - // - // LazyMethodGen methodSet = makeMethodGen(classGen, AjcMemberMaker.perObjectInterfaceSet(aspectType)); - // flagAsSynthetic(methodSet, true); - // classGen.addMethodGen(methodSet); - // InstructionList ilSet = methodSet.getBody(); - // ilSet = new InstructionList(); - // ilSet.append(InstructionConstants.ALOAD_0); - // ilSet.append(InstructionConstants.ALOAD_1); - // ilSet.append(Utility.createSet(factory, AjcMemberMaker.perObjectField(aspectType, aspectType))); - // ilSet.append(InstructionFactory.createReturn(Type.VOID)); - // } - - private void generatePerCflowAspectOfMethod(LazyClassGen classGen) { - InstructionFactory factory = classGen.getFactory(); - LazyMethodGen method = makeMethodGen(classGen, AjcMemberMaker.perCflowAspectOfMethod(aspectType)); - flagAsSynthetic(method, false); - classGen.addMethodGen(method); - - InstructionList il = method.getBody(); - il.append(Utility.createGet(factory, AjcMemberMaker.perCflowField(aspectType))); - il.append(Utility.createInvoke(factory, Constants.INVOKEVIRTUAL, AjcMemberMaker.cflowStackPeekInstance())); - il.append(factory.createCheckCast((ReferenceType) BcelWorld.makeBcelType(aspectType))); - il.append(InstructionFactory.createReturn(Type.OBJECT)); - } - - private void generatePerCflowHasAspectMethod(LazyClassGen classGen) { - InstructionFactory factory = classGen.getFactory(); - LazyMethodGen method = makeMethodGen(classGen, AjcMemberMaker.perCflowHasAspectMethod(aspectType)); - flagAsSynthetic(method, false); - classGen.addMethodGen(method); - - InstructionList il = method.getBody(); - il.append(Utility.createGet(factory, AjcMemberMaker.perCflowField(aspectType))); - il.append(Utility.createInvoke(factory, Constants.INVOKEVIRTUAL, AjcMemberMaker.cflowStackIsValid())); - il.append(InstructionFactory.createReturn(Type.INT)); - } - - private void generatePerCflowPushMethod(LazyClassGen classGen) { - InstructionFactory factory = classGen.getFactory(); - LazyMethodGen method = makeMethodGen(classGen, AjcMemberMaker.perCflowPush(aspectType)); - flagAsSynthetic(method, true); - classGen.addMethodGen(method); - - InstructionList il = method.getBody(); - il.append(Utility.createGet(factory, AjcMemberMaker.perCflowField(aspectType))); - il.append(factory.createNew(aspectType.getName())); - il.append(InstructionConstants.DUP); - il.append(factory.createInvoke(aspectType.getName(), "", Type.VOID, Type.NO_ARGS, Constants.INVOKESPECIAL)); - il.append(Utility.createInvoke(factory, Constants.INVOKEVIRTUAL, AjcMemberMaker.cflowStackPushInstance())); - il.append(InstructionFactory.createReturn(Type.VOID)); - } - - private void generatePerCflowAjcClinitMethod(LazyClassGen classGen) { - InstructionFactory factory = classGen.getFactory(); - - LazyMethodGen method = classGen.getAjcPreClinit(); // Creates a clinit if there isn't one - - InstructionList il = new InstructionList(); - il.append(factory.createNew(AjcMemberMaker.CFLOW_STACK_TYPE.getName())); - il.append(InstructionConstants.DUP); - il.append(factory.createInvoke(AjcMemberMaker.CFLOW_STACK_TYPE.getName(), "", Type.VOID, Type.NO_ARGS, - Constants.INVOKESPECIAL)); - il.append(Utility.createSet(factory, AjcMemberMaker.perCflowField(aspectType))); - method.getBody().insert(il); - } - - private void generatePerTWAspectOfMethod(LazyClassGen classGen) { - InstructionFactory factory = classGen.getFactory(); - LazyMethodGen method = makeMethodGen(classGen, AjcMemberMaker.perTypeWithinAspectOfMethod(aspectType, classGen.getWorld() - .isInJava5Mode())); - flagAsSynthetic(method, false); - classGen.addMethodGen(method); - - InstructionList il = method.getBody(); - InstructionHandle tryStart = il.append(InstructionConstants.ALOAD_0); - - il.append(Utility.createInvoke(factory, Constants.INVOKESTATIC, AjcMemberMaker.perTypeWithinGetInstance(aspectType))); - il.append(InstructionConstants.ASTORE_1); - il.append(InstructionConstants.ALOAD_1); - InstructionBranch ifNonNull = InstructionFactory.createBranchInstruction(Constants.IFNONNULL, null); - il.append(ifNonNull); - il.append(factory.createNew(AjcMemberMaker.NO_ASPECT_BOUND_EXCEPTION.getName())); - il.append(InstructionConstants.DUP); - il.append(InstructionFactory.PUSH(classGen.getConstantPool(), aspectType.getName())); - il.append(InstructionConstants.ACONST_NULL); - il.append(factory.createInvoke(AjcMemberMaker.NO_ASPECT_BOUND_EXCEPTION.getName(), "", Type.VOID, new Type[] { - Type.STRING, new ObjectType("java.lang.Throwable") }, Constants.INVOKESPECIAL)); - il.append(InstructionConstants.ATHROW); - InstructionHandle ifElse = il.append(InstructionConstants.ALOAD_1); - ifNonNull.setTarget(ifElse); - il.append(InstructionFactory.createReturn(Type.OBJECT)); - - InstructionHandle handler = il.append(InstructionConstants.ASTORE_1); - il.append(factory.createNew(AjcMemberMaker.NO_ASPECT_BOUND_EXCEPTION.getName())); - il.append(InstructionConstants.DUP); - il.append(factory.createInvoke(AjcMemberMaker.NO_ASPECT_BOUND_EXCEPTION.getName(), "", Type.VOID, Type.NO_ARGS, - Constants.INVOKESPECIAL)); - il.append(InstructionConstants.ATHROW); - - method.addExceptionHandler(tryStart, handler.getPrev(), handler, new ObjectType("java.lang.Exception"), false); - } - - // Create 'public String getWithinTypeName() { return ajc$withinType;}' - private void generatePerTWGetWithinTypeNameMethod(LazyClassGen classGen) { - InstructionFactory factory = classGen.getFactory(); - LazyMethodGen method = makeMethodGen(classGen, AjcMemberMaker.perTypeWithinGetWithinTypeNameMethod(aspectType, classGen - .getWorld().isInJava5Mode())); - flagAsSynthetic(method, false); - classGen.addMethodGen(method); - // 0: aload_0 - // 1: getfield #14; //Field ajc$withinType:Ljava/lang/String; - // 4: areturn - InstructionList il = method.getBody(); - il.append(InstructionConstants.ALOAD_0); - il.append(Utility.createGet(factory, AjcMemberMaker.perTypeWithinWithinTypeField(aspectType, aspectType))); - il.append(InstructionConstants.ARETURN); - } - - private void generatePerTWHasAspectMethod(LazyClassGen classGen) { - InstructionFactory factory = classGen.getFactory(); - LazyMethodGen method = makeMethodGen(classGen, AjcMemberMaker.perTypeWithinHasAspectMethod(aspectType, classGen.getWorld() - .isInJava5Mode())); - flagAsSynthetic(method, false); - classGen.addMethodGen(method); - - InstructionList il = method.getBody(); - InstructionHandle tryStart = il.append(InstructionConstants.ALOAD_0); - il.append(Utility.createInvoke(factory, Constants.INVOKESTATIC, AjcMemberMaker.perTypeWithinGetInstance(aspectType))); - InstructionBranch ifNull = InstructionFactory.createBranchInstruction(Constants.IFNULL, null); - il.append(ifNull); - il.append(InstructionConstants.ICONST_1); - il.append(InstructionConstants.IRETURN); - InstructionHandle ifElse = il.append(InstructionConstants.ICONST_0); - ifNull.setTarget(ifElse); - il.append(InstructionConstants.IRETURN); - - InstructionHandle handler = il.append(InstructionConstants.ASTORE_1); - il.append(InstructionConstants.ICONST_0); - il.append(InstructionConstants.IRETURN); - - method.addExceptionHandler(tryStart, handler.getPrev(), handler, new ObjectType("java.lang.Exception"), false); - } - - private void generatePerTWGetInstanceMethod(LazyClassGen classGen) { - InstructionFactory factory = classGen.getFactory(); - LazyMethodGen method = makeMethodGen(classGen, AjcMemberMaker.perTypeWithinGetInstance(aspectType)); - flagAsSynthetic(method, true); - classGen.addMethodGen(method); - - InstructionList il = method.getBody(); - InstructionHandle tryStart = il.append(InstructionConstants.ALOAD_0); - il.append(InstructionFactory.PUSH(factory.getConstantPool(), NameMangler.perTypeWithinLocalAspectOf(aspectType))); - il.append(InstructionConstants.ACONST_NULL);// Class[] for "getDeclaredMethod" - il.append(factory.createInvoke("java/lang/Class", "getDeclaredMethod", Type.getType("Ljava/lang/reflect/Method;"), - new Type[] { Type.getType("Ljava/lang/String;"), Type.getType("[Ljava/lang/Class;") }, Constants.INVOKEVIRTUAL)); - il.append(InstructionConstants.ACONST_NULL);// Object for "invoke", static method - il.append(InstructionConstants.ACONST_NULL);// Object[] for "invoke", no arg - il.append(factory.createInvoke("java/lang/reflect/Method", "invoke", Type.OBJECT, new Type[] { - Type.getType("Ljava/lang/Object;"), Type.getType("[Ljava/lang/Object;") }, Constants.INVOKEVIRTUAL)); - il.append(factory.createCheckCast((ReferenceType) BcelWorld.makeBcelType(aspectType))); - il.append(InstructionConstants.ARETURN); - - InstructionHandle handler = il.append(InstructionConstants.ASTORE_1); - il.append(InstructionConstants.ACONST_NULL); - il.append(InstructionConstants.ARETURN); - - method.addExceptionHandler(tryStart, handler.getPrev(), handler, new ObjectType("java.lang.Exception"), false); - } - - private void generatePerTWCreateAspectInstanceMethod(LazyClassGen classGen) { - InstructionFactory factory = classGen.getFactory(); - LazyMethodGen method = makeMethodGen(classGen, AjcMemberMaker.perTypeWithinCreateAspectInstance(aspectType)); - flagAsSynthetic(method, true); - classGen.addMethodGen(method); - - InstructionList il = method.getBody(); - il.append(factory.createNew(aspectType.getName())); - il.append(InstructionConstants.DUP); - il.append(factory.createInvoke(aspectType.getName(), "", Type.VOID, Type.NO_ARGS, Constants.INVOKESPECIAL)); - il.append(InstructionConstants.ASTORE_1); - il.append(InstructionConstants.ALOAD_1); - il.append(InstructionConstants.ALOAD_0); - il.append(Utility.createSet(factory, AjcMemberMaker.perTypeWithinWithinTypeField(aspectType, aspectType))); - il.append(InstructionConstants.ALOAD_1); - il.append(InstructionConstants.ARETURN); - } - - /** - * Add standard Synthetic (if wished) and AjSynthetic (always) attributes - * - * @param methodGen - * @param makeJavaSynthetic true if standard Synthetic attribute must be set as well (invisible to user) - */ - private static void flagAsSynthetic(LazyMethodGen methodGen, boolean makeJavaSynthetic) { - if (makeJavaSynthetic) { - methodGen.makeSynthetic(); - } - methodGen.addAttribute(Utility - .bcelAttribute(new AjAttribute.AjSynthetic(), methodGen.getEnclosingClass().getConstantPool())); - } - - // public boolean isLateTypeMunger() { - // return true; - // } -} diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelRenderer.java b/weaver/src/org/aspectj/weaver/bcel/BcelRenderer.java deleted file mode 100644 index e1f99439f..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/BcelRenderer.java +++ /dev/null @@ -1,270 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - -package org.aspectj.weaver.bcel; - -import java.lang.reflect.Modifier; - -import org.aspectj.apache.bcel.Constants; -import org.aspectj.apache.bcel.generic.InstructionFactory; -import org.aspectj.apache.bcel.generic.InstructionHandle; -import org.aspectj.apache.bcel.generic.InstructionList; -import org.aspectj.apache.bcel.generic.ObjectType; -import org.aspectj.apache.bcel.generic.ReferenceType; -import org.aspectj.apache.bcel.generic.Type; -import org.aspectj.weaver.BCException; -import org.aspectj.weaver.Member; -import org.aspectj.weaver.MemberImpl; -import org.aspectj.weaver.UnresolvedType; -import org.aspectj.weaver.ast.And; -import org.aspectj.weaver.ast.Call; -import org.aspectj.weaver.ast.CallExpr; -import org.aspectj.weaver.ast.Expr; -import org.aspectj.weaver.ast.FieldGet; -import org.aspectj.weaver.ast.FieldGetCall; -import org.aspectj.weaver.ast.HasAnnotation; -import org.aspectj.weaver.ast.IExprVisitor; -import org.aspectj.weaver.ast.ITestVisitor; -import org.aspectj.weaver.ast.Instanceof; -import org.aspectj.weaver.ast.Literal; -import org.aspectj.weaver.ast.Not; -import org.aspectj.weaver.ast.Or; -import org.aspectj.weaver.ast.Test; -import org.aspectj.weaver.ast.Var; -import org.aspectj.weaver.internal.tools.MatchingContextBasedTest; - -// we generate right to left, btw. -public final class BcelRenderer implements ITestVisitor, IExprVisitor { - - private InstructionList instructions; - private InstructionFactory fact; - private BcelWorld world; - - InstructionHandle sk, fk, next = null; - - private BcelRenderer(InstructionFactory fact, BcelWorld world) { - super(); - this.fact = fact; - this.world = world; - this.instructions = new InstructionList(); - } - - // ---- renderers - - public static InstructionList renderExpr(InstructionFactory fact, BcelWorld world, Expr e) { - BcelRenderer renderer = new BcelRenderer(fact, world); - e.accept(renderer); - return renderer.instructions; - } - - public static InstructionList renderExpr(InstructionFactory fact, BcelWorld world, Expr e, Type desiredType) { - BcelRenderer renderer = new BcelRenderer(fact, world); - e.accept(renderer); - InstructionList il = renderer.instructions; - il.append(Utility.createConversion(fact, BcelWorld.makeBcelType(e.getType()), desiredType)); - return il; - } - - public static InstructionList renderExprs(InstructionFactory fact, BcelWorld world, Expr[] es) { - BcelRenderer renderer = new BcelRenderer(fact, world); - for (int i = es.length - 1; i >= 0; i--) { - es[i].accept(renderer); - } - return renderer.instructions; - } - - /* - * Get the instructions representing this test. - * - * @param e test to render - * - * @param sk instructionHandle to jump to if our rendered check succeeds (typically start of advice) - * - * @param fk instructionHandle to jump to if our rendered check fails (typically after end of advice) - * - * @param next instructionHandle that will follow this generated code. Passing in null will generate one unnecessary GOTO - * instruction. - * - * @returns the instruction list representing this expression - */ - public static InstructionList renderTest(InstructionFactory fact, BcelWorld world, Test e, InstructionHandle sk, - InstructionHandle fk, InstructionHandle next) { - BcelRenderer renderer = new BcelRenderer(fact, world); - renderer.recur(e, sk, fk, next); - return renderer.instructions; - } - - // ---- recurrers - - private void recur(Test e, InstructionHandle sk, InstructionHandle fk, InstructionHandle next) { - this.sk = sk; - this.fk = fk; - this.next = next; - e.accept(this); - } - - // ---- test visitors - - public void visit(And e) { - InstructionHandle savedFk = fk; - recur(e.getRight(), sk, fk, next); - InstructionHandle ning = instructions.getStart(); - recur(e.getLeft(), ning, savedFk, ning); - } - - public void visit(Or e) { - InstructionHandle savedSk = sk; - recur(e.getRight(), sk, fk, next); - recur(e.getLeft(), savedSk, instructions.getStart(), instructions.getStart()); - } - - public void visit(Not e) { - recur(e.getBody(), fk, sk, next); - } - - public void visit(Instanceof i) { - instructions.insert(createJumpBasedOnBooleanOnStack()); - instructions.insert(Utility.createInstanceof(fact, (ReferenceType) BcelWorld.makeBcelType(i.getType()))); - i.getVar().accept(this); - } - - public void visit(HasAnnotation hasAnnotation) { - // in Java: - // foo.class.isAnnotationPresent(annotationClass); - // in bytecode: - - // ifnull? skip to the end if it is as getClass() will fail (see pr 257833) - - // load var onto the stack (done for us later) - // invokevirtual java/lang/Object.getClass:()Ljava/lang/Class - // ldc_w annotationClass - // invokevirtual java/lang/Class.isAnnotationPresent:(Ljava/lang/Class;)Z - InstructionList il = new InstructionList(); - - // If it is null jump past the advice call - il.append(InstructionFactory.createBranchInstruction(Constants.IFNULL, fk)); - - // Load up the var again - il.append(((BcelVar) hasAnnotation.getVar()).createLoad(fact)); - - Member getClass = MemberImpl.method(UnresolvedType.OBJECT, 0, UnresolvedType.JL_CLASS, "getClass", UnresolvedType.NONE); - il.append(Utility.createInvoke(fact, world, getClass)); - // aload annotationClass - il.append(fact.createConstant(new ObjectType(hasAnnotation.getAnnotationType().getName()))); - // int annClassIndex = fact.getConstantPool().addClass(hasAnnotation.getAnnotationType().getSignature()); - // il.append(new LDC_W(annClassIndex)); - Member isAnnotationPresent = MemberImpl.method(UnresolvedType.JL_CLASS, 0, UnresolvedType.BOOLEAN, "isAnnotationPresent", - new UnresolvedType[] { UnresolvedType.JL_CLASS }); - il.append(Utility.createInvoke(fact, world, isAnnotationPresent)); - il.append(createJumpBasedOnBooleanOnStack()); - instructions.insert(il); - hasAnnotation.getVar().accept(this); - } - - /* - * (non-Javadoc) - * - * @see org.aspectj.weaver.ast.ITestVisitor#visit(org.aspectj.weaver.internal.tools.MatchingContextBasedTest) - */ - public void visit(MatchingContextBasedTest matchingContextTest) { - throw new UnsupportedOperationException("matching context extension not supported in bytecode weaving"); - } - - private InstructionList createJumpBasedOnBooleanOnStack() { - InstructionList il = new InstructionList(); - if (sk == fk) { - // don't bother generating if it doesn't matter - if (sk != next) { - il.insert(InstructionFactory.createBranchInstruction(Constants.GOTO, sk)); - } - return il; - } - - if (fk == next) { - il.insert(InstructionFactory.createBranchInstruction(Constants.IFNE, sk)); - } else if (sk == next) { - il.insert(InstructionFactory.createBranchInstruction(Constants.IFEQ, fk)); - } else { - il.insert(InstructionFactory.createBranchInstruction(Constants.GOTO, sk)); - il.insert(InstructionFactory.createBranchInstruction(Constants.IFEQ, fk)); - } - return il; - } - - public void visit(Literal literal) { - if (literal == Literal.FALSE) { - throw new BCException("visiting a false expression"); - } - } - - public void visit(Call call) { - Member method = call.getMethod(); - // assert method.isStatic() - Expr[] args = call.getArgs(); - InstructionList callIl = new InstructionList(); - for (int i = 0, len = args.length; i < len; i++) { - // XXX only correct for static method calls - Type desiredType = BcelWorld.makeBcelType(method.getParameterTypes()[i]); - Expr arg = args[i]; - // if arg is null it is because we couldn't bind it properly, for example see 162135 - if (arg == null) { - InstructionList iList = new InstructionList(); - iList.append(InstructionFactory.createNull(desiredType)); - callIl.append(iList); - } else { - callIl.append(renderExpr(fact, world, arg, desiredType)); - } - } - // System.out.println("rendered args: " + callIl); - callIl.append(Utility.createInvoke(fact, world, method)); - callIl.append(createJumpBasedOnBooleanOnStack()); - instructions.insert(callIl); - } - - public void visit(FieldGetCall fieldGetCall) { - Member field = fieldGetCall.getField(); - Member method = fieldGetCall.getMethod(); - InstructionList il = new InstructionList(); - il.append(Utility.createGet(fact, field)); - // assert !method.isStatic() - Expr[] args = fieldGetCall.getArgs(); - // System.out.println("args: " + Arrays.asList(args)); - il.append(renderExprs(fact, world, args)); - // System.out.println("rendered args: " + callIl); - il.append(Utility.createInvoke(fact, world, method)); - il.append(createJumpBasedOnBooleanOnStack()); - instructions.insert(il); - } - - // ---- expr visitors - - public void visit(Var var) { - BcelVar bvar = (BcelVar) var; - bvar.insertLoad(instructions, fact); - } - - public void visit(FieldGet fieldGet) { - Member field = fieldGet.getField(); - // assert field.isStatic() - instructions.insert(Utility.createGet(fact, field)); - } - - public void visit(CallExpr call) { - Member method = call.getMethod(); - // assert method.isStatic() - Expr[] args = call.getArgs(); - InstructionList callIl = renderExprs(fact, world, args); - callIl.append(Utility.createInvoke(fact, world, method)); - instructions.insert(callIl); - } - -} diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelShadow.java b/weaver/src/org/aspectj/weaver/bcel/BcelShadow.java deleted file mode 100644 index c93a0f26e..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/BcelShadow.java +++ /dev/null @@ -1,3425 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * Alexandre Vasseur support for @AJ aspects - * ******************************************************************/ - -package org.aspectj.weaver.bcel; - -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import org.aspectj.apache.bcel.Constants; -import org.aspectj.apache.bcel.classfile.ConstantPool; -import org.aspectj.apache.bcel.classfile.Field; -import org.aspectj.apache.bcel.generic.ArrayType; -import org.aspectj.apache.bcel.generic.FieldInstruction; -import org.aspectj.apache.bcel.generic.INVOKEINTERFACE; -import org.aspectj.apache.bcel.generic.Instruction; -import org.aspectj.apache.bcel.generic.InstructionBranch; -import org.aspectj.apache.bcel.generic.InstructionConstants; -import org.aspectj.apache.bcel.generic.InstructionFactory; -import org.aspectj.apache.bcel.generic.InstructionHandle; -import org.aspectj.apache.bcel.generic.InstructionLV; -import org.aspectj.apache.bcel.generic.InstructionList; -import org.aspectj.apache.bcel.generic.InstructionTargeter; -import org.aspectj.apache.bcel.generic.InvokeInstruction; -import org.aspectj.apache.bcel.generic.LineNumberTag; -import org.aspectj.apache.bcel.generic.LocalVariableTag; -import org.aspectj.apache.bcel.generic.MULTIANEWARRAY; -import org.aspectj.apache.bcel.generic.ObjectType; -import org.aspectj.apache.bcel.generic.TargetLostException; -import org.aspectj.apache.bcel.generic.Type; -import org.aspectj.bridge.ISourceLocation; -import org.aspectj.weaver.Advice; -import org.aspectj.weaver.AdviceKind; -import org.aspectj.weaver.AjcMemberMaker; -import org.aspectj.weaver.BCException; -import org.aspectj.weaver.ConcreteTypeMunger; -import org.aspectj.weaver.IntMap; -import org.aspectj.weaver.Member; -import org.aspectj.weaver.MemberImpl; -import org.aspectj.weaver.NameMangler; -import org.aspectj.weaver.NewConstructorTypeMunger; -import org.aspectj.weaver.NewFieldTypeMunger; -import org.aspectj.weaver.NewMethodTypeMunger; -import org.aspectj.weaver.ResolvedMember; -import org.aspectj.weaver.ResolvedMemberImpl; -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.Shadow; -import org.aspectj.weaver.ShadowMunger; -import org.aspectj.weaver.UnresolvedType; -import org.aspectj.weaver.WeaverMessages; -import org.aspectj.weaver.World; -import org.aspectj.weaver.ast.Var; -import org.aspectj.weaver.patterns.AbstractPatternNodeVisitor; -import org.aspectj.weaver.patterns.AndPointcut; -import org.aspectj.weaver.patterns.NotPointcut; -import org.aspectj.weaver.patterns.OrPointcut; -import org.aspectj.weaver.patterns.ThisOrTargetPointcut; - -/* - * Some fun implementation stuff: - * - * * expressionKind advice is non-execution advice - * * may have a target. - * * if the body is extracted, it will be extracted into - * a static method. The first argument to the static - * method is the target - * * advice may expose a this object, but that's the advice's - * consideration, not ours. This object will NOT be cached in another - * local, but will always come from frame zero. - * - * * non-expressionKind advice is execution advice - * * may have a this. - * * target is same as this, and is exposed that way to advice - * (i.e., target will not be cached, will always come from frame zero) - * * if the body is extracted, it will be extracted into a method - * with same static/dynamic modifier as enclosing method. If non-static, - * target of callback call will be this. - * - * * because of these two facts, the setup of the actual arguments (including - * possible target) callback method is the same for both kinds of advice: - * push the targetVar, if it exists (it will not exist for advice on static - * things), then push all the argVars. - * - * Protected things: - * - * * the above is sufficient for non-expressionKind advice for protected things, - * since the target will always be this. - * - * * For expressionKind things, we have to modify the signature of the callback - * method slightly. For non-static expressionKind things, we modify - * the first argument of the callback method NOT to be the type specified - * by the method/field signature (the owner), but rather we type it to - * the currentlyEnclosing type. We are guaranteed this will be fine, - * since the verifier verifies that the target is a subtype of the currently - * enclosingType. - * - * Worries: - * - * * ConstructorCalls will be weirder than all of these, since they - * supposedly don't have a target (according to AspectJ), but they clearly - * do have a target of sorts, just one that needs to be pushed on the stack, - * dupped, and not touched otherwise until the constructor runs. - * - * @author Jim Hugunin - * @author Erik Hilsdale - * - */ - -public class BcelShadow extends Shadow { - - private static final String[] NoDeclaredExceptions = new String[0]; - - private ShadowRange range; - private final BcelWorld world; - private final LazyMethodGen enclosingMethod; - - // TESTING this will tell us if the optimisation succeeded *on the last shadow processed* - public static boolean appliedLazyTjpOptimization; - - // Some instructions have a target type that will vary - // from the signature (pr109728) (1.4 declaring type issue) - private String actualInstructionTargetType; - - /** - * This generates an unassociated shadow, rooted in a particular method but not rooted to any particular point in the code. It - * should be given to a rooted ShadowRange in the {@link ShadowRange#associateWithShadow(BcelShadow)} method. - */ - public BcelShadow(BcelWorld world, Kind kind, Member signature, LazyMethodGen enclosingMethod, BcelShadow enclosingShadow) { - super(kind, signature, enclosingShadow); - this.world = world; - this.enclosingMethod = enclosingMethod; - } - - // ---- copies all state, including Shadow's mungers... - - public BcelShadow copyInto(LazyMethodGen recipient, BcelShadow enclosing) { - BcelShadow s = new BcelShadow(world, getKind(), getSignature(), recipient, enclosing); - if (mungers.size() > 0) { - List src = mungers; - if (s.mungers == Collections.EMPTY_LIST) { - s.mungers = new ArrayList(); - } - List dest = s.mungers; - for (Iterator i = src.iterator(); i.hasNext();) { - dest.add(i.next()); - } - } - return s; - } - - // ---- overridden behaviour - - @Override - public World getIWorld() { - return world; - } - - // see comment in deleteNewAndDup - // } else if (inst.opcode == Constants.DUP_X2) { - // // This code seen in the wild (by Brad): - // // 40: new #12; //class java/lang/StringBuffer - // // STACK: STRINGBUFFER - // // 43: dup - // // STACK: STRINGBUFFER/STRINGBUFFER - // // 44: aload_0 - // // STACK: STRINGBUFFER/STRINGBUFFER/THIS - // // 45: dup_x2 - // // STACK: THIS/STRINGBUFFER/STRINGBUFFER/THIS - // // 46: getfield #36; //Field value:Ljava/lang/String; - // // STACK: THIS/STRINGBUFFER/STRINGBUFFER/STRING - // // 49: invokestatic #37; //Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String; - // // STACK: THIS/STRINGBUFFER/STRINGBUFFER/STRING - // // 52: invokespecial #19; //Method java/lang/StringBuffer."":(Ljava/lang/String;)V - // // STACK: THIS/STRINGBUFFER - // // 55: aload_1 - // // STACK: THIS/STRINGBUFFER/LOCAL1 - // // 56: invokevirtual #22; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer; - // // STACK: THIS/STRINGBUFFER - // // 59: invokevirtual #34; //Method java/lang/StringBuffer.toString:()Ljava/lang/String; - // // STACK: THIS/STRING - // // 62: putfield #36; //Field value:Ljava/lang/String; - // // STACK: - // // 65: return - // - // // if we attempt to match on the ctor call to StringBuffer. then we get into trouble. - // // if we simply delete the new/dup pair without fixing up the dup_x2 then the dup_x2 will fail due to there - // // not being 3 elements on the stack for it to work with. The fix *in this situation* is to change it to - // // a simple 'dup' - // - // // this fix is *not* very clean - but a general purpose decent solution will take much longer and this - // // bytecode sequence has only been seen once in the wild. - // ih.setInstruction(InstructionConstants.DUP); - - /** - * The new/dup (or new/dup_x1/swap) are removed and will be readded later (after the advice call) by the caller of this method. - * The groovy compiler produces unusual code where the new/dup isn't visible (when making a this() call from an existing ctor), - * an aload_0 is used to load the uninitialized object (as an example see the ctors in grails.util.BuildSettings). - * - * @return true if managed to remove them - */ - private boolean deleteNewAndDup() { - final ConstantPool cpool = getEnclosingClass().getConstantPool(); - int depth = 1; - InstructionHandle ih = range.getStart(); - - // Go back from where we are looking for 'NEW' that takes us to a stack depth of 0. INVOKESPECIAL - while (ih != null) { - Instruction inst = ih.getInstruction(); - if (inst.opcode == Constants.INVOKESPECIAL && ((InvokeInstruction) inst).getName(cpool).equals("")) { - depth++; - } else if (inst.opcode == Constants.NEW) { - depth--; - if (depth == 0) { - break; - } - // need a testcase to show this can really happen in a modern compiler - removed due to 315398 - moved this out to - // comment proceeding this method: - - } - ih = ih.getPrev(); - } - if (ih == null) { - return false; - } - // now IH points to the NEW. We're followed by the DUP, and that is followed - // by the actual instruction we care about. - InstructionHandle newHandle = ih; - InstructionHandle endHandle = newHandle.getNext(); - InstructionHandle nextHandle; - if (endHandle.getInstruction().opcode == Constants.DUP) { - nextHandle = endHandle.getNext(); - retargetFrom(newHandle, nextHandle); - retargetFrom(endHandle, nextHandle); - } else if (endHandle.getInstruction().opcode == Constants.DUP_X1) { - InstructionHandle dupHandle = endHandle; - endHandle = endHandle.getNext(); - nextHandle = endHandle.getNext(); - boolean skipEndRepositioning = false; - if (endHandle.getInstruction().opcode == Constants.SWAP) { - } else if (endHandle.getInstruction().opcode == Constants.IMPDEP1) { - skipEndRepositioning = true; // pr186884 - } else { - // XXX see next XXX comment - throw new RuntimeException("Unhandled kind of new " + endHandle); - } - // Now make any jumps to the 'new', the 'dup' or the 'end' now target the nextHandle - retargetFrom(newHandle, nextHandle); - retargetFrom(dupHandle, nextHandle); - if (!skipEndRepositioning) { - retargetFrom(endHandle, nextHandle); - } - } else { - endHandle = newHandle; - nextHandle = endHandle.getNext(); - retargetFrom(newHandle, nextHandle); - // add a POP here... we found a NEW w/o a dup or anything else, so - // we must be in statement context. - getRange().insert(InstructionConstants.POP, Range.OutsideAfter); - } - // assert (dupHandle.getInstruction() instanceof DUP); - - try { - range.getBody().delete(newHandle, endHandle); - } catch (TargetLostException e) { - throw new BCException("shouldn't happen"); - } - return true; - } - - private void retargetFrom(InstructionHandle old, InstructionHandle fresh) { - for (InstructionTargeter targeter : old.getTargetersCopy()) { - if (targeter instanceof ExceptionRange) { - ExceptionRange it = (ExceptionRange) targeter; - it.updateTarget(old, fresh, it.getBody()); - } else { - targeter.updateTarget(old, fresh); - } - } - } - - // records advice that is stopping us doing the lazyTjp optimization - private List badAdvice = null; - - public void addAdvicePreventingLazyTjp(BcelAdvice advice) { - if (badAdvice == null) { - badAdvice = new ArrayList(); - } - badAdvice.add(advice); - } - - @Override - protected void prepareForMungers() { - // if we're a constructor call, we need to remove the new:dup or the new:dup_x1:swap, - // and store all our arguments on the frame. - - // ??? This is a bit of a hack (for the Java langauge). We do this because - // we sometime add code "outsideBefore" when dealing with weaving join points. We only - // do this for exposing state that is on the stack. It turns out to just work for - // everything except for constructor calls and exception handlers. If we were to clean - // this up, every ShadowRange would have three instructionHandle points, the start of - // the arg-setup code, the start of the running code, and the end of the running code. - boolean deletedNewAndDup = true; - if (getKind() == ConstructorCall) { - if (!world.isJoinpointArrayConstructionEnabled() || !this.getSignature().getDeclaringType().isArray()) { - deletedNewAndDup = deleteNewAndDup(); // no new/dup for new array construction - } - initializeArgVars(); - } else if (getKind() == PreInitialization) { // pr74952 - ShadowRange range = getRange(); - range.insert(InstructionConstants.NOP, Range.InsideAfter); - } else if (getKind() == ExceptionHandler) { - - ShadowRange range = getRange(); - InstructionList body = range.getBody(); - InstructionHandle start = range.getStart(); - - // Create a store instruction to put the value from the top of the - // stack into a local variable slot. This is a trimmed version of - // what is in initializeArgVars() (since there is only one argument - // at a handler jp and only before advice is supported) (pr46298) - argVars = new BcelVar[1]; - // int positionOffset = (hasTarget() ? 1 : 0) + ((hasThis() && !getKind().isTargetSameAsThis()) ? 1 : 0); - UnresolvedType tx = getArgType(0); - argVars[0] = genTempVar(tx, "ajc$arg0"); - InstructionHandle insertedInstruction = range.insert(argVars[0].createStore(getFactory()), Range.OutsideBefore); - - // Now the exception range starts just after our new instruction. - // The next bit of code changes the exception range to point at - // the store instruction - for (InstructionTargeter t : start.getTargetersCopy()) { - if (t instanceof ExceptionRange) { - ExceptionRange er = (ExceptionRange) t; - er.updateTarget(start, insertedInstruction, body); - } - } - } - - // now we ask each munger to request our state - isThisJoinPointLazy = true;// world.isXlazyTjp(); // lazy is default now - - badAdvice = null; - for (ShadowMunger munger : mungers) { - munger.specializeOn(this); - } - - initializeThisJoinPoint(); - - if (thisJoinPointVar != null && !isThisJoinPointLazy && badAdvice != null && badAdvice.size() > 1) { - // something stopped us making it a lazy tjp - // can't build tjp lazily, no suitable test... - int valid = 0; - for (Iterator iter = badAdvice.iterator(); iter.hasNext();) { - BcelAdvice element = iter.next(); - ISourceLocation sLoc = element.getSourceLocation(); - if (sLoc != null && sLoc.getLine() > 0) { - valid++; - } - } - if (valid != 0) { - ISourceLocation[] badLocs = new ISourceLocation[valid]; - int i = 0; - for (Iterator iter = badAdvice.iterator(); iter.hasNext();) { - BcelAdvice element = iter.next(); - ISourceLocation sLoc = element.getSourceLocation(); - if (sLoc != null) { - badLocs[i++] = sLoc; - } - } - world.getLint().multipleAdviceStoppingLazyTjp - .signal(new String[] { this.toString() }, getSourceLocation(), badLocs); - } - } - badAdvice = null; - - // If we are an expression kind, we require our target/arguments on the stack - // before we do our actual thing. However, they may have been removed - // from the stack as the shadowMungers have requested state. - // if any of our shadowMungers requested either the arguments or target, - // the munger will have added code - // to pop the target/arguments into temporary variables, represented by - // targetVar and argVars. In such a case, we must make sure to re-push the - // values. - - // If we are nonExpressionKind, we don't expect arguments on the stack - // so this is moot. If our argVars happen to be null, then we know that - // no ShadowMunger has squirrelled away our arguments, so they're still - // on the stack. - InstructionFactory fact = getFactory(); - if (getKind().argsOnStack() && argVars != null) { - - // Special case first (pr46298). If we are an exception handler and the instruction - // just after the shadow is a POP then we should remove the pop. The code - // above which generated the store instruction has already cleared the stack. - // We also don't generate any code for the arguments in this case as it would be - // an incorrect aload. - if (getKind() == ExceptionHandler && range.getEnd().getNext().getInstruction().equals(InstructionConstants.POP)) { - // easier than deleting it ... - range.getEnd().getNext().setInstruction(InstructionConstants.NOP); - } else { - range.insert(BcelRenderer.renderExprs(fact, world, argVars), Range.InsideBefore); - if (targetVar != null) { - range.insert(BcelRenderer.renderExpr(fact, world, targetVar), Range.InsideBefore); - } - if (getKind() == ConstructorCall) { - if (!world.isJoinpointArrayConstructionEnabled() || !this.getSignature().getDeclaringType().isArray()) { - if (deletedNewAndDup) { // if didnt delete them, dont insert any! - range.insert(InstructionFactory.createDup(1), Range.InsideBefore); - range.insert(fact.createNew((ObjectType) BcelWorld.makeBcelType(getSignature().getDeclaringType())), - Range.InsideBefore); - } - } - } - } - } - } - - // ---- getters - - public ShadowRange getRange() { - return range; - } - - public void setRange(ShadowRange range) { - this.range = range; - } - - private int sourceline = -1; - - public int getSourceLine() { - // if the kind of join point for which we are a shadow represents - // a method or constructor execution, then the best source line is - // the one from the enclosingMethod declarationLineNumber if available. - if (sourceline != -1) { - return sourceline; - } - Kind kind = getKind(); - if ((kind == MethodExecution) || (kind == ConstructorExecution) || (kind == AdviceExecution) - || (kind == StaticInitialization) || (kind == PreInitialization) || (kind == Initialization)) { - if (getEnclosingMethod().hasDeclaredLineNumberInfo()) { - sourceline = getEnclosingMethod().getDeclarationLineNumber(); - return sourceline; - } - } - - if (range == null) { - if (getEnclosingMethod().hasBody()) { - sourceline = Utility.getSourceLine(getEnclosingMethod().getBody().getStart()); - return sourceline; - } else { - sourceline = 0; - return sourceline; - } - } - sourceline = Utility.getSourceLine(range.getStart()); - if (sourceline < 0) { - sourceline = 0; - } - return sourceline; - } - - @Override - public ResolvedType getEnclosingType() { - return getEnclosingClass().getType(); - } - - public LazyClassGen getEnclosingClass() { - return enclosingMethod.getEnclosingClass(); - } - - public BcelWorld getWorld() { - return world; - } - - // ---- factory methods - - public static BcelShadow makeConstructorExecution(BcelWorld world, LazyMethodGen enclosingMethod, - InstructionHandle justBeforeStart) { - final InstructionList body = enclosingMethod.getBody(); - BcelShadow s = new BcelShadow(world, ConstructorExecution, world.makeJoinPointSignatureFromMethod(enclosingMethod, - Member.CONSTRUCTOR), enclosingMethod, null); - ShadowRange r = new ShadowRange(body); - r.associateWithShadow(s); - r.associateWithTargets(Range.genStart(body, justBeforeStart.getNext()), Range.genEnd(body)); - return s; - } - - public static BcelShadow makeStaticInitialization(BcelWorld world, LazyMethodGen enclosingMethod) { - InstructionList body = enclosingMethod.getBody(); - // move the start past ajc$preClinit - InstructionHandle clinitStart = body.getStart(); - if (clinitStart.getInstruction() instanceof InvokeInstruction) { - InvokeInstruction ii = (InvokeInstruction) clinitStart.getInstruction(); - if (ii.getName(enclosingMethod.getEnclosingClass().getConstantPool()).equals(NameMangler.AJC_PRE_CLINIT_NAME)) { - clinitStart = clinitStart.getNext(); - } - } - - InstructionHandle clinitEnd = body.getEnd(); - - // XXX should move the end before the postClinit, but the return is then tricky... - // if (clinitEnd.getInstruction() instanceof InvokeInstruction) { - // InvokeInstruction ii = (InvokeInstruction)clinitEnd.getInstruction(); - // if (ii.getName(enclosingMethod.getEnclosingClass().getConstantPool()).equals(NameMangler.AJC_POST_CLINIT_NAME)) { - // clinitEnd = clinitEnd.getPrev(); - // } - // } - - BcelShadow s = new BcelShadow(world, StaticInitialization, world.makeJoinPointSignatureFromMethod(enclosingMethod, - Member.STATIC_INITIALIZATION), enclosingMethod, null); - ShadowRange r = new ShadowRange(body); - r.associateWithShadow(s); - r.associateWithTargets(Range.genStart(body, clinitStart), Range.genEnd(body, clinitEnd)); - return s; - } - - /** - * Make the shadow for an exception handler. Currently makes an empty shadow that only allows before advice to be woven into it. - */ - - public static BcelShadow makeExceptionHandler(BcelWorld world, ExceptionRange exceptionRange, LazyMethodGen enclosingMethod, - InstructionHandle startOfHandler, BcelShadow enclosingShadow) { - InstructionList body = enclosingMethod.getBody(); - UnresolvedType catchType = exceptionRange.getCatchType(); - UnresolvedType inType = enclosingMethod.getEnclosingClass().getType(); - - ResolvedMemberImpl sig = MemberImpl.makeExceptionHandlerSignature(inType, catchType); - sig.setParameterNames(new String[] { findHandlerParamName(startOfHandler) }); - - BcelShadow s = new BcelShadow(world, ExceptionHandler, sig, enclosingMethod, enclosingShadow); - ShadowRange r = new ShadowRange(body); - r.associateWithShadow(s); - InstructionHandle start = Range.genStart(body, startOfHandler); - InstructionHandle end = Range.genEnd(body, start); - - r.associateWithTargets(start, end); - exceptionRange.updateTarget(startOfHandler, start, body); - return s; - } - - private static String findHandlerParamName(InstructionHandle startOfHandler) { - if (startOfHandler.getInstruction().isStoreInstruction() && startOfHandler.getNext() != null) { - int slot = startOfHandler.getInstruction().getIndex(); - // System.out.println("got store: " + startOfHandler.getInstruction() + ", " + index); - Iterator tIter = startOfHandler.getNext().getTargeters().iterator(); - while (tIter.hasNext()) { - InstructionTargeter targeter = tIter.next(); - if (targeter instanceof LocalVariableTag) { - LocalVariableTag t = (LocalVariableTag) targeter; - if (t.getSlot() == slot) { - return t.getName(); - } - } - } - } - - return ""; - } - - /** create an init join point associated w/ an interface in the body of a constructor */ - - public static BcelShadow makeIfaceInitialization(BcelWorld world, LazyMethodGen constructor, - Member interfaceConstructorSignature) { - // this call marks the instruction list as changed - constructor.getBody(); - // UnresolvedType inType = constructor.getEnclosingClass().getType(); - BcelShadow s = new BcelShadow(world, Initialization, interfaceConstructorSignature, constructor, null); - // s.fallsThrough = true; - // ShadowRange r = new ShadowRange(body); - // r.associateWithShadow(s); - // InstructionHandle start = Range.genStart(body, handle); - // InstructionHandle end = Range.genEnd(body, handle); - // - // r.associateWithTargets(start, end); - return s; - } - - public void initIfaceInitializer(InstructionHandle end) { - final InstructionList body = enclosingMethod.getBody(); - ShadowRange r = new ShadowRange(body); - r.associateWithShadow(this); - InstructionHandle nop = body.insert(end, InstructionConstants.NOP); - - r.associateWithTargets(Range.genStart(body, nop), Range.genEnd(body, nop)); - } - - // public static BcelShadow makeIfaceConstructorExecution( - // BcelWorld world, - // LazyMethodGen constructor, - // InstructionHandle next, - // Member interfaceConstructorSignature) - // { - // // final InstructionFactory fact = constructor.getEnclosingClass().getFactory(); - // InstructionList body = constructor.getBody(); - // // UnresolvedType inType = constructor.getEnclosingClass().getType(); - // BcelShadow s = - // new BcelShadow( - // world, - // ConstructorExecution, - // interfaceConstructorSignature, - // constructor, - // null); - // s.fallsThrough = true; - // ShadowRange r = new ShadowRange(body); - // r.associateWithShadow(s); - // // ??? this may or may not work - // InstructionHandle start = Range.genStart(body, next); - // //InstructionHandle end = Range.genEnd(body, body.append(start, fact.NOP)); - // InstructionHandle end = Range.genStart(body, next); - // //body.append(start, fact.NOP); - // - // r.associateWithTargets(start, end); - // return s; - // } - - /** - * Create an initialization join point associated with a constructor, but not with any body of code yet. If this is actually - * matched, it's range will be set when we inline self constructors. - * - * @param constructor The constructor starting this initialization. - */ - public static BcelShadow makeUnfinishedInitialization(BcelWorld world, LazyMethodGen constructor) { - BcelShadow ret = new BcelShadow(world, Initialization, world.makeJoinPointSignatureFromMethod(constructor, - Member.CONSTRUCTOR), constructor, null); - if (constructor.getEffectiveSignature() != null) { - ret.setMatchingSignature(constructor.getEffectiveSignature().getEffectiveSignature()); - } - return ret; - } - - public static BcelShadow makeUnfinishedPreinitialization(BcelWorld world, LazyMethodGen constructor) { - BcelShadow ret = new BcelShadow(world, PreInitialization, world.makeJoinPointSignatureFromMethod(constructor, - Member.CONSTRUCTOR), constructor, null); - if (constructor.getEffectiveSignature() != null) { - ret.setMatchingSignature(constructor.getEffectiveSignature().getEffectiveSignature()); - } - return ret; - } - - public static BcelShadow makeMethodExecution(BcelWorld world, LazyMethodGen enclosingMethod, boolean lazyInit) { - if (!lazyInit) { - return makeMethodExecution(world, enclosingMethod); - } - - BcelShadow s = new BcelShadow(world, MethodExecution, enclosingMethod.getMemberView(), enclosingMethod, null); - - return s; - } - - public void init() { - if (range != null) { - return; - } - - final InstructionList body = enclosingMethod.getBody(); - ShadowRange r = new ShadowRange(body); - r.associateWithShadow(this); - r.associateWithTargets(Range.genStart(body), Range.genEnd(body)); - } - - public static BcelShadow makeMethodExecution(BcelWorld world, LazyMethodGen enclosingMethod) { - return makeShadowForMethod(world, enclosingMethod, MethodExecution, enclosingMethod.getMemberView()); - } - - public static BcelShadow makeShadowForMethod(BcelWorld world, LazyMethodGen enclosingMethod, Shadow.Kind kind, Member sig) { - final InstructionList body = enclosingMethod.getBody(); - BcelShadow s = new BcelShadow(world, kind, sig, enclosingMethod, null); - ShadowRange r = new ShadowRange(body); - r.associateWithShadow(s); - r.associateWithTargets(// OPTIMIZE this occurs lots of times for all jp kinds... - Range.genStart(body), Range.genEnd(body)); - return s; - } - - public static BcelShadow makeAdviceExecution(BcelWorld world, LazyMethodGen enclosingMethod) { - final InstructionList body = enclosingMethod.getBody(); - BcelShadow s = new BcelShadow(world, AdviceExecution, - world.makeJoinPointSignatureFromMethod(enclosingMethod, Member.ADVICE), enclosingMethod, null); - ShadowRange r = new ShadowRange(body); - r.associateWithShadow(s); - r.associateWithTargets(Range.genStart(body), Range.genEnd(body)); - return s; - } - - // constructor call shadows are initially just around the - // call to the constructor. If ANY advice gets put on it, we move - // the NEW instruction inside the join point, which involves putting - // all the arguments in temps. - public static BcelShadow makeConstructorCall(BcelWorld world, LazyMethodGen enclosingMethod, InstructionHandle callHandle, - BcelShadow enclosingShadow) { - final InstructionList body = enclosingMethod.getBody(); - - Member sig = world.makeJoinPointSignatureForMethodInvocation(enclosingMethod.getEnclosingClass(), - (InvokeInstruction) callHandle.getInstruction()); - - BcelShadow s = new BcelShadow(world, ConstructorCall, sig, enclosingMethod, enclosingShadow); - ShadowRange r = new ShadowRange(body); - r.associateWithShadow(s); - r.associateWithTargets(Range.genStart(body, callHandle), Range.genEnd(body, callHandle)); - retargetAllBranches(callHandle, r.getStart()); - return s; - } - - public static BcelShadow makeArrayConstructorCall(BcelWorld world, LazyMethodGen enclosingMethod, - InstructionHandle arrayInstruction, BcelShadow enclosingShadow) { - final InstructionList body = enclosingMethod.getBody(); - Member sig = world.makeJoinPointSignatureForArrayConstruction(enclosingMethod.getEnclosingClass(), arrayInstruction); - BcelShadow s = new BcelShadow(world, ConstructorCall, sig, enclosingMethod, enclosingShadow); - ShadowRange r = new ShadowRange(body); - r.associateWithShadow(s); - r.associateWithTargets(Range.genStart(body, arrayInstruction), Range.genEnd(body, arrayInstruction)); - retargetAllBranches(arrayInstruction, r.getStart()); - return s; - } - - public static BcelShadow makeMonitorEnter(BcelWorld world, LazyMethodGen enclosingMethod, InstructionHandle monitorInstruction, - BcelShadow enclosingShadow) { - final InstructionList body = enclosingMethod.getBody(); - Member sig = world.makeJoinPointSignatureForMonitorEnter(enclosingMethod.getEnclosingClass(), monitorInstruction); - BcelShadow s = new BcelShadow(world, SynchronizationLock, sig, enclosingMethod, enclosingShadow); - ShadowRange r = new ShadowRange(body); - r.associateWithShadow(s); - r.associateWithTargets(Range.genStart(body, monitorInstruction), Range.genEnd(body, monitorInstruction)); - retargetAllBranches(monitorInstruction, r.getStart()); - return s; - } - - public static BcelShadow makeMonitorExit(BcelWorld world, LazyMethodGen enclosingMethod, InstructionHandle monitorInstruction, - BcelShadow enclosingShadow) { - final InstructionList body = enclosingMethod.getBody(); - Member sig = world.makeJoinPointSignatureForMonitorExit(enclosingMethod.getEnclosingClass(), monitorInstruction); - BcelShadow s = new BcelShadow(world, SynchronizationUnlock, sig, enclosingMethod, enclosingShadow); - ShadowRange r = new ShadowRange(body); - r.associateWithShadow(s); - r.associateWithTargets(Range.genStart(body, monitorInstruction), Range.genEnd(body, monitorInstruction)); - retargetAllBranches(monitorInstruction, r.getStart()); - return s; - } - - // see pr77166 - // public static BcelShadow makeArrayLoadCall( - // BcelWorld world, - // LazyMethodGen enclosingMethod, - // InstructionHandle arrayInstruction, - // BcelShadow enclosingShadow) - // { - // final InstructionList body = enclosingMethod.getBody(); - // Member sig = world.makeJoinPointSignatureForArrayLoad(enclosingMethod.getEnclosingClass(),arrayInstruction); - // BcelShadow s = - // new BcelShadow( - // world, - // MethodCall, - // sig, - // enclosingMethod, - // enclosingShadow); - // ShadowRange r = new ShadowRange(body); - // r.associateWithShadow(s); - // r.associateWithTargets( - // Range.genStart(body, arrayInstruction), - // Range.genEnd(body, arrayInstruction)); - // retargetAllBranches(arrayInstruction, r.getStart()); - // return s; - // } - - public static BcelShadow makeMethodCall(BcelWorld world, LazyMethodGen enclosingMethod, InstructionHandle callHandle, - BcelShadow enclosingShadow) { - final InstructionList body = enclosingMethod.getBody(); - BcelShadow s = new BcelShadow(world, MethodCall, world.makeJoinPointSignatureForMethodInvocation( - enclosingMethod.getEnclosingClass(), (InvokeInstruction) callHandle.getInstruction()), enclosingMethod, - enclosingShadow); - ShadowRange r = new ShadowRange(body); - r.associateWithShadow(s); - r.associateWithTargets(Range.genStart(body, callHandle), Range.genEnd(body, callHandle)); - retargetAllBranches(callHandle, r.getStart()); - return s; - } - - public static BcelShadow makeShadowForMethodCall(BcelWorld world, LazyMethodGen enclosingMethod, InstructionHandle callHandle, - BcelShadow enclosingShadow, Kind kind, ResolvedMember sig) { - final InstructionList body = enclosingMethod.getBody(); - BcelShadow s = new BcelShadow(world, kind, sig, enclosingMethod, enclosingShadow); - ShadowRange r = new ShadowRange(body); - r.associateWithShadow(s); - r.associateWithTargets(Range.genStart(body, callHandle), Range.genEnd(body, callHandle)); - retargetAllBranches(callHandle, r.getStart()); - return s; - } - - public static BcelShadow makeFieldGet(BcelWorld world, ResolvedMember field, LazyMethodGen enclosingMethod, - InstructionHandle getHandle, BcelShadow enclosingShadow) { - final InstructionList body = enclosingMethod.getBody(); - BcelShadow s = new BcelShadow(world, FieldGet, field, - // BcelWorld.makeFieldSignature( - // enclosingMethod.getEnclosingClass(), - // (FieldInstruction) getHandle.getInstruction()), - enclosingMethod, enclosingShadow); - ShadowRange r = new ShadowRange(body); - r.associateWithShadow(s); - r.associateWithTargets(Range.genStart(body, getHandle), Range.genEnd(body, getHandle)); - retargetAllBranches(getHandle, r.getStart()); - return s; - } - - public static BcelShadow makeFieldSet(BcelWorld world, ResolvedMember field, LazyMethodGen enclosingMethod, - InstructionHandle setHandle, BcelShadow enclosingShadow) { - final InstructionList body = enclosingMethod.getBody(); - BcelShadow s = new BcelShadow(world, FieldSet, field, - // BcelWorld.makeFieldJoinPointSignature( - // enclosingMethod.getEnclosingClass(), - // (FieldInstruction) setHandle.getInstruction()), - enclosingMethod, enclosingShadow); - ShadowRange r = new ShadowRange(body); - r.associateWithShadow(s); - r.associateWithTargets(Range.genStart(body, setHandle), Range.genEnd(body, setHandle)); - retargetAllBranches(setHandle, r.getStart()); - return s; - } - - public static void retargetAllBranches(InstructionHandle from, InstructionHandle to) { - for (InstructionTargeter source : from.getTargetersCopy()) { - if (source instanceof InstructionBranch) { - source.updateTarget(from, to); - } - } - } - - // // ---- type access methods - // private ObjectType getTargetBcelType() { - // return (ObjectType) BcelWorld.makeBcelType(getTargetType()); - // } - // private Type getArgBcelType(int arg) { - // return BcelWorld.makeBcelType(getArgType(arg)); - // } - - // ---- kinding - - /** - * If the end of my range has no real instructions following then my context needs a return at the end. - */ - public boolean terminatesWithReturn() { - return getRange().getRealNext() == null; - } - - /** - * Is arg0 occupied with the value of this - */ - public boolean arg0HoldsThis() { - if (getKind().isEnclosingKind()) { - return !Modifier.isStatic(getSignature().getModifiers()); - } else if (enclosingShadow == null) { - // XXX this is mostly right - // this doesn't do the right thing for calls in the pre part of introduced constructors. - return !enclosingMethod.isStatic(); - } else { - return ((BcelShadow) enclosingShadow).arg0HoldsThis(); - } - } - - // ---- argument getting methods - - private BcelVar thisVar = null; - private BcelVar targetVar = null; - private BcelVar[] argVars = null; - private Map kindedAnnotationVars = null; - private Map thisAnnotationVars = null; - private Map targetAnnotationVars = null; - // private Map/* */[] argAnnotationVars = null; - private Map withinAnnotationVars = null; - private Map withincodeAnnotationVars = null; - private boolean allArgVarsInitialized = false; - - @Override - public Var getThisVar() { - if (!hasThis()) { - throw new IllegalStateException("no this"); - } - initializeThisVar(); - return thisVar; - } - - @Override - public Var getThisAnnotationVar(UnresolvedType forAnnotationType) { - if (!hasThis()) { - throw new IllegalStateException("no this"); - } - initializeThisAnnotationVars(); // FIXME asc Why bother with this if we always return one? - // Even if we can't find one, we have to return one as we might have this annotation at runtime - Var v = thisAnnotationVars.get(forAnnotationType); - if (v == null) { - v = new TypeAnnotationAccessVar(forAnnotationType.resolve(world), (BcelVar) getThisVar()); - } - return v; - } - - @Override - public Var getTargetVar() { - if (!hasTarget()) { - throw new IllegalStateException("no target"); - } - initializeTargetVar(); - return targetVar; - } - - @Override - public Var getTargetAnnotationVar(UnresolvedType forAnnotationType) { - if (!hasTarget()) { - throw new IllegalStateException("no target"); - } - initializeTargetAnnotationVars(); // FIXME asc why bother with this if we always return one? - Var v = targetAnnotationVars.get(forAnnotationType); - // Even if we can't find one, we have to return one as we might have this annotation at runtime - if (v == null) { - v = new TypeAnnotationAccessVar(forAnnotationType.resolve(world), (BcelVar) getTargetVar()); - } - return v; - } - - @Override - public Var getArgVar(int i) { - ensureInitializedArgVar(i); - return argVars[i]; - } - - @Override - public Var getArgAnnotationVar(int i, UnresolvedType forAnnotationType) { - return new TypeAnnotationAccessVar(forAnnotationType.resolve(world), (BcelVar) getArgVar(i)); - // initializeArgAnnotationVars(); - // - // Var v = (Var) argAnnotationVars[i].get(forAnnotationType); - // if (v == null) { - // v = new TypeAnnotationAccessVar(forAnnotationType.resolve(world), (BcelVar) getArgVar(i)); - // } - // return v; - } - - @Override - public Var getKindedAnnotationVar(UnresolvedType forAnnotationType) { - initializeKindedAnnotationVars(); - return kindedAnnotationVars.get(forAnnotationType); - } - - @Override - public Var getWithinAnnotationVar(UnresolvedType forAnnotationType) { - initializeWithinAnnotationVars(); - return withinAnnotationVars.get(forAnnotationType); - } - - @Override - public Var getWithinCodeAnnotationVar(UnresolvedType forAnnotationType) { - initializeWithinCodeAnnotationVars(); - return withincodeAnnotationVars.get(forAnnotationType); - } - - // reflective thisJoinPoint support - private BcelVar thisJoinPointVar = null; - private boolean isThisJoinPointLazy; - private int lazyTjpConsumers = 0; - private BcelVar thisJoinPointStaticPartVar = null; - - // private BcelVar thisEnclosingJoinPointStaticPartVar = null; - - @Override - public final Var getThisJoinPointStaticPartVar() { - return getThisJoinPointStaticPartBcelVar(); - } - - @Override - public final Var getThisEnclosingJoinPointStaticPartVar() { - return getThisEnclosingJoinPointStaticPartBcelVar(); - } - - public void requireThisJoinPoint(boolean hasGuardTest, boolean isAround) { - if (!isAround) { - if (!hasGuardTest) { - isThisJoinPointLazy = false; - } else { - lazyTjpConsumers++; - } - } - // if (!hasGuardTest) { - // isThisJoinPointLazy = false; - // } else { - // lazyTjpConsumers++; - // } - if (thisJoinPointVar == null) { - thisJoinPointVar = genTempVar(UnresolvedType.forName("org.aspectj.lang.JoinPoint")); - } - } - - @Override - public Var getThisJoinPointVar() { - requireThisJoinPoint(false, false); - return thisJoinPointVar; - } - - void initializeThisJoinPoint() { - if (thisJoinPointVar == null) { - return; - } - - if (isThisJoinPointLazy) { - isThisJoinPointLazy = checkLazyTjp(); - } - - if (isThisJoinPointLazy) { - appliedLazyTjpOptimization = true; - createThisJoinPoint(); // make sure any state needed is initialized, but throw the instructions out - - if (lazyTjpConsumers == 1) { - return; // special case only one lazyTjpUser - } - - InstructionFactory fact = getFactory(); - InstructionList il = new InstructionList(); - il.append(InstructionConstants.ACONST_NULL); - il.append(thisJoinPointVar.createStore(fact)); - range.insert(il, Range.OutsideBefore); - } else { - appliedLazyTjpOptimization = false; - InstructionFactory fact = getFactory(); - InstructionList il = createThisJoinPoint(); - il.append(thisJoinPointVar.createStore(fact)); - range.insert(il, Range.OutsideBefore); - } - } - - private boolean checkLazyTjp() { - // check for around advice - for (Iterator i = mungers.iterator(); i.hasNext();) { - ShadowMunger munger = i.next(); - if (munger instanceof Advice) { - if (((Advice) munger).getKind() == AdviceKind.Around) { - if (munger.getSourceLocation() != null) { // do we know enough to bother reporting? - if (world.getLint().canNotImplementLazyTjp.isEnabled()) { - world.getLint().canNotImplementLazyTjp.signal(new String[] { toString() }, getSourceLocation(), - new ISourceLocation[] { munger.getSourceLocation() }); - } - } - return false; - } - } - } - - return true; - } - - InstructionList loadThisJoinPoint() { - InstructionFactory fact = getFactory(); - InstructionList il = new InstructionList(); - - if (isThisJoinPointLazy) { - // If we're lazy, build the join point right here. - il.append(createThisJoinPoint()); - - // Does someone else need it? If so, store it for later retrieval - if (lazyTjpConsumers > 1) { - il.append(thisJoinPointVar.createStore(fact)); - - InstructionHandle end = il.append(thisJoinPointVar.createLoad(fact)); - - il.insert(InstructionFactory.createBranchInstruction(Constants.IFNONNULL, end)); - il.insert(thisJoinPointVar.createLoad(fact)); - } - } else { - // If not lazy, its already been built and stored, just retrieve it - thisJoinPointVar.appendLoad(il, fact); - } - - return il; - } - - InstructionList createThisJoinPoint() { - InstructionFactory fact = getFactory(); - InstructionList il = new InstructionList(); - - BcelVar staticPart = getThisJoinPointStaticPartBcelVar(); - staticPart.appendLoad(il, fact); - if (hasThis()) { - ((BcelVar) getThisVar()).appendLoad(il, fact); - } else { - il.append(InstructionConstants.ACONST_NULL); - } - if (hasTarget()) { - ((BcelVar) getTargetVar()).appendLoad(il, fact); - } else { - il.append(InstructionConstants.ACONST_NULL); - } - - switch (getArgCount()) { - case 0: - il.append(fact.createInvoke("org.aspectj.runtime.reflect.Factory", "makeJP", LazyClassGen.tjpType, new Type[] { - LazyClassGen.staticTjpType, Type.OBJECT, Type.OBJECT }, Constants.INVOKESTATIC)); - break; - case 1: - ((BcelVar) getArgVar(0)).appendLoadAndConvert(il, fact, world.getCoreType(ResolvedType.OBJECT)); - il.append(fact.createInvoke("org.aspectj.runtime.reflect.Factory", "makeJP", LazyClassGen.tjpType, new Type[] { - LazyClassGen.staticTjpType, Type.OBJECT, Type.OBJECT, Type.OBJECT }, Constants.INVOKESTATIC)); - break; - case 2: - ((BcelVar) getArgVar(0)).appendLoadAndConvert(il, fact, world.getCoreType(ResolvedType.OBJECT)); - ((BcelVar) getArgVar(1)).appendLoadAndConvert(il, fact, world.getCoreType(ResolvedType.OBJECT)); - il.append(fact.createInvoke("org.aspectj.runtime.reflect.Factory", "makeJP", LazyClassGen.tjpType, new Type[] { - LazyClassGen.staticTjpType, Type.OBJECT, Type.OBJECT, Type.OBJECT, Type.OBJECT }, Constants.INVOKESTATIC)); - break; - default: - il.append(makeArgsObjectArray()); - il.append(fact.createInvoke("org.aspectj.runtime.reflect.Factory", "makeJP", LazyClassGen.tjpType, new Type[] { - LazyClassGen.staticTjpType, Type.OBJECT, Type.OBJECT, new ArrayType(Type.OBJECT, 1) }, Constants.INVOKESTATIC)); - break; - } - - return il; - } - - public BcelVar getThisJoinPointStaticPartBcelVar() { - return getThisJoinPointStaticPartBcelVar(false); - } - - @Override - public BcelVar getThisAspectInstanceVar(ResolvedType aspectType) { - return new AspectInstanceVar(aspectType); - } - - /** - * Get the Var for the xxxxJpStaticPart, xxx = this or enclosing - * - * @param isEnclosingJp true to have the enclosingJpStaticPart - * @return - */ - public BcelVar getThisJoinPointStaticPartBcelVar(final boolean isEnclosingJp) { - if (thisJoinPointStaticPartVar == null) { - Field field = getEnclosingClass().getTjpField(this, isEnclosingJp); - ResolvedType sjpType = null; - if (world.isTargettingAspectJRuntime12()) { // TAG:SUPPORTING12: We didn't have different jpsp types in 1.2 - sjpType = world.getCoreType(UnresolvedType.JOINPOINT_STATICPART); - } else { - sjpType = isEnclosingJp ? world.getCoreType(UnresolvedType.JOINPOINT_ENCLOSINGSTATICPART) : world - .getCoreType(UnresolvedType.JOINPOINT_STATICPART); - } - thisJoinPointStaticPartVar = new BcelFieldRef(sjpType, getEnclosingClass().getClassName(), field.getName()); - // getEnclosingClass().warnOnAddedStaticInitializer(this,munger.getSourceLocation()); - } - return thisJoinPointStaticPartVar; - } - - /** - * Get the Var for the enclosingJpStaticPart - * - * @return - */ - public BcelVar getThisEnclosingJoinPointStaticPartBcelVar() { - if (enclosingShadow == null) { - // the enclosing of an execution is itself - return getThisJoinPointStaticPartBcelVar(true); - } else { - return ((BcelShadow) enclosingShadow).getThisJoinPointStaticPartBcelVar(true); - } - } - - // ??? need to better understand all the enclosing variants - @Override - public Member getEnclosingCodeSignature() { - if (getKind().isEnclosingKind()) { - return getSignature(); - } else if (getKind() == Shadow.PreInitialization) { - // PreInit doesn't enclose code but its signature - // is correctly the signature of the ctor. - return getSignature(); - } else if (enclosingShadow == null) { - return getEnclosingMethod().getMemberView(); - } else { - return enclosingShadow.getSignature(); - } - } - - public Member getRealEnclosingCodeSignature() { - return enclosingMethod.getMemberView(); - } - - // public Member getEnclosingCodeSignatureForModel() { - // if (getKind().isEnclosingKind()) { - // return getSignature(); - // } else if (getKind() == Shadow.PreInitialization) { - // // PreInit doesn't enclose code but its signature - // // is correctly the signature of the ctor. - // return getSignature(); - // } else if (enclosingShadow == null) { - // return getEnclosingMethod().getMemberView(); - // } else { - // if (enclosingShadow.getKind() == Shadow.MethodExecution && enclosingMethod.getEffectiveSignature() != null) { - // - // } else { - // return enclosingShadow.getSignature(); - // } - // } - // } - - private InstructionList makeArgsObjectArray() { - InstructionFactory fact = getFactory(); - BcelVar arrayVar = genTempVar(UnresolvedType.OBJECTARRAY); - final InstructionList il = new InstructionList(); - int alen = getArgCount(); - il.append(Utility.createConstant(fact, alen)); - il.append(fact.createNewArray(Type.OBJECT, (short) 1)); - arrayVar.appendStore(il, fact); - - int stateIndex = 0; - for (int i = 0, len = getArgCount(); i < len; i++) { - arrayVar.appendConvertableArrayStore(il, fact, stateIndex, (BcelVar) getArgVar(i)); - stateIndex++; - } - arrayVar.appendLoad(il, fact); - return il; - } - - // ---- initializing var tables - - /* - * initializing this is doesn't do anything, because this is protected from side-effects, so we don't need to copy its location - */ - - private void initializeThisVar() { - if (thisVar != null) { - return; - } - thisVar = new BcelVar(getThisType().resolve(world), 0); - thisVar.setPositionInAroundState(0); - } - - public void initializeTargetVar() { - InstructionFactory fact = getFactory(); - if (targetVar != null) { - return; - } - if (getKind().isTargetSameAsThis()) { - if (hasThis()) { - initializeThisVar(); - } - targetVar = thisVar; - } else { - initializeArgVars(); // gotta pop off the args before we find the target - UnresolvedType type = getTargetType(); - type = ensureTargetTypeIsCorrect(type); - targetVar = genTempVar(type, "ajc$target"); - range.insert(targetVar.createStore(fact), Range.OutsideBefore); - targetVar.setPositionInAroundState(hasThis() ? 1 : 0); - } - } - - /* - * PR 72528 This method double checks the target type under certain conditions. The Java 1.4 compilers seem to take calls to - * clone methods on array types and create bytecode that looks like clone is being called on Object. If we advise a clone call - * with around advice we extract the call into a helper method which we can then refer to. Because the type in the bytecode for - * the call to clone is Object we create a helper method with an Object parameter - this is not correct as we have lost the fact - * that the actual type is an array type. If we don't do the check below we will create code that fails java verification. This - * method checks for the peculiar set of conditions and if they are true, it has a sneak peek at the code before the call to see - * what is on the stack. - */ - public UnresolvedType ensureTargetTypeIsCorrect(UnresolvedType tx) { - - Member msig = getSignature(); - if (msig.getArity() == 0 && getKind() == MethodCall && msig.getName().charAt(0) == 'c' && tx.equals(ResolvedType.OBJECT) - && msig.getReturnType().equals(ResolvedType.OBJECT) && msig.getName().equals("clone")) { - - // Lets go back through the code from the start of the shadow - InstructionHandle searchPtr = range.getStart().getPrev(); - while (Range.isRangeHandle(searchPtr) || searchPtr.getInstruction().isStoreInstruction()) { // ignore this instruction - - // it doesnt give us the - // info we want - searchPtr = searchPtr.getPrev(); - } - - // A load instruction may tell us the real type of what the clone() call is on - if (searchPtr.getInstruction().isLoadInstruction()) { - LocalVariableTag lvt = LazyMethodGen.getLocalVariableTag(searchPtr, searchPtr.getInstruction().getIndex()); - if (lvt != null) { - return UnresolvedType.forSignature(lvt.getType()); - } - } - // A field access instruction may tell us the real type of what the clone() call is on - if (searchPtr.getInstruction() instanceof FieldInstruction) { - FieldInstruction si = (FieldInstruction) searchPtr.getInstruction(); - Type t = si.getFieldType(getEnclosingClass().getConstantPool()); - return BcelWorld.fromBcel(t); - } - // A new array instruction obviously tells us it is an array type ! - if (searchPtr.getInstruction().opcode == Constants.ANEWARRAY) { - // ANEWARRAY ana = (ANEWARRAY)searchPoint.getInstruction(); - // Type t = ana.getType(getEnclosingClass().getConstantPool()); - // Just use a standard java.lang.object array - that will work fine - return BcelWorld.fromBcel(new ArrayType(Type.OBJECT, 1)); - } - // A multi new array instruction obviously tells us it is an array type ! - if (searchPtr.getInstruction() instanceof MULTIANEWARRAY) { - MULTIANEWARRAY ana = (MULTIANEWARRAY) searchPtr.getInstruction(); - // Type t = ana.getType(getEnclosingClass().getConstantPool()); - // t = new ArrayType(t,ana.getDimensions()); - // Just use a standard java.lang.object array - that will work fine - return BcelWorld.fromBcel(new ArrayType(Type.OBJECT, ana.getDimensions())); - } - throw new BCException("Can't determine real target of clone() when processing instruction " - + searchPtr.getInstruction() + ". Perhaps avoid selecting clone with your pointcut?"); - } - return tx; - } - - public void ensureInitializedArgVar(int argNumber) { - if (allArgVarsInitialized || (argVars != null && argVars[argNumber] != null)) { - return; - } - InstructionFactory fact = getFactory(); - int len = getArgCount(); - if (argVars == null) { - argVars = new BcelVar[len]; - } - - // Need to initialize argument i - int positionOffset = (hasTarget() ? 1 : 0) + ((hasThis() && !getKind().isTargetSameAsThis()) ? 1 : 0); - - if (getKind().argsOnStack()) { - // Let's just do them all now since they are on the stack - // we move backwards because we're popping off the stack - for (int i = len - 1; i >= 0; i--) { - UnresolvedType type = getArgType(i); - BcelVar tmp = genTempVar(type, "ajc$arg" + i); - range.insert(tmp.createStore(getFactory()), Range.OutsideBefore); - int position = i; - position += positionOffset; - tmp.setPositionInAroundState(position); - argVars[i] = tmp; - } - allArgVarsInitialized = true; - } else { - int index = 0; - if (arg0HoldsThis()) { - index++; - } - boolean allInited = true; - for (int i = 0; i < len; i++) { - UnresolvedType type = getArgType(i); - if (i == argNumber) { - argVars[argNumber] = genTempVar(type, "ajc$arg" + argNumber); - range.insert(argVars[argNumber].createCopyFrom(fact, index), Range.OutsideBefore); - argVars[argNumber].setPositionInAroundState(argNumber + positionOffset); - } - allInited = allInited && argVars[i] != null; - index += type.getSize(); - } - if (allInited && (argNumber + 1) == len) { - allArgVarsInitialized = true; - } - } - } - - /** - * Initialize all the available arguments at the shadow. This means creating a copy of them that we can then use for advice - * calls (the copy ensures we are not affected by other advice changing the values). This method initializes all arguments - * whereas the method ensureInitializedArgVar will only ensure a single argument is setup. - */ - public void initializeArgVars() { - if (allArgVarsInitialized) { - return; - } - InstructionFactory fact = getFactory(); - int len = getArgCount(); - if (argVars == null) { - argVars = new BcelVar[len]; - } - int positionOffset = (hasTarget() ? 1 : 0) + ((hasThis() && !getKind().isTargetSameAsThis()) ? 1 : 0); - - if (getKind().argsOnStack()) { - // we move backwards because we're popping off the stack - for (int i = len - 1; i >= 0; i--) { - UnresolvedType type = getArgType(i); - BcelVar tmp = genTempVar(type, "ajc$arg" + i); - range.insert(tmp.createStore(getFactory()), Range.OutsideBefore); - int position = i; - position += positionOffset; - tmp.setPositionInAroundState(position); - argVars[i] = tmp; - } - } else { - int index = 0; - if (arg0HoldsThis()) { - index++; - } - - for (int i = 0; i < len; i++) { - UnresolvedType type = getArgType(i); - if (argVars[i] == null) { - BcelVar tmp = genTempVar(type, "ajc$arg" + i); - range.insert(tmp.createCopyFrom(fact, index), Range.OutsideBefore); - argVars[i] = tmp; - tmp.setPositionInAroundState(i + positionOffset); - } - index += type.resolve(world).getSize(); - } - } - allArgVarsInitialized = true; - - } - - public void initializeForAroundClosure() { - initializeArgVars(); - if (hasTarget()) { - initializeTargetVar(); - } - if (hasThis()) { - initializeThisVar(); - // System.out.println("initialized: " + this + " thisVar = " + thisVar); - } - } - - public void initializeThisAnnotationVars() { - if (thisAnnotationVars != null) { - return; - } - thisAnnotationVars = new HashMap(); - // populate.. - } - - public void initializeTargetAnnotationVars() { - if (targetAnnotationVars != null) { - return; - } - if (getKind().isTargetSameAsThis()) { - if (hasThis()) { - initializeThisAnnotationVars(); - } - targetAnnotationVars = thisAnnotationVars; - } else { - targetAnnotationVars = new HashMap(); - ResolvedType[] rtx = this.getTargetType().resolve(world).getAnnotationTypes(); // what about annotations we havent - // gotten yet but we will get in - // subclasses? - for (int i = 0; i < rtx.length; i++) { - ResolvedType typeX = rtx[i]; - targetAnnotationVars.put(typeX, new TypeAnnotationAccessVar(typeX, (BcelVar) getTargetVar())); - } - // populate. - } - } - - // public void initializeArgAnnotationVars() { - // if (argAnnotationVars != null) { - // return; - // } - // int numArgs = getArgCount(); - // argAnnotationVars = new Map[numArgs]; - // for (int i = 0; i < argAnnotationVars.length; i++) { - // argAnnotationVars[i] = new HashMap(); - // // FIXME asc just delete this logic - we always build the Var on demand, as we don't know at weave time - // // what the full set of annotations could be (due to static/dynamic type differences...) - // } - // } - - protected ResolvedMember getRelevantMember(ResolvedMember foundMember, Member relevantMember, ResolvedType relevantType) { - if (foundMember != null) { - return foundMember; - } - - foundMember = getSignature().resolve(world); - if (foundMember == null && relevantMember != null) { - foundMember = relevantType.lookupMemberWithSupersAndITDs(relevantMember); - } - - // check the ITD'd dooberries - List mungers = relevantType.resolve(world).getInterTypeMungers(); - for (ConcreteTypeMunger typeMunger : mungers) { - if (typeMunger.getMunger() instanceof NewMethodTypeMunger || typeMunger.getMunger() instanceof NewConstructorTypeMunger) { - ResolvedMember fakerm = typeMunger.getSignature(); - if (fakerm.getName().equals(getSignature().getName()) - && fakerm.getParameterSignature().equals(getSignature().getParameterSignature())) { - if (foundMember.getKind() == ResolvedMember.CONSTRUCTOR) { - foundMember = AjcMemberMaker.interConstructor(relevantType, foundMember, typeMunger.getAspectType()); - } else { - foundMember = AjcMemberMaker.interMethod(foundMember, typeMunger.getAspectType(), false); - // ResolvedMember o = AjcMemberMaker.interMethodBody(fakerm, typeMunger.getAspectType()); - // // Object os = o.getAnnotations(); - // ResolvedMember foundMember2 = findMethod(typeMunger.getAspectType(), o); - // Object os2 = foundMember2.getAnnotations(); - // int stop = 1; - // foundMember = foundMember2; - // foundMember = AjcMemberMaker.interMethod(foundMember, typeMunger.getAspectType()); - } - // in the above.. what about if it's on an Interface? Can that happen? - // then the last arg of the above should be true - return foundMember; - } - } - } - return foundMember; - } - - protected ResolvedType[] getAnnotations(ResolvedMember foundMember, Member relevantMember, ResolvedType relevantType) { - if (foundMember == null) { - // check the ITD'd dooberries - List mungers = relevantType.resolve(world).getInterTypeMungers(); - for (Iterator iter = mungers.iterator(); iter.hasNext();) { - Object munger = iter.next(); - ConcreteTypeMunger typeMunger = (ConcreteTypeMunger) munger; - if (typeMunger.getMunger() instanceof NewMethodTypeMunger - || typeMunger.getMunger() instanceof NewConstructorTypeMunger) { - ResolvedMember fakerm = typeMunger.getSignature(); - // if (fakerm.hasAnnotations()) - - ResolvedMember ajcMethod = (getSignature().getKind() == ResolvedMember.CONSTRUCTOR ? AjcMemberMaker - .postIntroducedConstructor(typeMunger.getAspectType(), fakerm.getDeclaringType(), - fakerm.getParameterTypes()) : AjcMemberMaker.interMethodDispatcher(fakerm, - typeMunger.getAspectType())); - // AjcMemberMaker.interMethodBody(fakerm,typeMunger.getAspectType())); - ResolvedMember rmm = findMethod(typeMunger.getAspectType(), ajcMethod); - if (fakerm.getName().equals(getSignature().getName()) - && fakerm.getParameterSignature().equals(getSignature().getParameterSignature())) { - relevantType = typeMunger.getAspectType(); - foundMember = rmm; - return foundMember.getAnnotationTypes(); - } - } - } - // didn't find in ITDs, look in supers - foundMember = relevantType.lookupMemberWithSupersAndITDs(relevantMember); - if (foundMember == null) { - throw new IllegalStateException("Couldn't find member " + relevantMember + " for type " + relevantType); - } - } - return foundMember.getAnnotationTypes(); - } - - /** - * By determining what "kind" of shadow we are, we can find out the annotations on the appropriate element (method, field, - * constructor, type). Then create one BcelVar entry in the map for each annotation, keyed by annotation type. - */ - public void initializeKindedAnnotationVars() { - if (kindedAnnotationVars != null) { - return; - } - kindedAnnotationVars = new HashMap(); - - ResolvedType[] annotations = null; - Member shadowSignature = getSignature(); - Member annotationHolder = getSignature(); - ResolvedType relevantType = shadowSignature.getDeclaringType().resolve(world); - - if (relevantType.isRawType() || relevantType.isParameterizedType()) { - relevantType = relevantType.getGenericType(); - } - - // Determine the annotations that are of interest - if (getKind() == Shadow.StaticInitialization) { - annotations = relevantType.resolve(world).getAnnotationTypes(); - } else if (getKind() == Shadow.MethodCall || getKind() == Shadow.ConstructorCall) { - ResolvedMember foundMember = findMethod2(relevantType.resolve(world).getDeclaredMethods(), getSignature()); - annotations = getAnnotations(foundMember, shadowSignature, relevantType); - annotationHolder = getRelevantMember(foundMember, shadowSignature, relevantType); - relevantType = annotationHolder.getDeclaringType().resolve(world); - } else if (getKind() == Shadow.FieldSet || getKind() == Shadow.FieldGet) { - annotationHolder = findField(relevantType.getDeclaredFields(), getSignature()); - - if (annotationHolder == null) { - // check the ITD'd dooberries - List mungers = relevantType.resolve(world).getInterTypeMungers(); - for (ConcreteTypeMunger typeMunger : mungers) { - if (typeMunger.getMunger() instanceof NewFieldTypeMunger) { - ResolvedMember fakerm = typeMunger.getSignature(); - // if (fakerm.hasAnnotations()) - ResolvedMember ajcMethod = AjcMemberMaker.interFieldInitializer(fakerm, typeMunger.getAspectType()); - ResolvedMember rmm = findMethod(typeMunger.getAspectType(), ajcMethod); - if (fakerm.equals(getSignature())) { - relevantType = typeMunger.getAspectType(); - annotationHolder = rmm; - } - } - } - } - annotations = ((ResolvedMember) annotationHolder).getAnnotationTypes(); - - } else if (getKind() == Shadow.MethodExecution || getKind() == Shadow.ConstructorExecution - || getKind() == Shadow.AdviceExecution) { - - ResolvedMember foundMember = findMethod2(relevantType.getDeclaredMethods(), getSignature()); - annotations = getAnnotations(foundMember, shadowSignature, relevantType); - annotationHolder = getRelevantMember(foundMember, annotationHolder, relevantType); - UnresolvedType ut = annotationHolder.getDeclaringType(); - relevantType = ut.resolve(world); - - } else if (getKind() == Shadow.ExceptionHandler) { - relevantType = getSignature().getParameterTypes()[0].resolve(world); - annotations = relevantType.getAnnotationTypes(); - - } else if (getKind() == Shadow.PreInitialization || getKind() == Shadow.Initialization) { - ResolvedMember found = findMethod2(relevantType.getDeclaredMethods(), getSignature()); - annotations = found.getAnnotationTypes(); - } - - if (annotations == null) { - // We can't have recognized the shadow - should blow up now to be on the safe side - throw new BCException("Could not discover annotations for shadow: " + getKind()); - } - - for (ResolvedType annotationType : annotations) { - AnnotationAccessVar accessVar = new AnnotationAccessVar(this, getKind(), annotationType.resolve(world), relevantType, - annotationHolder, false); - kindedAnnotationVars.put(annotationType, accessVar); - } - } - - private ResolvedMember findMethod2(ResolvedMember members[], Member sig) { - String signatureName = sig.getName(); - String parameterSignature = sig.getParameterSignature(); - for (ResolvedMember member : members) { - if (member.getName().equals(signatureName) && member.getParameterSignature().equals(parameterSignature)) { - return member; - } - } - return null; - } - - private ResolvedMember findMethod(ResolvedType aspectType, ResolvedMember ajcMethod) { - ResolvedMember decMethods[] = aspectType.getDeclaredMethods(); - for (int i = 0; i < decMethods.length; i++) { - ResolvedMember member = decMethods[i]; - if (member.equals(ajcMethod)) { - return member; - } - } - return null; - } - - private ResolvedMember findField(ResolvedMember[] members, Member lookingFor) { - for (int i = 0; i < members.length; i++) { - ResolvedMember member = members[i]; - if (member.getName().equals(getSignature().getName()) && member.getType().equals(getSignature().getType())) { - return member; - } - } - return null; - } - - public void initializeWithinAnnotationVars() { - if (withinAnnotationVars != null) { - return; - } - withinAnnotationVars = new HashMap(); - - ResolvedType[] annotations = getEnclosingType().resolve(world).getAnnotationTypes(); - for (int i = 0; i < annotations.length; i++) { - ResolvedType ann = annotations[i]; - Kind k = Shadow.StaticInitialization; - withinAnnotationVars.put(ann, new AnnotationAccessVar(this, k, ann, getEnclosingType(), null, true)); - } - } - - public void initializeWithinCodeAnnotationVars() { - if (withincodeAnnotationVars != null) { - return; - } - withincodeAnnotationVars = new HashMap(); - - // For some shadow we are interested in annotations on the method containing that shadow. - ResolvedType[] annotations = getEnclosingMethod().getMemberView().getAnnotationTypes(); - for (int i = 0; i < annotations.length; i++) { - ResolvedType ann = annotations[i]; - Kind k = (getEnclosingMethod().getMemberView().getKind() == Member.CONSTRUCTOR ? Shadow.ConstructorExecution - : Shadow.MethodExecution); - withincodeAnnotationVars.put(ann, new AnnotationAccessVar(this, k, ann, getEnclosingType(), - getEnclosingCodeSignature(), true)); - } - } - - // ---- weave methods - - void weaveBefore(BcelAdvice munger) { - range.insert(munger.getAdviceInstructions(this, null, range.getRealStart()), Range.InsideBefore); - } - - public void weaveAfter(BcelAdvice munger) { - weaveAfterThrowing(munger, UnresolvedType.THROWABLE); - weaveAfterReturning(munger); - } - - /** - * The basic strategy here is to add a set of instructions at the end of the shadow range that dispatch the advice, and then - * return whatever the shadow was going to return anyway. - * - * To achieve this, we note all the return statements in the advice, and replace them with code that: 1) stores the return value - * on top of the stack in a temp var 2) jumps to the start of our advice block 3) restores the return value at the end of the - * advice block before ultimately returning - * - * We also need to bind the return value into a returning parameter, if the advice specified one. - */ - public void weaveAfterReturning(BcelAdvice munger) { - List returns = findReturnInstructions(); - boolean hasReturnInstructions = !returns.isEmpty(); - - // list of instructions that handle the actual return from the join point - InstructionList retList = new InstructionList(); - - // variable that holds the return value - BcelVar returnValueVar = null; - - if (hasReturnInstructions) { - returnValueVar = generateReturnInstructions(returns, retList); - } else { - // we need at least one instruction, as the target for jumps - retList.append(InstructionConstants.NOP); - } - - // list of instructions for dispatching to the advice itself - InstructionList advice = getAfterReturningAdviceDispatchInstructions(munger, retList.getStart()); - - if (hasReturnInstructions) { - InstructionHandle gotoTarget = advice.getStart(); - for (Iterator i = returns.iterator(); i.hasNext();) { - InstructionHandle ih = i.next(); - retargetReturnInstruction(munger.hasExtraParameter(), returnValueVar, gotoTarget, ih); - } - } - - range.append(advice); - range.append(retList); - } - - /** - * @return a list of all the return instructions in the range of this shadow - */ - private List findReturnInstructions() { - List returns = new ArrayList(); - for (InstructionHandle ih = range.getStart(); ih != range.getEnd(); ih = ih.getNext()) { - if (ih.getInstruction().isReturnInstruction()) { - returns.add(ih); - } - } - return returns; - } - - /** - * Given a list containing all the return instruction handles for this shadow, finds the last return instruction and copies it, - * making this the ultimate return. If the shadow has a non-void return type, we also create a temporary variable to hold the - * return value, and load the value from this var before returning (see pr148007 for why we do this - it works around a JRockit - * bug, and is also closer to what javac generates) - * - * Sometimes the 'last return' isnt the right one - some rogue code can include the real return from the body of a subroutine - * that exists at the end of the method. In this case the last return is RETURN but that may not be correct for a method with a - * non-void return type... pr151673 - * - * @param returns list of all the return instructions in the shadow - * @param returnInstructions instruction list into which the return instructions should be generated - * @return the variable holding the return value, if needed - */ - private BcelVar generateReturnInstructions(List returns, InstructionList returnInstructions) { - BcelVar returnValueVar = null; - if (this.hasANonVoidReturnType()) { - // Find the last *correct* return - this is a method with a non-void return type - // so ignore RETURN - Instruction newReturnInstruction = null; - int i = returns.size() - 1; - while (newReturnInstruction == null && i >= 0) { - InstructionHandle ih = returns.get(i); - if (ih.getInstruction().opcode != Constants.RETURN) { - newReturnInstruction = Utility.copyInstruction(ih.getInstruction()); - } - i--; - } - returnValueVar = genTempVar(this.getReturnType()); - returnValueVar.appendLoad(returnInstructions, getFactory()); - returnInstructions.append(newReturnInstruction); - } else { - InstructionHandle lastReturnHandle = returns.get(returns.size() - 1); - Instruction newReturnInstruction = Utility.copyInstruction(lastReturnHandle.getInstruction()); - returnInstructions.append(newReturnInstruction); - } - return returnValueVar; - } - - /** - * @return true, iff this shadow returns a value - */ - private boolean hasANonVoidReturnType() { - return !this.getReturnType().equals(UnresolvedType.VOID); - } - - /** - * Get the list of instructions used to dispatch to the after advice - * - * @param munger - * @param firstInstructionInReturnSequence - * @return - */ - private InstructionList getAfterReturningAdviceDispatchInstructions(BcelAdvice munger, - InstructionHandle firstInstructionInReturnSequence) { - InstructionList advice = new InstructionList(); - - BcelVar tempVar = null; - if (munger.hasExtraParameter()) { - tempVar = insertAdviceInstructionsForBindingReturningParameter(advice); - } - advice.append(munger.getAdviceInstructions(this, tempVar, firstInstructionInReturnSequence)); - return advice; - } - - /** - * If the after() returning(Foo f) form is used, bind the return value to the parameter. If the shadow returns void, bind null. - * - * @param advice - * @return - */ - private BcelVar insertAdviceInstructionsForBindingReturningParameter(InstructionList advice) { - BcelVar tempVar; - UnresolvedType tempVarType = getReturnType(); - if (tempVarType.equals(UnresolvedType.VOID)) { - tempVar = genTempVar(UnresolvedType.OBJECT); - advice.append(InstructionConstants.ACONST_NULL); - tempVar.appendStore(advice, getFactory()); - } else { - tempVar = genTempVar(tempVarType); - advice.append(InstructionFactory.createDup(tempVarType.getSize())); - tempVar.appendStore(advice, getFactory()); - } - return tempVar; - } - - /** - * Helper method for weaveAfterReturning - * - * Each return instruction in the method body is retargeted by calling this method. The return instruction is replaced by up to - * three instructions: 1) if the shadow returns a value, and that value is bound to an after returning parameter, then we DUP - * the return value on the top of the stack 2) if the shadow returns a value, we store it in the returnValueVar (it will be - * retrieved from here when we ultimately return after the advice dispatch) 3) if the return was the last instruction, we add a - * NOP (it will fall through to the advice dispatch), otherwise we add a GOTO that branches to the supplied gotoTarget (start of - * the advice dispatch) - */ - private void retargetReturnInstruction(boolean hasReturningParameter, BcelVar returnValueVar, InstructionHandle gotoTarget, - InstructionHandle returnHandle) { - // pr148007, work around JRockit bug - // replace ret with store into returnValueVar, followed by goto if not - // at the end of the instruction list... - InstructionList newInstructions = new InstructionList(); - if (returnValueVar != null) { - if (hasReturningParameter) { - // we have to dup the return val before consuming it... - newInstructions.append(InstructionFactory.createDup(this.getReturnType().getSize())); - } - // store the return value into this var - returnValueVar.appendStore(newInstructions, getFactory()); - } - if (!isLastInstructionInRange(returnHandle, range)) { - newInstructions.append(InstructionFactory.createBranchInstruction(Constants.GOTO, gotoTarget)); - } - if (newInstructions.isEmpty()) { - newInstructions.append(InstructionConstants.NOP); - } - Utility.replaceInstruction(returnHandle, newInstructions, enclosingMethod); - } - - private boolean isLastInstructionInRange(InstructionHandle ih, ShadowRange aRange) { - return ih.getNext() == aRange.getEnd(); - } - - public void weaveAfterThrowing(BcelAdvice munger, UnresolvedType catchType) { - // a good optimization would be not to generate anything here - // if the shadow is GUARANTEED empty (i.e., there's NOTHING, not even - // a shadow, inside me). - if (getRange().getStart().getNext() == getRange().getEnd()) { - return; - } - InstructionFactory fact = getFactory(); - InstructionList handler = new InstructionList(); - BcelVar exceptionVar = genTempVar(catchType); - exceptionVar.appendStore(handler, fact); - - // pr62642 - // I will now jump through some firey BCEL hoops to generate a trivial bit of code: - // if (exc instanceof ExceptionInInitializerError) - // throw (ExceptionInInitializerError)exc; - if (this.getEnclosingMethod().getName().equals("")) { - ResolvedType eiieType = world.resolve("java.lang.ExceptionInInitializerError"); - ObjectType eiieBcelType = (ObjectType) BcelWorld.makeBcelType(eiieType); - InstructionList ih = new InstructionList(InstructionConstants.NOP); - handler.append(exceptionVar.createLoad(fact)); - handler.append(fact.createInstanceOf(eiieBcelType)); - InstructionBranch bi = InstructionFactory.createBranchInstruction(Constants.IFEQ, ih.getStart()); - handler.append(bi); - handler.append(exceptionVar.createLoad(fact)); - handler.append(fact.createCheckCast(eiieBcelType)); - handler.append(InstructionConstants.ATHROW); - handler.append(ih); - } - - InstructionList endHandler = new InstructionList(exceptionVar.createLoad(fact)); - handler.append(munger.getAdviceInstructions(this, exceptionVar, endHandler.getStart())); - handler.append(endHandler); - handler.append(InstructionConstants.ATHROW); - InstructionHandle handlerStart = handler.getStart(); - - if (isFallsThrough()) { - InstructionHandle jumpTarget = handler.append(InstructionConstants.NOP); - handler.insert(InstructionFactory.createBranchInstruction(Constants.GOTO, jumpTarget)); - } - InstructionHandle protectedEnd = handler.getStart(); - range.insert(handler, Range.InsideAfter); - - enclosingMethod.addExceptionHandler(range.getStart().getNext(), protectedEnd.getPrev(), handlerStart, - (ObjectType) BcelWorld.makeBcelType(catchType), // ???Type.THROWABLE, - // high priority if our args are on the stack - getKind().hasHighPriorityExceptions()); - } - - // ??? this shares a lot of code with the above weaveAfterThrowing - // ??? would be nice to abstract that to say things only once - public void weaveSoftener(BcelAdvice munger, UnresolvedType catchType) { - // a good optimization would be not to generate anything here - // if the shadow is GUARANTEED empty (i.e., there's NOTHING, not even - // a shadow, inside me). - if (getRange().getStart().getNext() == getRange().getEnd()) { - return; - } - - InstructionFactory fact = getFactory(); - InstructionList handler = new InstructionList(); - InstructionList rtExHandler = new InstructionList(); - BcelVar exceptionVar = genTempVar(catchType); - - handler.append(fact.createNew(NameMangler.SOFT_EXCEPTION_TYPE)); - handler.append(InstructionFactory.createDup(1)); - handler.append(exceptionVar.createLoad(fact)); - handler.append(fact.createInvoke(NameMangler.SOFT_EXCEPTION_TYPE, "", Type.VOID, new Type[] { Type.THROWABLE }, - Constants.INVOKESPECIAL)); // ??? special - handler.append(InstructionConstants.ATHROW); - - // ENH 42737 - exceptionVar.appendStore(rtExHandler, fact); - // aload_1 - rtExHandler.append(exceptionVar.createLoad(fact)); - // instanceof class java/lang/RuntimeException - rtExHandler.append(fact.createInstanceOf(new ObjectType("java.lang.RuntimeException"))); - // ifeq go to new SOFT_EXCEPTION_TYPE instruction - rtExHandler.append(InstructionFactory.createBranchInstruction(Constants.IFEQ, handler.getStart())); - // aload_1 - rtExHandler.append(exceptionVar.createLoad(fact)); - // athrow - rtExHandler.append(InstructionFactory.ATHROW); - - InstructionHandle handlerStart = rtExHandler.getStart(); - - if (isFallsThrough()) { - InstructionHandle jumpTarget = range.getEnd();// handler.append(fact.NOP); - rtExHandler.insert(InstructionFactory.createBranchInstruction(Constants.GOTO, jumpTarget)); - } - - rtExHandler.append(handler); - - InstructionHandle protectedEnd = rtExHandler.getStart(); - range.insert(rtExHandler, Range.InsideAfter); - - enclosingMethod.addExceptionHandler(range.getStart().getNext(), protectedEnd.getPrev(), handlerStart, - (ObjectType) BcelWorld.makeBcelType(catchType), - // high priority if our args are on the stack - getKind().hasHighPriorityExceptions()); - } - - public void weavePerObjectEntry(final BcelAdvice munger, final BcelVar onVar) { - final InstructionFactory fact = getFactory(); - - InstructionList entryInstructions = new InstructionList(); - InstructionList entrySuccessInstructions = new InstructionList(); - onVar.appendLoad(entrySuccessInstructions, fact); - - entrySuccessInstructions - .append(Utility.createInvoke(fact, world, AjcMemberMaker.perObjectBind(munger.getConcreteAspect()))); - - InstructionList testInstructions = munger.getTestInstructions(this, entrySuccessInstructions.getStart(), - range.getRealStart(), entrySuccessInstructions.getStart()); - - entryInstructions.append(testInstructions); - entryInstructions.append(entrySuccessInstructions); - - range.insert(entryInstructions, Range.InsideBefore); - } - - // PTWIMPL Create static initializer to call the aspect factory - /** - * Causes the aspect instance to be *set* for later retrievable through localAspectof()/aspectOf() - */ - public void weavePerTypeWithinAspectInitialization(final BcelAdvice munger, UnresolvedType t) { - ResolvedType tResolved = t.resolve(world); - if (tResolved.isInterface()) { - return; // Don't initialize statics in interfaces - } - ResolvedType aspectRT = munger.getConcreteAspect(); - BcelWorld.getBcelObjectType(aspectRT); - - // Although matched, if the visibility rules prevent the aspect from seeing this type, don't - // insert any code (easier to do it here than try to affect the matching logic, unfortunately) - if (!(tResolved.canBeSeenBy(aspectRT) || aspectRT.isPrivilegedAspect())) { - return; - } - - final InstructionFactory fact = getFactory(); - - InstructionList entryInstructions = new InstructionList(); - InstructionList entrySuccessInstructions = new InstructionList(); - - String aspectname = munger.getConcreteAspect().getName(); - - String ptwField = NameMangler.perTypeWithinFieldForTarget(munger.getConcreteAspect()); - entrySuccessInstructions.append(InstructionFactory.PUSH(fact.getConstantPool(), t.getName())); - - entrySuccessInstructions.append(fact.createInvoke(aspectname, "ajc$createAspectInstance", new ObjectType(aspectname), - new Type[] { new ObjectType("java.lang.String") }, Constants.INVOKESTATIC)); - entrySuccessInstructions.append(fact.createPutStatic(t.getName(), ptwField, new ObjectType(aspectname))); - - entryInstructions.append(entrySuccessInstructions); - - range.insert(entryInstructions, Range.InsideBefore); - } - - public void weaveCflowEntry(final BcelAdvice munger, final Member cflowField) { - final boolean isPer = munger.getKind() == AdviceKind.PerCflowBelowEntry || munger.getKind() == AdviceKind.PerCflowEntry; - if (!isPer && getKind() == PreInitialization) { - return; - } - final Type objectArrayType = new ArrayType(Type.OBJECT, 1); - final InstructionFactory fact = getFactory(); - - final BcelVar testResult = genTempVar(UnresolvedType.BOOLEAN); - - InstructionList entryInstructions = new InstructionList(); - { - InstructionList entrySuccessInstructions = new InstructionList(); - - if (munger.hasDynamicTests()) { - entryInstructions.append(Utility.createConstant(fact, 0)); - testResult.appendStore(entryInstructions, fact); - - entrySuccessInstructions.append(Utility.createConstant(fact, 1)); - testResult.appendStore(entrySuccessInstructions, fact); - } - - if (isPer) { - entrySuccessInstructions.append(fact.createInvoke(munger.getConcreteAspect().getName(), - NameMangler.PERCFLOW_PUSH_METHOD, Type.VOID, new Type[] {}, Constants.INVOKESTATIC)); - } else { - BcelVar[] cflowStateVars = munger.getExposedStateAsBcelVars(false); - - if (cflowStateVars.length == 0) { - // This should be getting managed by a counter - lets make sure. - if (!cflowField.getType().getName().endsWith("CFlowCounter")) { - throw new RuntimeException("Incorrectly attempting counter operation on stacked cflow"); - } - entrySuccessInstructions.append(Utility.createGet(fact, cflowField)); - // arrayVar.appendLoad(entrySuccessInstructions, fact); - entrySuccessInstructions.append(fact.createInvoke(NameMangler.CFLOW_COUNTER_TYPE, "inc", Type.VOID, - new Type[] {}, Constants.INVOKEVIRTUAL)); - } else { - BcelVar arrayVar = genTempVar(UnresolvedType.OBJECTARRAY); - - int alen = cflowStateVars.length; - entrySuccessInstructions.append(Utility.createConstant(fact, alen)); - entrySuccessInstructions.append(fact.createNewArray(Type.OBJECT, (short) 1)); - arrayVar.appendStore(entrySuccessInstructions, fact); - - for (int i = 0; i < alen; i++) { - arrayVar.appendConvertableArrayStore(entrySuccessInstructions, fact, i, cflowStateVars[i]); - } - - entrySuccessInstructions.append(Utility.createGet(fact, cflowField)); - arrayVar.appendLoad(entrySuccessInstructions, fact); - - entrySuccessInstructions.append(fact.createInvoke(NameMangler.CFLOW_STACK_TYPE, "push", Type.VOID, - new Type[] { objectArrayType }, Constants.INVOKEVIRTUAL)); - } - } - - InstructionList testInstructions = munger.getTestInstructions(this, entrySuccessInstructions.getStart(), - range.getRealStart(), entrySuccessInstructions.getStart()); - entryInstructions.append(testInstructions); - entryInstructions.append(entrySuccessInstructions); - } - - BcelAdvice exitAdvice = new BcelAdvice(null, null, null, 0, 0, 0, null, munger.getConcreteAspect()) { - @Override - public InstructionList getAdviceInstructions(BcelShadow s, BcelVar extraArgVar, InstructionHandle ifNoAdvice) { - InstructionList exitInstructions = new InstructionList(); - if (munger.hasDynamicTests()) { - testResult.appendLoad(exitInstructions, fact); - exitInstructions.append(InstructionFactory.createBranchInstruction(Constants.IFEQ, ifNoAdvice)); - } - exitInstructions.append(Utility.createGet(fact, cflowField)); - if (munger.getKind() != AdviceKind.PerCflowEntry && munger.getKind() != AdviceKind.PerCflowBelowEntry - && munger.getExposedStateAsBcelVars(false).length == 0) { - exitInstructions.append(fact.createInvoke(NameMangler.CFLOW_COUNTER_TYPE, "dec", Type.VOID, new Type[] {}, - Constants.INVOKEVIRTUAL)); - } else { - exitInstructions.append(fact.createInvoke(NameMangler.CFLOW_STACK_TYPE, "pop", Type.VOID, new Type[] {}, - Constants.INVOKEVIRTUAL)); - } - return exitInstructions; - } - }; -// if (getKind() == PreInitialization) { -// weaveAfterReturning(exitAdvice); -// } -// else { - weaveAfter(exitAdvice); -// } - - range.insert(entryInstructions, Range.InsideBefore); - } - - /* - * Implementation notes: - * - * AroundInline still extracts the instructions of the original shadow into an extracted method. This allows inlining of even - * that advice that doesn't call proceed or calls proceed more than once. - * - * It extracts the instructions of the original shadow into a method. - * - * Then it extracts the instructions of the advice into a new method defined on this enclosing class. This new method can then - * be specialized as below. - * - * Then it searches in the instructions of the advice for any call to the proceed method. - * - * At such a call, there is stuff on the stack representing the arguments to proceed. Pop these into the frame. - * - * Now build the stack for the call to the extracted method, taking values either from the join point state or from the new - * frame locs from proceed. Now call the extracted method. The right return value should be on the stack, so no cast is - * necessary. - * - * If only one call to proceed is made, we can re-inline the original shadow. We are not doing that presently. - * - * If the body of the advice can be determined to not alter the stack, or if this shadow doesn't care about the stack, i.e. - * method-execution, then the new method for the advice can also be re-lined. We are not doing that presently. - */ - public void weaveAroundInline(BcelAdvice munger, boolean hasDynamicTest) { - // !!! THIS BLOCK OF CODE SHOULD BE IN A METHOD CALLED weaveAround(...); - Member mungerSig = munger.getSignature(); - // Member originalSig = mungerSig; // If mungerSig is on a parameterized type, originalSig is the member on the generic type - if (mungerSig instanceof ResolvedMember) { - ResolvedMember rm = (ResolvedMember) mungerSig; - if (rm.hasBackingGenericMember()) { - mungerSig = rm.getBackingGenericMember(); - } - } - ResolvedType declaringAspectType = world.resolve(mungerSig.getDeclaringType(), true); - if (declaringAspectType.isMissing()) { - world.getLint().cantFindType.signal( - new String[] { WeaverMessages.format(WeaverMessages.CANT_FIND_TYPE_DURING_AROUND_WEAVE, - declaringAspectType.getClassName()) }, getSourceLocation(), - new ISourceLocation[] { munger.getSourceLocation() }); - } - - // ??? might want some checks here to give better errors - ResolvedType rt = (declaringAspectType.isParameterizedType() ? declaringAspectType.getGenericType() : declaringAspectType); - BcelObjectType ot = BcelWorld.getBcelObjectType(rt); - LazyMethodGen adviceMethod = ot.getLazyClassGen().getLazyMethodGen(mungerSig); - if (!adviceMethod.getCanInline()) { - weaveAroundClosure(munger, hasDynamicTest); - return; - } - - // specific test for @AJ proceedInInners - if (isAnnotationStylePassingProceedingJoinPointOutOfAdvice(munger, hasDynamicTest, adviceMethod)) { - return; - } - - // We can't inline around methods if they have around advice on them, this - // is because the weaving will extract the body and hence the proceed call. - - // TODO should consider optimizations to recognize simple cases that don't require body extraction - - enclosingMethod.setCanInline(false); - - LazyClassGen shadowClass = getEnclosingClass(); - - // Extract the shadow into a new method. For example: - // "private static final void method_aroundBody0(M, M, String, org.aspectj.lang.JoinPoint)" - // Parameters are: this if there is one, target if there is one and its different to this, then original arguments - // at the shadow, then tjp - String extractedShadowMethodName = NameMangler.aroundShadowMethodName(getSignature(), shadowClass.getNewGeneratedNameTag()); - List parameterNames = new ArrayList(); - boolean shadowClassIsInterface = shadowClass.isInterface(); - LazyMethodGen extractedShadowMethod = extractShadowInstructionsIntoNewMethod(extractedShadowMethodName, - shadowClassIsInterface?Modifier.PUBLIC:Modifier.PRIVATE, - munger.getSourceLocation(), parameterNames,shadowClassIsInterface); - - List argsToCallLocalAdviceMethodWith = new ArrayList(); - List proceedVarList = new ArrayList(); - int extraParamOffset = 0; - - // Create the extra parameters that are needed for passing to proceed - // This code is very similar to that found in makeCallToCallback and should - // be rationalized in the future - - if (thisVar != null) { - argsToCallLocalAdviceMethodWith.add(thisVar); - proceedVarList.add(new BcelVar(thisVar.getType(), extraParamOffset)); - extraParamOffset += thisVar.getType().getSize(); - } - - if (targetVar != null && targetVar != thisVar) { - argsToCallLocalAdviceMethodWith.add(targetVar); - proceedVarList.add(new BcelVar(targetVar.getType(), extraParamOffset)); - extraParamOffset += targetVar.getType().getSize(); - } - for (int i = 0, len = getArgCount(); i < len; i++) { - argsToCallLocalAdviceMethodWith.add(argVars[i]); - proceedVarList.add(new BcelVar(argVars[i].getType(), extraParamOffset)); - extraParamOffset += argVars[i].getType().getSize(); - } - if (thisJoinPointVar != null) { - argsToCallLocalAdviceMethodWith.add(thisJoinPointVar); - proceedVarList.add(new BcelVar(thisJoinPointVar.getType(), extraParamOffset)); - extraParamOffset += thisJoinPointVar.getType().getSize(); - } - - // We use the munger signature here because it allows for any parameterization of the mungers pointcut that - // may have occurred ie. if the pointcut is p(T t) in the super aspect and that has become p(Foo t) in the sub aspect - // then here the munger signature will have 'Foo' as an argument in it whilst the adviceMethod argument type will be - // 'Object' - since it represents the advice method in the superaspect which uses the erasure of the type variable p(Object - // t) - see pr174449. - - Type[] adviceParameterTypes = BcelWorld.makeBcelTypes(munger.getSignature().getParameterTypes()); - - // forces initialization ... dont like this but seems to be required for some tests to pass, I think that means there - // is a LazyMethodGen method that is not correctly setup to call initialize() when it is invoked - but I dont have - // time right now to discover which - adviceMethod.getArgumentTypes(); - - Type[] extractedMethodParameterTypes = extractedShadowMethod.getArgumentTypes(); - - Type[] parameterTypes = new Type[extractedMethodParameterTypes.length + adviceParameterTypes.length + 1]; - int parameterIndex = 0; - System.arraycopy(extractedMethodParameterTypes, 0, parameterTypes, parameterIndex, extractedMethodParameterTypes.length); - parameterIndex += extractedMethodParameterTypes.length; - parameterTypes[parameterIndex++] = BcelWorld.makeBcelType(adviceMethod.getEnclosingClass().getType()); - System.arraycopy(adviceParameterTypes, 0, parameterTypes, parameterIndex, adviceParameterTypes.length); - - // Extract the advice into a new method. This will go in the same type as the shadow - // name will be something like foo_aroundBody1$advice - String localAdviceMethodName = NameMangler.aroundAdviceMethodName(getSignature(), shadowClass.getNewGeneratedNameTag()); - int localAdviceMethodModifiers = Modifier.PRIVATE | (world.useFinal() & !shadowClassIsInterface ? Modifier.FINAL : 0) | Modifier.STATIC; - LazyMethodGen localAdviceMethod = new LazyMethodGen(localAdviceMethodModifiers, BcelWorld.makeBcelType(mungerSig.getReturnType()), localAdviceMethodName, parameterTypes, - NoDeclaredExceptions, shadowClass); - - // Doesnt work properly, so leave it out: - // String aspectFilename = adviceMethod.getEnclosingClass().getInternalFileName(); - // String shadowFilename = shadowClass.getInternalFileName(); - // if (!aspectFilename.equals(shadowFilename)) { - // localAdviceMethod.fromFilename = aspectFilename; - // shadowClass.addInlinedSourceFileInfo(aspectFilename, adviceMethod.highestLineNumber); - // } - - shadowClass.addMethodGen(localAdviceMethod); - - // create a map that will move all slots in advice method forward by extraParamOffset - // in order to make room for the new proceed-required arguments that are added at - // the beginning of the parameter list - int nVars = adviceMethod.getMaxLocals() + extraParamOffset; - IntMap varMap = IntMap.idMap(nVars); - for (int i = extraParamOffset; i < nVars; i++) { - varMap.put(i - extraParamOffset, i); - } - - final InstructionFactory fact = getFactory(); - - localAdviceMethod.getBody().insert( - BcelClassWeaver.genInlineInstructions(adviceMethod, localAdviceMethod, varMap, fact, true)); - - localAdviceMethod.setMaxLocals(nVars); - - // the shadow is now empty. First, create a correct call - // to the around advice. This includes both the call (which may involve - // value conversion of the advice arguments) and the return - // (which may involve value conversion of the return value). Right now - // we push a null for the unused closure. It's sad, but there it is. - - InstructionList advice = new InstructionList(); - // InstructionHandle adviceMethodInvocation; - { - for (Iterator i = argsToCallLocalAdviceMethodWith.iterator(); i.hasNext();) { - BcelVar var = i.next(); - var.appendLoad(advice, fact); - } - // ??? we don't actually need to push NULL for the closure if we take care - boolean isAnnoStyleConcreteAspect = munger.getConcreteAspect().isAnnotationStyleAspect(); - boolean isAnnoStyleDeclaringAspect = munger.getDeclaringAspect() != null ? munger.getDeclaringAspect().resolve(world) - .isAnnotationStyleAspect() : false; - - InstructionList iList = null; - if (isAnnoStyleConcreteAspect && isAnnoStyleDeclaringAspect) { - iList = this.loadThisJoinPoint(); - iList.append(Utility.createConversion(getFactory(), LazyClassGen.tjpType, LazyClassGen.proceedingTjpType)); - } else { - iList = new InstructionList(InstructionConstants.ACONST_NULL); - } - advice.append(munger.getAdviceArgSetup(this, null, iList)); - // adviceMethodInvocation = - advice.append(Utility.createInvoke(fact, localAdviceMethod)); // (fact, getWorld(), munger.getSignature())); - advice.append(Utility.createConversion(getFactory(), BcelWorld.makeBcelType(mungerSig.getReturnType()), - extractedShadowMethod.getReturnType(), world.isInJava5Mode())); - if (!isFallsThrough()) { - advice.append(InstructionFactory.createReturn(extractedShadowMethod.getReturnType())); - } - } - - // now, situate the call inside the possible dynamic tests, - // and actually add the whole mess to the shadow - if (!hasDynamicTest) { - range.append(advice); - } else { - InstructionList afterThingie = new InstructionList(InstructionConstants.NOP); - InstructionList callback = makeCallToCallback(extractedShadowMethod); - if (terminatesWithReturn()) { - callback.append(InstructionFactory.createReturn(extractedShadowMethod.getReturnType())); - } else { - // InstructionHandle endNop = range.insert(fact.NOP, Range.InsideAfter); - advice.append(InstructionFactory.createBranchInstruction(Constants.GOTO, afterThingie.getStart())); - } - range.append(munger.getTestInstructions(this, advice.getStart(), callback.getStart(), advice.getStart())); - range.append(advice); - range.append(callback); - range.append(afterThingie); - } - - // now search through the advice, looking for a call to PROCEED. - // Then we replace the call to proceed with some argument setup, and a - // call to the extracted method. - - // inlining support for code style aspects - if (!munger.getDeclaringType().isAnnotationStyleAspect()) { - String proceedName = NameMangler.proceedMethodName(munger.getSignature().getName()); - - InstructionHandle curr = localAdviceMethod.getBody().getStart(); - InstructionHandle end = localAdviceMethod.getBody().getEnd(); - ConstantPool cpg = localAdviceMethod.getEnclosingClass().getConstantPool(); - while (curr != end) { - InstructionHandle next = curr.getNext(); - Instruction inst = curr.getInstruction(); - if ((inst.opcode == Constants.INVOKESTATIC) && proceedName.equals(((InvokeInstruction) inst).getMethodName(cpg))) { - - localAdviceMethod.getBody().append(curr, - getRedoneProceedCall(fact, extractedShadowMethod, munger, localAdviceMethod, proceedVarList)); - Utility.deleteInstruction(curr, localAdviceMethod); - } - curr = next; - } - // and that's it. - } else { - // ATAJ inlining support for @AJ aspects - // [TODO document @AJ code rule: don't manipulate 2 jps proceed at the same time.. in an advice body] - InstructionHandle curr = localAdviceMethod.getBody().getStart(); - InstructionHandle end = localAdviceMethod.getBody().getEnd(); - ConstantPool cpg = localAdviceMethod.getEnclosingClass().getConstantPool(); - while (curr != end) { - InstructionHandle next = curr.getNext(); - Instruction inst = curr.getInstruction(); - if ((inst instanceof INVOKEINTERFACE) && "proceed".equals(((INVOKEINTERFACE) inst).getMethodName(cpg))) { - final boolean isProceedWithArgs; - if (((INVOKEINTERFACE) inst).getArgumentTypes(cpg).length == 1) { - // proceed with args as a boxed Object[] - isProceedWithArgs = true; - } else { - isProceedWithArgs = false; - } - InstructionList insteadProceedIl = getRedoneProceedCallForAnnotationStyle(fact, extractedShadowMethod, munger, - localAdviceMethod, proceedVarList, isProceedWithArgs); - localAdviceMethod.getBody().append(curr, insteadProceedIl); - Utility.deleteInstruction(curr, localAdviceMethod); - } - curr = next; - } - } - - // if (parameterNames.size() == 0) { - // On return we have inserted the advice body into the local advice method. We have remapped all the local variables - // that were referenced in the advice as we did the copy, and so the local variable table for localAdviceMethod is - // now lacking any information about all the initial variables. - InstructionHandle start = localAdviceMethod.getBody().getStart(); - InstructionHandle end = localAdviceMethod.getBody().getEnd(); - - // Find the real start and end - while (start.getInstruction().opcode == Constants.IMPDEP1) { - start = start.getNext(); - } - while (end.getInstruction().opcode == Constants.IMPDEP1) { - end = end.getPrev(); - } - Type[] args = localAdviceMethod.getArgumentTypes(); - int argNumber = 0; - for (int slot = 0; slot < extraParamOffset; argNumber++) { // slot will increase by the argument size each time - String argumentName = null; - if (argNumber >= args.length || parameterNames.size() == 0 || argNumber >= parameterNames.size()) { - // this should be unnecessary as I think all known joinpoints and helper methods - // propagate the parameter names around correctly - but just in case let us do this - // rather than fail. If a bug is raised reporting unknown as a local variable name - // then investigate the joinpoint giving rise to the ResolvedMember and why it has - // no parameter names specified - argumentName = new StringBuffer("unknown").append(argNumber).toString(); - } else { - argumentName = parameterNames.get(argNumber); - } - String argumentSignature = args[argNumber].getSignature(); - LocalVariableTag lvt = new LocalVariableTag(argumentSignature, argumentName, slot, 0); - start.addTargeter(lvt); - end.addTargeter(lvt); - slot += args[argNumber].getSize(); - } - } - - /** - * Check if the advice method passes a pjp parameter out via an invoke instruction - if so we can't risk inlining. - */ - private boolean isAnnotationStylePassingProceedingJoinPointOutOfAdvice(BcelAdvice munger, boolean hasDynamicTest, - LazyMethodGen adviceMethod) { - if (munger.getConcreteAspect().isAnnotationStyleAspect()) { - // if we can't find one proceed() we suspect that the call - // is happening in an inner class so we don't inline it. - // Note: for code style, this is done at Aspect compilation time. - boolean canSeeProceedPassedToOther = false; - InstructionHandle curr = adviceMethod.getBody().getStart(); - InstructionHandle end = adviceMethod.getBody().getEnd(); - ConstantPool cpg = adviceMethod.getEnclosingClass().getConstantPool(); - while (curr != end) { - InstructionHandle next = curr.getNext(); - Instruction inst = curr.getInstruction(); - if ((inst instanceof InvokeInstruction) - && ((InvokeInstruction) inst).getSignature(cpg).indexOf("Lorg/aspectj/lang/ProceedingJoinPoint;") > 0) { - // we may want to refine to exclude stuff returning jp ? - // does code style skip inline if i write dump(thisJoinPoint) ? - canSeeProceedPassedToOther = true;// we see one pjp passed around - dangerous - break; - } - curr = next; - } - if (canSeeProceedPassedToOther) { - // remember this decision to avoid re-analysis - adviceMethod.setCanInline(false); - weaveAroundClosure(munger, hasDynamicTest); - return true; - } - } - return false; - } - - private InstructionList getRedoneProceedCall(InstructionFactory fact, LazyMethodGen callbackMethod, BcelAdvice munger, - LazyMethodGen localAdviceMethod, List argVarList) { - InstructionList ret = new InstructionList(); - // we have on stack all the arguments for the ADVICE call. - // we have in frame somewhere all the arguments for the non-advice call. - - BcelVar[] adviceVars = munger.getExposedStateAsBcelVars(true); - IntMap proceedMap = makeProceedArgumentMap(adviceVars); - - // System.out.println(proceedMap + " for " + this); - // System.out.println(argVarList); - - ResolvedType[] proceedParamTypes = world.resolve(munger.getSignature().getParameterTypes()); - // remove this*JoinPoint* as arguments to proceed - if (munger.getBaseParameterCount() + 1 < proceedParamTypes.length) { - int len = munger.getBaseParameterCount() + 1; - ResolvedType[] newTypes = new ResolvedType[len]; - System.arraycopy(proceedParamTypes, 0, newTypes, 0, len); - proceedParamTypes = newTypes; - } - - // System.out.println("stateTypes: " + Arrays.asList(stateTypes)); - BcelVar[] proceedVars = Utility.pushAndReturnArrayOfVars(proceedParamTypes, ret, fact, localAdviceMethod); - - Type[] stateTypes = callbackMethod.getArgumentTypes(); - // System.out.println("stateTypes: " + Arrays.asList(stateTypes)); - - for (int i = 0, len = stateTypes.length; i < len; i++) { - Type stateType = stateTypes[i]; - ResolvedType stateTypeX = BcelWorld.fromBcel(stateType).resolve(world); - if (proceedMap.hasKey(i)) { - // throw new RuntimeException("unimplemented"); - proceedVars[proceedMap.get(i)].appendLoadAndConvert(ret, fact, stateTypeX); - } else { - argVarList.get(i).appendLoad(ret, fact); - } - } - - ret.append(Utility.createInvoke(fact, callbackMethod)); - ret.append(Utility.createConversion(fact, callbackMethod.getReturnType(), - BcelWorld.makeBcelType(munger.getSignature().getReturnType()), world.isInJava5Mode())); - return ret; - } - - // private static boolean bindsThisOrTarget(Pointcut pointcut) { - // ThisTargetFinder visitor = new ThisTargetFinder(); - // pointcut.accept(visitor, null); - // return visitor.bindsThisOrTarget; - // } - - // private static class ThisTargetFinder extends IdentityPointcutVisitor { - // boolean bindsThisOrTarget = false; - // - // public Object visit(ThisOrTargetPointcut node, Object data) { - // if (node.isBinding()) { - // bindsThisOrTarget = true; - // } - // return node; - // } - // - // public Object visit(AndPointcut node, Object data) { - // if (!bindsThisOrTarget) node.getLeft().accept(this, data); - // if (!bindsThisOrTarget) node.getRight().accept(this, data); - // return node; - // } - // - // public Object visit(NotPointcut node, Object data) { - // if (!bindsThisOrTarget) node.getNegatedPointcut().accept(this, data); - // return node; - // } - // - // public Object visit(OrPointcut node, Object data) { - // if (!bindsThisOrTarget) node.getLeft().accept(this, data); - // if (!bindsThisOrTarget) node.getRight().accept(this, data); - // return node; - // } - // } - - /** - * Annotation style handling for inlining. - * - * Note: The proceedingjoinpoint is already on the stack (since the user was calling pjp.proceed(...) - * - * The proceed map is ignored (in terms of argument repositioning) since we have a fixed expected format for annotation style. - * The aim here is to change the proceed() call into a call to the xxx_aroundBody0 method. - * - * - */ - private InstructionList getRedoneProceedCallForAnnotationStyle(InstructionFactory fact, LazyMethodGen callbackMethod, - BcelAdvice munger, LazyMethodGen localAdviceMethod, List argVarList, boolean isProceedWithArgs) { - InstructionList ret = new InstructionList(); - - // store the Object[] array on stack if proceed with args - if (isProceedWithArgs) { - - // STORE the Object[] into a local variable - Type objectArrayType = Type.OBJECT_ARRAY; - int theObjectArrayLocalNumber = localAdviceMethod.allocateLocal(objectArrayType); - ret.append(InstructionFactory.createStore(objectArrayType, theObjectArrayLocalNumber)); - - // STORE the ProceedingJoinPoint instance into a local variable - Type proceedingJpType = Type.getType("Lorg/aspectj/lang/ProceedingJoinPoint;"); - int pjpLocalNumber = localAdviceMethod.allocateLocal(proceedingJpType); - ret.append(InstructionFactory.createStore(proceedingJpType, pjpLocalNumber)); - - // Aim here initially is to determine whether the user will have provided a new - // this/target in the object array and consume them if they have, leaving us the rest of - // the arguments to process as regular arguments to the invocation at the original join point - - boolean pointcutBindsThis = bindsThis(munger); - boolean pointcutBindsTarget = bindsTarget(munger); - boolean targetIsSameAsThis = getKind().isTargetSameAsThis(); - - int nextArgumentToProvideForCallback = 0; - - if (hasThis()) { - if (!(pointcutBindsTarget && targetIsSameAsThis)) { - if (pointcutBindsThis) { - // they have supplied new this as first entry in object array, consume it - ret.append(InstructionFactory.createLoad(objectArrayType, theObjectArrayLocalNumber)); - ret.append(Utility.createConstant(fact, 0)); - ret.append(InstructionFactory.createArrayLoad(Type.OBJECT)); - ret.append(Utility.createConversion(fact, Type.OBJECT, callbackMethod.getArgumentTypes()[0])); - } else { - // use local variable 0 - ret.append(InstructionFactory.createALOAD(0)); - } - nextArgumentToProvideForCallback++; - } - } - - if (hasTarget()) { - if (pointcutBindsTarget) { - if (getKind().isTargetSameAsThis()) { - ret.append(InstructionFactory.createLoad(objectArrayType, theObjectArrayLocalNumber)); - ret.append(Utility.createConstant(fact, pointcutBindsThis ? 1 : 0)); - ret.append(InstructionFactory.createArrayLoad(Type.OBJECT)); - ret.append(Utility.createConversion(fact, Type.OBJECT, callbackMethod.getArgumentTypes()[0])); - } else { - int position = (hasThis() && pointcutBindsThis)? 1 : 0; - ret.append(InstructionFactory.createLoad(objectArrayType, theObjectArrayLocalNumber)); - ret.append(Utility.createConstant(fact, position)); - ret.append(InstructionFactory.createArrayLoad(Type.OBJECT)); - ret.append(Utility.createConversion(fact, Type.OBJECT, callbackMethod.getArgumentTypes()[nextArgumentToProvideForCallback])); - } - nextArgumentToProvideForCallback++; - } else { - if (getKind().isTargetSameAsThis()) { - // ret.append(new ALOAD(0)); - } else { - ret.append(InstructionFactory.createLoad(localAdviceMethod.getArgumentTypes()[0], hasThis() ? 1 : 0)); - nextArgumentToProvideForCallback++; - } - } - } - - // Where to start in the object array in order to pick up arguments - int indexIntoObjectArrayForArguments = (pointcutBindsThis ? 1 : 0) + (pointcutBindsTarget ? 1 : 0); - - int len = callbackMethod.getArgumentTypes().length; - for (int i = nextArgumentToProvideForCallback; i < len; i++) { - Type stateType = callbackMethod.getArgumentTypes()[i]; - BcelWorld.fromBcel(stateType).resolve(world); - if ("Lorg/aspectj/lang/JoinPoint;".equals(stateType.getSignature())) { - ret.append(new InstructionLV(Constants.ALOAD, pjpLocalNumber)); - } else { - ret.append(InstructionFactory.createLoad(objectArrayType, theObjectArrayLocalNumber)); - ret.append(Utility - .createConstant(fact, i - nextArgumentToProvideForCallback + indexIntoObjectArrayForArguments)); - ret.append(InstructionFactory.createArrayLoad(Type.OBJECT)); - ret.append(Utility.createConversion(fact, Type.OBJECT, stateType)); - } - } - - } else { - Type proceedingJpType = Type.getType("Lorg/aspectj/lang/ProceedingJoinPoint;"); - int localJp = localAdviceMethod.allocateLocal(proceedingJpType); - ret.append(InstructionFactory.createStore(proceedingJpType, localJp)); - - int idx = 0; - for (int i = 0, len = callbackMethod.getArgumentTypes().length; i < len; i++) { - Type stateType = callbackMethod.getArgumentTypes()[i]; - /* ResolvedType stateTypeX = */ - BcelWorld.fromBcel(stateType).resolve(world); - if ("Lorg/aspectj/lang/JoinPoint;".equals(stateType.getSignature())) { - ret.append(InstructionFactory.createALOAD(localJp));// from localAdvice signature - // } else if ("Lorg/aspectj/lang/ProceedingJoinPoint;".equals(stateType.getSignature())) { - // //FIXME ALEX? - // ret.append(new ALOAD(localJp));// from localAdvice signature - // // ret.append(fact.createCheckCast( - // // (ReferenceType) BcelWorld.makeBcelType(stateTypeX) - // // )); - // // cast ? - // - idx++; - } else { - ret.append(InstructionFactory.createLoad(stateType, idx)); - idx += stateType.getSize(); - } - } - } - - // do the callback invoke - ret.append(Utility.createInvoke(fact, callbackMethod)); - - // box it again. Handles cases where around advice does return something else than Object - if (!UnresolvedType.OBJECT.equals(munger.getSignature().getReturnType())) { - ret.append(Utility.createConversion(fact, callbackMethod.getReturnType(), Type.OBJECT)); - } - ret.append(Utility.createConversion(fact, callbackMethod.getReturnType(), - BcelWorld.makeBcelType(munger.getSignature().getReturnType()), world.isInJava5Mode())); - - return ret; - - // - // - // - // if (proceedMap.hasKey(i)) { - // ret.append(new ALOAD(i)); - // //throw new RuntimeException("unimplemented"); - // //proceedVars[proceedMap.get(i)].appendLoadAndConvert(ret, fact, stateTypeX); - // } else { - // //((BcelVar) argVarList.get(i)).appendLoad(ret, fact); - // //ret.append(new ALOAD(i)); - // if ("Lorg/aspectj/lang/JoinPoint;".equals(stateType.getSignature())) { - // ret.append(new ALOAD(i)); - // } else { - // ret.append(new ALOAD(i)); - // } - // } - // } - // - // ret.append(Utility.createInvoke(fact, callbackMethod)); - // ret.append(Utility.createConversion(fact, callbackMethod.getReturnType(), - // BcelWorld.makeBcelType(munger.getSignature().getReturnType()))); - // - // //ret.append(new ACONST_NULL());//will be POPed - // if (true) return ret; - // - // - // - // // we have on stack all the arguments for the ADVICE call. - // // we have in frame somewhere all the arguments for the non-advice call. - // - // BcelVar[] adviceVars = munger.getExposedStateAsBcelVars(); - // IntMap proceedMap = makeProceedArgumentMap(adviceVars); - // - // System.out.println(proceedMap + " for " + this); - // System.out.println(argVarList); - // - // ResolvedType[] proceedParamTypes = - // world.resolve(munger.getSignature().getParameterTypes()); - // // remove this*JoinPoint* as arguments to proceed - // if (munger.getBaseParameterCount()+1 < proceedParamTypes.length) { - // int len = munger.getBaseParameterCount()+1; - // ResolvedType[] newTypes = new ResolvedType[len]; - // System.arraycopy(proceedParamTypes, 0, newTypes, 0, len); - // proceedParamTypes = newTypes; - // } - // - // //System.out.println("stateTypes: " + Arrays.asList(stateTypes)); - // BcelVar[] proceedVars = - // Utility.pushAndReturnArrayOfVars(proceedParamTypes, ret, fact, localAdviceMethod); - // - // Type[] stateTypes = callbackMethod.getArgumentTypes(); - // // System.out.println("stateTypes: " + Arrays.asList(stateTypes)); - // - // for (int i=0, len=stateTypes.length; i < len; i++) { - // Type stateType = stateTypes[i]; - // ResolvedType stateTypeX = BcelWorld.fromBcel(stateType).resolve(world); - // if (proceedMap.hasKey(i)) { - // //throw new RuntimeException("unimplemented"); - // proceedVars[proceedMap.get(i)].appendLoadAndConvert(ret, fact, stateTypeX); - // } else { - // ((BcelVar) argVarList.get(i)).appendLoad(ret, fact); - // } - // } - // - // ret.append(Utility.createInvoke(fact, callbackMethod)); - // ret.append(Utility.createConversion(fact, callbackMethod.getReturnType(), - // BcelWorld.makeBcelType(munger.getSignature().getReturnType()))); - // return ret; - } - - private boolean bindsThis(BcelAdvice munger) { - UsesThisVisitor utv = new UsesThisVisitor(); - munger.getPointcut().accept(utv, null); - return utv.usesThis; - } - - private boolean bindsTarget(BcelAdvice munger) { - UsesTargetVisitor utv = new UsesTargetVisitor(); - munger.getPointcut().accept(utv, null); - return utv.usesTarget; - } - - private static class UsesThisVisitor extends AbstractPatternNodeVisitor { - boolean usesThis = false; - - @Override - public Object visit(ThisOrTargetPointcut node, Object data) { - if (node.isThis() && node.isBinding()) { - usesThis = true; - } - return node; - } - - @Override - public Object visit(AndPointcut node, Object data) { - if (!usesThis) { - node.getLeft().accept(this, data); - } - if (!usesThis) { - node.getRight().accept(this, data); - } - return node; - } - - @Override - public Object visit(NotPointcut node, Object data) { - if (!usesThis) { - node.getNegatedPointcut().accept(this, data); - } - return node; - } - - @Override - public Object visit(OrPointcut node, Object data) { - if (!usesThis) { - node.getLeft().accept(this, data); - } - if (!usesThis) { - node.getRight().accept(this, data); - } - return node; - } - } - - private static class UsesTargetVisitor extends AbstractPatternNodeVisitor { - boolean usesTarget = false; - - @Override - public Object visit(ThisOrTargetPointcut node, Object data) { - if (!node.isThis() && node.isBinding()) { - usesTarget = true; - } - return node; - } - - @Override - public Object visit(AndPointcut node, Object data) { - if (!usesTarget) { - node.getLeft().accept(this, data); - } - if (!usesTarget) { - node.getRight().accept(this, data); - } - return node; - } - - @Override - public Object visit(NotPointcut node, Object data) { - if (!usesTarget) { - node.getNegatedPointcut().accept(this, data); - } - return node; - } - - @Override - public Object visit(OrPointcut node, Object data) { - if (!usesTarget) { - node.getLeft().accept(this, data); - } - if (!usesTarget) { - node.getRight().accept(this, data); - } - return node; - } - } - - public void weaveAroundClosure(BcelAdvice munger, boolean hasDynamicTest) { - InstructionFactory fact = getFactory(); - - enclosingMethod.setCanInline(false); - - int linenumber = getSourceLine(); - // MOVE OUT ALL THE INSTRUCTIONS IN MY SHADOW INTO ANOTHER METHOD! - - // callbackMethod will be something like: "static final void m_aroundBody0(I)" - boolean shadowClassIsInterface = getEnclosingClass().isInterface(); - LazyMethodGen callbackMethod = extractShadowInstructionsIntoNewMethod( - NameMangler.aroundShadowMethodName(getSignature(), getEnclosingClass().getNewGeneratedNameTag()), shadowClassIsInterface?Modifier.PUBLIC:0, - munger.getSourceLocation(), new ArrayList(),shadowClassIsInterface); - - BcelVar[] adviceVars = munger.getExposedStateAsBcelVars(true); - - String closureClassName = NameMangler.makeClosureClassName(getEnclosingClass().getType(), getEnclosingClass() - .getNewGeneratedNameTag()); - - Member constructorSig = new MemberImpl(Member.CONSTRUCTOR, UnresolvedType.forName(closureClassName), 0, "", - "([Ljava/lang/Object;)V"); - - BcelVar closureHolder = null; - - // This is not being used currently since getKind() == preinitializaiton - // cannot happen in around advice - if (getKind() == PreInitialization) { - closureHolder = genTempVar(AjcMemberMaker.AROUND_CLOSURE_TYPE); - } - - InstructionList closureInstantiation = makeClosureInstantiation(constructorSig, closureHolder); - - /* LazyMethodGen constructor = */ - makeClosureClassAndReturnConstructor(closureClassName, callbackMethod, makeProceedArgumentMap(adviceVars)); - - InstructionList returnConversionCode; - if (getKind() == PreInitialization) { - returnConversionCode = new InstructionList(); - - BcelVar stateTempVar = genTempVar(UnresolvedType.OBJECTARRAY); - closureHolder.appendLoad(returnConversionCode, fact); - - returnConversionCode.append(Utility.createInvoke(fact, world, AjcMemberMaker.aroundClosurePreInitializationGetter())); - stateTempVar.appendStore(returnConversionCode, fact); - - Type[] stateTypes = getSuperConstructorParameterTypes(); - - returnConversionCode.append(InstructionConstants.ALOAD_0); // put "this" back on the stack - for (int i = 0, len = stateTypes.length; i < len; i++) { - UnresolvedType bcelTX = BcelWorld.fromBcel(stateTypes[i]); - ResolvedType stateRTX = world.resolve(bcelTX, true); - if (stateRTX.isMissing()) { - world.getLint().cantFindType.signal( - new String[] { WeaverMessages.format(WeaverMessages.CANT_FIND_TYPE_DURING_AROUND_WEAVE_PREINIT, - bcelTX.getClassName()) }, getSourceLocation(), - new ISourceLocation[] { munger.getSourceLocation() }); - // IMessage msg = new Message( - // WeaverMessages.format(WeaverMessages.CANT_FIND_TYPE_DURING_AROUND_WEAVE_PREINIT,bcelTX.getClassName()), - // "",IMessage.ERROR,getSourceLocation(),null, - // new ISourceLocation[]{ munger.getSourceLocation()}); - // world.getMessageHandler().handleMessage(msg); - } - stateTempVar.appendConvertableArrayLoad(returnConversionCode, fact, i, stateRTX); - } - } else { - // pr226201 - Member mungerSignature = munger.getSignature(); - if (munger.getSignature() instanceof ResolvedMember) { - if (((ResolvedMember) mungerSignature).hasBackingGenericMember()) { - mungerSignature = ((ResolvedMember) mungerSignature).getBackingGenericMember(); - } - } - UnresolvedType returnType = mungerSignature.getReturnType(); - returnConversionCode = Utility.createConversion(getFactory(), BcelWorld.makeBcelType(returnType), - callbackMethod.getReturnType(), world.isInJava5Mode()); - if (!isFallsThrough()) { - returnConversionCode.append(InstructionFactory.createReturn(callbackMethod.getReturnType())); - } - } - - // initialize the bit flags for this shadow - int bitflags = 0x000000; - if (getKind().isTargetSameAsThis()) { - bitflags |= 0x010000; - } - if (hasThis()) { - bitflags |= 0x001000; - } - if (bindsThis(munger)) { - bitflags |= 0x000100; - } - if (hasTarget()) { - bitflags |= 0x000010; - } - if (bindsTarget(munger)) { - bitflags |= 0x000001; - } - - // ATAJ for @AJ aspect we need to link the closure with the joinpoint instance - if (munger.getConcreteAspect() != null && munger.getConcreteAspect().isAnnotationStyleAspect() - && munger.getDeclaringAspect() != null && munger.getDeclaringAspect().resolve(world).isAnnotationStyleAspect()) { - // stick the bitflags on the stack and call the variant of linkClosureAndJoinPoint that takes an int - closureInstantiation.append(fact.createConstant(Integer.valueOf(bitflags))); - closureInstantiation.append(Utility.createInvoke(getFactory(), getWorld(), - new MemberImpl(Member.METHOD, UnresolvedType.forName("org.aspectj.runtime.internal.AroundClosure"), - Modifier.PUBLIC, "linkClosureAndJoinPoint", String.format("%s%s", "(I)", "Lorg/aspectj/lang/ProceedingJoinPoint;")))); - } - - InstructionList advice = new InstructionList(); - advice.append(munger.getAdviceArgSetup(this, null, closureInstantiation)); - - // invoke the advice - advice.append(munger.getNonTestAdviceInstructions(this)); - advice.append(returnConversionCode); - if (getKind() == Shadow.MethodExecution && linenumber > 0) { - advice.getStart().addTargeter(new LineNumberTag(linenumber)); - } - - if (!hasDynamicTest) { - range.append(advice); - } else { - InstructionList callback = makeCallToCallback(callbackMethod); - InstructionList postCallback = new InstructionList(); - if (terminatesWithReturn()) { - callback.append(InstructionFactory.createReturn(callbackMethod.getReturnType())); - } else { - advice.append(InstructionFactory.createBranchInstruction(Constants.GOTO, - postCallback.append(InstructionConstants.NOP))); - } - range.append(munger.getTestInstructions(this, advice.getStart(), callback.getStart(), advice.getStart())); - range.append(advice); - range.append(callback); - range.append(postCallback); - } - } - - // exposed for testing - InstructionList makeCallToCallback(LazyMethodGen callbackMethod) { - InstructionFactory fact = getFactory(); - InstructionList callback = new InstructionList(); - if (thisVar != null) { - callback.append(InstructionConstants.ALOAD_0); - } - if (targetVar != null && targetVar != thisVar) { - callback.append(BcelRenderer.renderExpr(fact, world, targetVar)); - } - callback.append(BcelRenderer.renderExprs(fact, world, argVars)); - // remember to render tjps - if (thisJoinPointVar != null) { - callback.append(BcelRenderer.renderExpr(fact, world, thisJoinPointVar)); - } - callback.append(Utility.createInvoke(fact, callbackMethod)); - return callback; - } - - /** side-effect-free */ - private InstructionList makeClosureInstantiation(Member constructor, BcelVar holder) { - - // LazyMethodGen constructor) { - InstructionFactory fact = getFactory(); - BcelVar arrayVar = genTempVar(UnresolvedType.OBJECTARRAY); - // final Type objectArrayType = new ArrayType(Type.OBJECT, 1); - final InstructionList il = new InstructionList(); - int alen = getArgCount() + (thisVar == null ? 0 : 1) + ((targetVar != null && targetVar != thisVar) ? 1 : 0) - + (thisJoinPointVar == null ? 0 : 1); - il.append(Utility.createConstant(fact, alen)); - il.append(fact.createNewArray(Type.OBJECT, (short) 1)); - arrayVar.appendStore(il, fact); - - int stateIndex = 0; - if (thisVar != null) { - arrayVar.appendConvertableArrayStore(il, fact, stateIndex, thisVar); - thisVar.setPositionInAroundState(stateIndex); - stateIndex++; - } - if (targetVar != null && targetVar != thisVar) { - arrayVar.appendConvertableArrayStore(il, fact, stateIndex, targetVar); - targetVar.setPositionInAroundState(stateIndex); - stateIndex++; - } - for (int i = 0, len = getArgCount(); i < len; i++) { - arrayVar.appendConvertableArrayStore(il, fact, stateIndex, argVars[i]); - argVars[i].setPositionInAroundState(stateIndex); - stateIndex++; - } - if (thisJoinPointVar != null) { - arrayVar.appendConvertableArrayStore(il, fact, stateIndex, thisJoinPointVar); - thisJoinPointVar.setPositionInAroundState(stateIndex); - stateIndex++; - } - il.append(fact.createNew(new ObjectType(constructor.getDeclaringType().getName()))); - il.append(InstructionConstants.DUP); - arrayVar.appendLoad(il, fact); - il.append(Utility.createInvoke(fact, world, constructor)); - if (getKind() == PreInitialization) { - il.append(InstructionConstants.DUP); - holder.appendStore(il, fact); - } - return il; - } - - private IntMap makeProceedArgumentMap(BcelVar[] adviceArgs) { - // System.err.println("coming in with " + Arrays.asList(adviceArgs)); - - IntMap ret = new IntMap(); - for (int i = 0, len = adviceArgs.length; i < len; i++) { - BcelVar v = adviceArgs[i]; - if (v == null) { - continue; // XXX we don't know why this is required - } - int pos = v.getPositionInAroundState(); - if (pos >= 0) { // need this test to avoid args bound via cflow - ret.put(pos, i); - } - } - // System.err.println("returning " + ret); - - return ret; - } - - /** - * - * @param callbackMethod the method we will call back to when our run method gets called. - * @param proceedMap A map from state position to proceed argument position. May be non covering on state position. - */ - private LazyMethodGen makeClosureClassAndReturnConstructor(String closureClassName, LazyMethodGen callbackMethod, - IntMap proceedMap) { - String superClassName = "org.aspectj.runtime.internal.AroundClosure"; - Type objectArrayType = new ArrayType(Type.OBJECT, 1); - - LazyClassGen closureClass = new LazyClassGen(closureClassName, superClassName, getEnclosingClass().getFileName(), - Modifier.PUBLIC, new String[] {}, getWorld()); - closureClass.setMajorMinor(getEnclosingClass().getMajor(), getEnclosingClass().getMinor()); - InstructionFactory fact = new InstructionFactory(closureClass.getConstantPool()); - - // constructor - LazyMethodGen constructor = new LazyMethodGen(Modifier.PUBLIC, Type.VOID, "", new Type[] { objectArrayType }, - new String[] {}, closureClass); - InstructionList cbody = constructor.getBody(); - cbody.append(InstructionFactory.createLoad(Type.OBJECT, 0)); - cbody.append(InstructionFactory.createLoad(objectArrayType, 1)); - cbody.append(fact - .createInvoke(superClassName, "", Type.VOID, new Type[] { objectArrayType }, Constants.INVOKESPECIAL)); - cbody.append(InstructionFactory.createReturn(Type.VOID)); - - closureClass.addMethodGen(constructor); - - // Create the 'Object run(Object[])' method - LazyMethodGen runMethod = new LazyMethodGen(Modifier.PUBLIC, Type.OBJECT, "run", new Type[] { objectArrayType }, - new String[] {}, closureClass); - InstructionList mbody = runMethod.getBody(); - BcelVar proceedVar = new BcelVar(UnresolvedType.OBJECTARRAY.resolve(world), 1); - // int proceedVarIndex = 1; - BcelVar stateVar = new BcelVar(UnresolvedType.OBJECTARRAY.resolve(world), runMethod.allocateLocal(1)); - // int stateVarIndex = runMethod.allocateLocal(1); - mbody.append(InstructionFactory.createThis()); - mbody.append(fact.createGetField(superClassName, "state", objectArrayType)); - mbody.append(stateVar.createStore(fact)); - // mbody.append(fact.createStore(objectArrayType, stateVarIndex)); - - Type[] stateTypes = callbackMethod.getArgumentTypes(); - - for (int i = 0, len = stateTypes.length; i < len; i++) { - ResolvedType resolvedStateType = BcelWorld.fromBcel(stateTypes[i]).resolve(world); - if (proceedMap.hasKey(i)) { - mbody.append(proceedVar.createConvertableArrayLoad(fact, proceedMap.get(i), resolvedStateType)); - } else { - mbody.append(stateVar.createConvertableArrayLoad(fact, i, resolvedStateType)); - } - } - - mbody.append(Utility.createInvoke(fact, callbackMethod)); - - if (getKind() == PreInitialization) { - mbody.append(Utility.createSet(fact, AjcMemberMaker.aroundClosurePreInitializationField())); - mbody.append(InstructionConstants.ACONST_NULL); - } else { - mbody.append(Utility.createConversion(fact, callbackMethod.getReturnType(), Type.OBJECT)); - } - mbody.append(InstructionFactory.createReturn(Type.OBJECT)); - - closureClass.addMethodGen(runMethod); - - // class - getEnclosingClass().addGeneratedInner(closureClass); - - return constructor; - } - - // ---- extraction methods - - /** - * Extract the instructions in the shadow to a new method. - * - * @param extractedMethodName name for the new method - * @param extractedMethodVisibilityModifier visibility modifiers for the new method - * @param adviceSourceLocation source location of the advice affecting the shadow - * @param beingPlacedInInterface is this new method going into an interface - */ - LazyMethodGen extractShadowInstructionsIntoNewMethod(String extractedMethodName, int extractedMethodVisibilityModifier, - ISourceLocation adviceSourceLocation, List parameterNames, boolean beingPlacedInInterface) { - // LazyMethodGen.assertGoodBody(range.getBody(), extractedMethodName); - if (!getKind().allowsExtraction()) { - throw new BCException("Attempt to extract method from a shadow kind (" + getKind() - + ") that does not support this operation"); - } - LazyMethodGen newMethod = createShadowMethodGen(extractedMethodName, extractedMethodVisibilityModifier, parameterNames, beingPlacedInInterface); - IntMap remapper = makeRemap(); - range.extractInstructionsInto(newMethod, remapper, (getKind() != PreInitialization) && isFallsThrough()); - if (getKind() == PreInitialization) { - addPreInitializationReturnCode(newMethod, getSuperConstructorParameterTypes()); - } - getEnclosingClass().addMethodGen(newMethod, adviceSourceLocation); - return newMethod; - } - - private void addPreInitializationReturnCode(LazyMethodGen extractedMethod, Type[] superConstructorTypes) { - InstructionList body = extractedMethod.getBody(); - final InstructionFactory fact = getFactory(); - - BcelVar arrayVar = new BcelVar(world.getCoreType(UnresolvedType.OBJECTARRAY), extractedMethod.allocateLocal(1)); - - int len = superConstructorTypes.length; - - body.append(Utility.createConstant(fact, len)); - - body.append(fact.createNewArray(Type.OBJECT, (short) 1)); - arrayVar.appendStore(body, fact); - - for (int i = len - 1; i >= 0; i++) { - // convert thing on top of stack to object - body.append(Utility.createConversion(fact, superConstructorTypes[i], Type.OBJECT)); - // push object array - arrayVar.appendLoad(body, fact); - // swap - body.append(InstructionConstants.SWAP); - // do object array store. - body.append(Utility.createConstant(fact, i)); - body.append(InstructionConstants.SWAP); - body.append(InstructionFactory.createArrayStore(Type.OBJECT)); - } - arrayVar.appendLoad(body, fact); - body.append(InstructionConstants.ARETURN); - } - - private Type[] getSuperConstructorParameterTypes() { - // assert getKind() == PreInitialization - InstructionHandle superCallHandle = getRange().getEnd().getNext(); - InvokeInstruction superCallInstruction = (InvokeInstruction) superCallHandle.getInstruction(); - return superCallInstruction.getArgumentTypes(getEnclosingClass().getConstantPool()); - } - - /** - * make a map from old frame location to new frame location. Any unkeyed frame location picks out a copied local - */ - private IntMap makeRemap() { - IntMap ret = new IntMap(5); - int reti = 0; - if (thisVar != null) { - ret.put(0, reti++); // thisVar guaranteed to be 0 - } - if (targetVar != null && targetVar != thisVar) { - ret.put(targetVar.getSlot(), reti++); - } - for (int i = 0, len = argVars.length; i < len; i++) { - ret.put(argVars[i].getSlot(), reti); - reti += argVars[i].getType().getSize(); - } - if (thisJoinPointVar != null) { - ret.put(thisJoinPointVar.getSlot(), reti++); - } - // we not only need to put the arguments, we also need to remap their - // aliases, which we so helpfully put into temps at the beginning of this join - // point. - if (!getKind().argsOnStack()) { - int oldi = 0; - int newi = 0; - // if we're passing in a this and we're not argsOnStack we're always - // passing in a target too - if (arg0HoldsThis()) { - ret.put(0, 0); - oldi++; - newi += 1; - } - // assert targetVar == thisVar - for (int i = 0; i < getArgCount(); i++) { - UnresolvedType type = getArgType(i); - ret.put(oldi, newi); - oldi += type.getSize(); - newi += type.getSize(); - } - } - - // System.err.println("making remap for : " + this); - // if (targetVar != null) System.err.println("target slot : " + targetVar.getSlot()); - // if (thisVar != null) System.err.println(" this slot : " + thisVar.getSlot()); - // System.err.println(ret); - - return ret; - } - - /** - * The new method always static. It may take some extra arguments: this, target. If it's argsOnStack, then it must take both - * this/target If it's argsOnFrame, it shares this and target. ??? rewrite this to do less array munging, please - */ - private LazyMethodGen createShadowMethodGen(String newMethodName, int visibilityModifier, List parameterNames, boolean beingPlacedInInterface) { - Type[] shadowParameterTypes = BcelWorld.makeBcelTypes(getArgTypes()); - int modifiers = (world.useFinal() && !beingPlacedInInterface ? Modifier.FINAL : 0) | Modifier.STATIC | visibilityModifier; - if (targetVar != null && targetVar != thisVar) { - UnresolvedType targetType = getTargetType(); - targetType = ensureTargetTypeIsCorrect(targetType); - // see pr109728,pr229910 - this fixes the case when the declaring class is sometype 'X' but the (gs)etfield - // in the bytecode refers to a subtype of 'X'. This makes sure we use the type originally - // mentioned in the fieldget instruction as the method parameter and *not* the type upon which the - // field is declared because when the instructions are extracted into the new around body, - // they will still refer to the subtype. - if ((getKind() == FieldGet || getKind() == FieldSet) && getActualTargetType() != null - && !getActualTargetType().equals(targetType.getName())) { - targetType = UnresolvedType.forName(getActualTargetType()).resolve(world); - } - ResolvedMember resolvedMember = getSignature().resolve(world); - - // pr230075, pr197719 - if (resolvedMember != null && Modifier.isProtected(resolvedMember.getModifiers()) - && !samePackage(resolvedMember.getDeclaringType().getPackageName(), getEnclosingType().getPackageName()) - && !resolvedMember.getName().equals("clone")) { - if (!hasThis()) { // pr197719 - static accessor has been created to handle the call - if (Modifier.isStatic(enclosingMethod.getAccessFlags()) && enclosingMethod.getName().startsWith("access$")) { - targetType = BcelWorld.fromBcel(enclosingMethod.getArgumentTypes()[0]); - } - } else { - if (!targetType.resolve(world).isAssignableFrom(getThisType().resolve(world))) { - throw new BCException("bad bytecode"); - } - targetType = getThisType(); - } - } - parameterNames.add("target"); - // There is a 'target' and it is not the same as 'this', so add it to the parameter list - shadowParameterTypes = addTypeToFront(BcelWorld.makeBcelType(targetType), shadowParameterTypes); - } - - if (thisVar != null) { - UnresolvedType thisType = getThisType(); - parameterNames.add(0, "ajc$this"); - shadowParameterTypes = addTypeToFront(BcelWorld.makeBcelType(thisType), shadowParameterTypes); - } - - if (this.getKind() == Shadow.FieldSet || this.getKind() == Shadow.FieldGet) { - parameterNames.add(getSignature().getName()); - } else { - String[] pnames = getSignature().getParameterNames(world); - if (pnames != null) { - for (int i = 0; i < pnames.length; i++) { - if (i == 0 && pnames[i].equals("this")) { - parameterNames.add("ajc$this"); - } else { - parameterNames.add(pnames[i]); - } - } - } - } - - // We always want to pass down thisJoinPoint in case we have already woven - // some advice in here. If we only have a single piece of around advice on a - // join point, it is unnecessary to accept (and pass) tjp. - if (thisJoinPointVar != null) { - parameterNames.add("thisJoinPoint"); - shadowParameterTypes = addTypeToEnd(LazyClassGen.tjpType, shadowParameterTypes); - } - - UnresolvedType returnType; - if (getKind() == PreInitialization) { - returnType = UnresolvedType.OBJECTARRAY; - } else { - if (getKind() == ConstructorCall) { - returnType = getSignature().getDeclaringType(); - } else if (getKind() == FieldSet) { - returnType = UnresolvedType.VOID; - } else { - returnType = getSignature().getReturnType().resolve(world); - // returnType = getReturnType(); // for this and above lines, see pr137496 - } - } - return new LazyMethodGen(modifiers, BcelWorld.makeBcelType(returnType), newMethodName, shadowParameterTypes, - NoDeclaredExceptions, getEnclosingClass()); - } - - private boolean samePackage(String p1, String p2) { - if (p1 == null) { - return p2 == null; - } - if (p2 == null) { - return false; - } - return p1.equals(p2); - } - - private Type[] addTypeToFront(Type type, Type[] types) { - int len = types.length; - Type[] ret = new Type[len + 1]; - ret[0] = type; - System.arraycopy(types, 0, ret, 1, len); - return ret; - } - - private Type[] addTypeToEnd(Type type, Type[] types) { - int len = types.length; - Type[] ret = new Type[len + 1]; - ret[len] = type; - System.arraycopy(types, 0, ret, 0, len); - return ret; - } - - public BcelVar genTempVar(UnresolvedType utype) { - ResolvedType rtype = utype.resolve(world); - return new BcelVar(rtype, genTempVarIndex(rtype.getSize())); - } - - // public static final boolean CREATE_TEMP_NAMES = true; - - public BcelVar genTempVar(UnresolvedType typeX, String localName) { - BcelVar tv = genTempVar(typeX); - - // if (CREATE_TEMP_NAMES) { - // for (InstructionHandle ih = range.getStart(); ih != range.getEnd(); ih = ih.getNext()) { - // if (Range.isRangeHandle(ih)) continue; - // ih.addTargeter(new LocalVariableTag(typeX, localName, tv.getSlot())); - // } - // } - return tv; - } - - // eh doesn't think we need to garbage collect these (64K is a big number...) - private int genTempVarIndex(int size) { - return enclosingMethod.allocateLocal(size); - } - - public InstructionFactory getFactory() { - return getEnclosingClass().getFactory(); - } - - @Override - public ISourceLocation getSourceLocation() { - int sourceLine = getSourceLine(); - if (sourceLine == 0 || sourceLine == -1) { - // Thread.currentThread().dumpStack(); - // System.err.println(this + ": " + range); - return getEnclosingClass().getType().getSourceLocation(); - } else { - // For staticinitialization, if we have a nice offset, don't build a new source loc - if (getKind() == Shadow.StaticInitialization && getEnclosingClass().getType().getSourceLocation().getOffset() != 0) { - return getEnclosingClass().getType().getSourceLocation(); - } else { - int offset = 0; - Kind kind = getKind(); - if ((kind == MethodExecution) || (kind == ConstructorExecution) || (kind == AdviceExecution) - || (kind == StaticInitialization) || (kind == PreInitialization) || (kind == Initialization)) { - if (getEnclosingMethod().hasDeclaredLineNumberInfo()) { - offset = getEnclosingMethod().getDeclarationOffset(); - } - } - return getEnclosingClass().getType().getSourceContext().makeSourceLocation(sourceLine, offset); - } - } - } - - public Shadow getEnclosingShadow() { - return enclosingShadow; - } - - public LazyMethodGen getEnclosingMethod() { - return enclosingMethod; - } - - public boolean isFallsThrough() { - return !terminatesWithReturn(); - } - - public void setActualTargetType(String className) { - this.actualInstructionTargetType = className; - } - - public String getActualTargetType() { - return actualInstructionTargetType; - } -} \ No newline at end of file diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelTypeMunger.java b/weaver/src/org/aspectj/weaver/bcel/BcelTypeMunger.java deleted file mode 100644 index b92760fe9..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/BcelTypeMunger.java +++ /dev/null @@ -1,2116 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * Alexandre Vasseur @AspectJ ITDs - * ******************************************************************/ - -package org.aspectj.weaver.bcel; - -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.aspectj.apache.bcel.Constants; -import org.aspectj.apache.bcel.classfile.ClassFormatException; -import org.aspectj.apache.bcel.classfile.ConstantPool; -import org.aspectj.apache.bcel.classfile.Signature; -import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; -import org.aspectj.apache.bcel.generic.FieldGen; -import org.aspectj.apache.bcel.generic.InstructionBranch; -import org.aspectj.apache.bcel.generic.InstructionConstants; -import org.aspectj.apache.bcel.generic.InstructionFactory; -import org.aspectj.apache.bcel.generic.InstructionHandle; -import org.aspectj.apache.bcel.generic.InstructionList; -import org.aspectj.apache.bcel.generic.InvokeInstruction; -import org.aspectj.apache.bcel.generic.Type; -import org.aspectj.asm.AsmManager; -import org.aspectj.asm.IProgramElement; -import org.aspectj.bridge.IMessage; -import org.aspectj.bridge.ISourceLocation; -import org.aspectj.bridge.Message; -import org.aspectj.bridge.MessageUtil; -import org.aspectj.bridge.WeaveMessage; -import org.aspectj.bridge.context.CompilationAndWeavingContext; -import org.aspectj.bridge.context.ContextToken; -import org.aspectj.weaver.AjcMemberMaker; -import org.aspectj.weaver.AnnotationAJ; -import org.aspectj.weaver.AnnotationOnTypeMunger; -import org.aspectj.weaver.BCException; -import org.aspectj.weaver.ConcreteTypeMunger; -import org.aspectj.weaver.Member; -import org.aspectj.weaver.MemberUtils; -import org.aspectj.weaver.MethodDelegateTypeMunger; -import org.aspectj.weaver.NameMangler; -import org.aspectj.weaver.NewConstructorTypeMunger; -import org.aspectj.weaver.NewFieldTypeMunger; -import org.aspectj.weaver.NewMemberClassTypeMunger; -import org.aspectj.weaver.NewMethodTypeMunger; -import org.aspectj.weaver.NewParentTypeMunger; -import org.aspectj.weaver.PerObjectInterfaceTypeMunger; -import org.aspectj.weaver.PrivilegedAccessMunger; -import org.aspectj.weaver.ResolvedMember; -import org.aspectj.weaver.ResolvedMemberImpl; -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.ResolvedTypeMunger; -import org.aspectj.weaver.Shadow; -import org.aspectj.weaver.TypeVariableReference; -import org.aspectj.weaver.UnresolvedType; -import org.aspectj.weaver.WeaverMessages; -import org.aspectj.weaver.WeaverStateInfo; -import org.aspectj.weaver.World; -import org.aspectj.weaver.model.AsmRelationshipProvider; -import org.aspectj.weaver.patterns.DeclareAnnotation; -import org.aspectj.weaver.patterns.Pointcut; - -public class BcelTypeMunger extends ConcreteTypeMunger { - - public BcelTypeMunger(ResolvedTypeMunger munger, ResolvedType aspectType) { - super(munger, aspectType); - } - - @Override - public String toString() { - return "(BcelTypeMunger " + getMunger() + ")"; - } - - @Override - public boolean shouldOverwrite() { - return false; - } - - public boolean munge(BcelClassWeaver weaver) { - ContextToken tok = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.MUNGING_WITH, this); - boolean changed = false; - boolean worthReporting = true; - - if (weaver.getWorld().isOverWeaving()) { - WeaverStateInfo typeWeaverState = weaver.getLazyClassGen().getType().getWeaverState(); - if (typeWeaverState != null && typeWeaverState.isAspectAlreadyApplied(getAspectType())) { - return false; - } - } - - if (munger.getKind() == ResolvedTypeMunger.Field) { - changed = mungeNewField(weaver, (NewFieldTypeMunger) munger); - } else if (munger.getKind() == ResolvedTypeMunger.Method) { - changed = mungeNewMethod(weaver, (NewMethodTypeMunger) munger); - } else if (munger.getKind() == ResolvedTypeMunger.InnerClass) { - changed = mungeNewMemberType(weaver, (NewMemberClassTypeMunger) munger); - } else if (munger.getKind() == ResolvedTypeMunger.MethodDelegate2) { - changed = mungeMethodDelegate(weaver, (MethodDelegateTypeMunger) munger); - } else if (munger.getKind() == ResolvedTypeMunger.FieldHost) { - changed = mungeFieldHost(weaver, (MethodDelegateTypeMunger.FieldHostTypeMunger) munger); - } else if (munger.getKind() == ResolvedTypeMunger.PerObjectInterface) { - changed = mungePerObjectInterface(weaver, (PerObjectInterfaceTypeMunger) munger); - worthReporting = false; - } else if (munger.getKind() == ResolvedTypeMunger.PerTypeWithinInterface) { - // PTWIMPL Transform the target type (add the aspect instance field) - changed = mungePerTypeWithinTransformer(weaver); - worthReporting = false; - } else if (munger.getKind() == ResolvedTypeMunger.PrivilegedAccess) { - changed = mungePrivilegedAccess(weaver, (PrivilegedAccessMunger) munger); - worthReporting = false; - } else if (munger.getKind() == ResolvedTypeMunger.Constructor) { - changed = mungeNewConstructor(weaver, (NewConstructorTypeMunger) munger); - } else if (munger.getKind() == ResolvedTypeMunger.Parent) { - changed = mungeNewParent(weaver, (NewParentTypeMunger) munger); - } else if (munger.getKind() == ResolvedTypeMunger.AnnotationOnType) { - changed = mungeNewAnnotationOnType(weaver, (AnnotationOnTypeMunger) munger); - worthReporting = false; - } else { - throw new RuntimeException("unimplemented"); - } - - if (changed && munger.changesPublicSignature()) { - WeaverStateInfo info = weaver.getLazyClassGen().getOrCreateWeaverStateInfo(weaver.getReweavableMode()); - info.addConcreteMunger(this); - } - - if (changed && worthReporting) { - ResolvedType declaringAspect = null; - AsmManager model = ((BcelWorld) getWorld()).getModelAsAsmManager(); - if (model != null) { - if (munger instanceof NewParentTypeMunger) { - NewParentTypeMunger nptMunger = (NewParentTypeMunger) munger; - declaringAspect = nptMunger.getDeclaringType(); - if (declaringAspect.isParameterizedOrGenericType()) { - declaringAspect = declaringAspect.getRawType(); - } - ResolvedType thisAspect = getAspectType(); - AsmRelationshipProvider.addRelationship(model, weaver.getLazyClassGen().getType(), munger, thisAspect); - - // Add a relationship on the actual declaring aspect too - if (!thisAspect.equals(declaringAspect)) { - // Might be the case the declaring aspect is generic and thisAspect is parameterizing it. In that case - // record the actual parameterizations - - ResolvedType target = weaver.getLazyClassGen().getType(); - ResolvedType newParent = nptMunger.getNewParent(); - IProgramElement thisAspectNode = model.getHierarchy().findElementForType(thisAspect.getPackageName(), - thisAspect.getClassName()); - Map> declareParentsMap = thisAspectNode.getDeclareParentsMap(); - if (declareParentsMap == null) { - declareParentsMap = new HashMap>(); - thisAspectNode.setDeclareParentsMap(declareParentsMap); - } - String tname = target.getName(); - String pname = newParent.getName(); - List newparents = declareParentsMap.get(tname); - if (newparents == null) { - newparents = new ArrayList(); - declareParentsMap.put(tname, newparents); - } - newparents.add(pname); - AsmRelationshipProvider.addRelationship(model, weaver.getLazyClassGen().getType(), munger, declaringAspect); - } - } else { - declaringAspect = getAspectType(); - AsmRelationshipProvider.addRelationship(model, weaver.getLazyClassGen().getType(), munger, declaringAspect); - } - } - } - - // TAG: WeavingMessage - if (changed && worthReporting && munger != null && !weaver.getWorld().getMessageHandler().isIgnoring(IMessage.WEAVEINFO)) { - String tName = weaver.getLazyClassGen().getType().getSourceLocation().getSourceFile().getName(); - if (tName.indexOf("no debug info available") != -1) { - tName = "no debug info available"; - } else { - tName = getShortname(weaver.getLazyClassGen().getType().getSourceLocation().getSourceFile().getPath()); - } - String fName = getShortname(getAspectType().getSourceLocation().getSourceFile().getPath()); - if (munger.getKind().equals(ResolvedTypeMunger.Parent)) { - // This message could come out of AjLookupEnvironment.addParent - // if doing parents munging at compile time only... - NewParentTypeMunger parentTM = (NewParentTypeMunger) munger; - if (parentTM.isMixin()) { - weaver.getWorld() - .getMessageHandler() - .handleMessage( - WeaveMessage.constructWeavingMessage(WeaveMessage.WEAVEMESSAGE_MIXIN, new String[] { - parentTM.getNewParent().getName(), fName, weaver.getLazyClassGen().getType().getName(), - tName }, weaver.getLazyClassGen().getClassName(), getAspectType().getName())); - } else { - if (parentTM.getNewParent().isInterface()) { - weaver.getWorld() - .getMessageHandler() - .handleMessage( - WeaveMessage.constructWeavingMessage(WeaveMessage.WEAVEMESSAGE_DECLAREPARENTSIMPLEMENTS, - new String[] { weaver.getLazyClassGen().getType().getName(), tName, - parentTM.getNewParent().getName(), fName }, weaver.getLazyClassGen() - .getClassName(), getAspectType().getName())); - } else { - weaver.getWorld() - .getMessageHandler() - .handleMessage( - WeaveMessage.constructWeavingMessage(WeaveMessage.WEAVEMESSAGE_DECLAREPARENTSEXTENDS, - new String[] { weaver.getLazyClassGen().getType().getName(), tName, - parentTM.getNewParent().getName(), fName })); - // TAG: WeavingMessage DECLARE PARENTS: EXTENDS - // reportDeclareParentsMessage(WeaveMessage. - // WEAVEMESSAGE_DECLAREPARENTSEXTENDS,sourceType,parent); - - } - } - } else if (munger.getKind().equals(ResolvedTypeMunger.FieldHost)) { - // hidden - } else { - ResolvedMember declaredSig = munger.getSignature(); - String fromString = fName + ":'" + declaredSig + "'"; - // if (declaredSig==null) declaredSig= munger.getSignature(); - String kindString = munger.getKind().toString().toLowerCase(); - if (kindString.equals("innerclass")) { - kindString = "member class"; - fromString = fName; - } - weaver.getWorld() - .getMessageHandler() - .handleMessage( - WeaveMessage.constructWeavingMessage(WeaveMessage.WEAVEMESSAGE_ITD, new String[] { - weaver.getLazyClassGen().getType().getName(), tName, kindString, getAspectType().getName(), - fromString }, weaver.getLazyClassGen().getClassName(), getAspectType().getName())); - } - } - - CompilationAndWeavingContext.leavingPhase(tok); - return changed; - } - - private String getShortname(String path) { - int takefrom = path.lastIndexOf('/'); - if (takefrom == -1) { - takefrom = path.lastIndexOf('\\'); - } - return path.substring(takefrom + 1); - } - - private boolean mungeNewAnnotationOnType(BcelClassWeaver weaver, AnnotationOnTypeMunger munger) { - // FIXME asc this has already been done up front, need to do it here too? - try { - BcelAnnotation anno = (BcelAnnotation) munger.getNewAnnotation(); - weaver.getLazyClassGen().addAnnotation(anno.getBcelAnnotation()); - } catch (ClassCastException cce) { - throw new IllegalStateException("DiagnosticsFor318237: The typemunger "+munger+" contains an annotation of type "+ - munger.getNewAnnotation().getClass().getName()+" when it should be a BcelAnnotation",cce); - } - return true; - } - - /** - * For a long time, AspectJ did not allow binary weaving of declare parents. This restriction is now lifted but could do with - * more testing! - */ - private boolean mungeNewParent(BcelClassWeaver weaver, NewParentTypeMunger typeTransformer) { - LazyClassGen newParentTarget = weaver.getLazyClassGen(); - ResolvedType newParent = typeTransformer.getNewParent(); - - boolean performChange = true; - performChange = enforceDecpRule1_abstractMethodsImplemented(weaver, typeTransformer.getSourceLocation(), newParentTarget, - newParent); - performChange = enforceDecpRule2_cantExtendFinalClass(weaver, typeTransformer.getSourceLocation(), newParentTarget, - newParent) && performChange; - - List methods = newParent.getMethodsWithoutIterator(false, true, false); - for (ResolvedMember method : methods) { - if (!method.getName().equals("")) { - LazyMethodGen subMethod = findMatchingMethod(newParentTarget, method); - // FIXME asc is this safe for all bridge methods? - if (subMethod != null && !subMethod.isBridgeMethod()) { - if (!(subMethod.isSynthetic() && method.isSynthetic())) { - if (!(subMethod.isStatic() && subMethod.getName().startsWith("access$"))) { - // ignore generated accessors - performChange = enforceDecpRule3_visibilityChanges(weaver, newParent, method, subMethod) - && performChange; - performChange = enforceDecpRule4_compatibleReturnTypes(weaver, method, subMethod) && performChange; - performChange = enforceDecpRule5_cantChangeFromStaticToNonstatic(weaver, - typeTransformer.getSourceLocation(), method, subMethod) - && performChange; - } - } - } - } - } - if (!performChange) { - // A rule was violated and an error message already reported - return false; - } - - if (newParent.isClass()) { - // Changing the supertype - if (!attemptToModifySuperCalls(weaver, newParentTarget, newParent)) { - return false; - } - newParentTarget.setSuperClass(newParent); - } else { - // Add a new interface - newParentTarget.addInterface(newParent, getSourceLocation()); - } - return true; - } - - /** - * Rule 1: For the declare parents to be allowed, the target type must override and implement inherited abstract methods (if the - * type is not declared abstract) - */ - private boolean enforceDecpRule1_abstractMethodsImplemented(BcelClassWeaver weaver, ISourceLocation mungerLoc, - LazyClassGen newParentTarget, ResolvedType newParent) { - // Ignore abstract classes or interfaces - if (newParentTarget.isAbstract() || newParentTarget.isInterface()) { - return true; - } - boolean ruleCheckingSucceeded = true; - List newParentMethods = newParent.getMethodsWithoutIterator(false, true, false); - for (ResolvedMember newParentMethod : newParentMethods) { - String newParentMethodName = newParentMethod.getName(); - // Ignore abstract ajc$interField prefixed methods - if (newParentMethod.isAbstract() && !newParentMethodName.startsWith("ajc$interField")) { - ResolvedMember discoveredImpl = null; - List targetMethods = newParentTarget.getType().getMethodsWithoutIterator(false, true, false); - for (ResolvedMember targetMethod : targetMethods) { - if (!targetMethod.isAbstract() && targetMethod.getName().equals(newParentMethodName)) { - String newParentMethodSig = newParentMethod.getParameterSignature(); // ([TT;) - String targetMethodSignature = targetMethod.getParameterSignature(); // ([Ljava/lang/Object;) - // could be a match - if (targetMethodSignature.equals(newParentMethodSig)) { - discoveredImpl = targetMethod; - } else { - // Does the erasure match? In which case a bridge method will be created later to - // satisfy the abstract method - if (targetMethod.hasBackingGenericMember() - && targetMethod.getBackingGenericMember().getParameterSignature().equals(newParentMethodSig)) { - discoveredImpl = targetMethod; - } else if (newParentMethod.hasBackingGenericMember()) { - if (newParentMethod.getBackingGenericMember().getParameterSignature().equals(targetMethodSignature)) { // newParentMethod.getBackingGenericMember().getParameterSignature gives: (Pjava/util/List;) targetMethodSignature= (Ljava/util/List;) - discoveredImpl = targetMethod; - } else if (targetMethod instanceof BcelMethod) { - // BcelMethod does not have backing generic member set (need to investigate why). For now, special case here: - UnresolvedType[] targetMethodGenericParameterTypes = targetMethod.getGenericParameterTypes(); - if (targetMethodGenericParameterTypes !=null) { - StringBuilder b = new StringBuilder("("); - for (UnresolvedType p: targetMethodGenericParameterTypes) { - b.append(p.getSignature()); - } - b.append(')'); - if (b.toString().equals(newParentMethodSig)) { - discoveredImpl = targetMethod; - } - } - } - } - } - if (discoveredImpl != null) { - break; - } - } - } - if (discoveredImpl == null) { - // didnt find a valid implementation, lets check the - // ITDs on this type to see if they satisfy it - boolean satisfiedByITD = false; - for (ConcreteTypeMunger m : newParentTarget.getType().getInterTypeMungersIncludingSupers()) { - if (m.getMunger() != null && m.getMunger().getKind() == ResolvedTypeMunger.Method) { - ResolvedMember sig = m.getSignature(); - if (!Modifier.isAbstract(sig.getModifiers())) { - // If the ITD shares a type variable with some target type, we need to tailor it - // for that type - if (m.isTargetTypeParameterized()) { - ResolvedType genericOnType = getWorld().resolve(sig.getDeclaringType()).getGenericType(); - ResolvedType actualOccurrence = newParent.discoverActualOccurrenceOfTypeInHierarchy(genericOnType); - if (actualOccurrence == null) { - // Handle the case where the ITD is onto the type targeted by the declare parents (PR478003) - actualOccurrence = newParentTarget.getType().discoverActualOccurrenceOfTypeInHierarchy(genericOnType); - } - m = m.parameterizedFor(actualOccurrence); - // possible sig change when type parameters filled in - sig = m.getSignature(); - } - if (ResolvedType.matches( - AjcMemberMaker.interMethod(sig, m.getAspectType(), - sig.getDeclaringType().resolve(weaver.getWorld()).isInterface()), newParentMethod)) { - satisfiedByITD = true; - } - } - } else if (m.getMunger() != null && m.getMunger().getKind() == ResolvedTypeMunger.MethodDelegate2) { - // AV - that should be enough, no need to check more - satisfiedByITD = true; - } - } - if (!satisfiedByITD) { - error(weaver, - "The type " + newParentTarget.getName() + " must implement the inherited abstract method " - + newParentMethod.getDeclaringType() + "." + newParentMethodName - + newParentMethod.getParameterSignature(), newParentTarget.getType().getSourceLocation(), - new ISourceLocation[] { newParentMethod.getSourceLocation(), mungerLoc }); - ruleCheckingSucceeded = false; - } - } - } - } - return ruleCheckingSucceeded; - } - - /** - * Rule 2. Can't extend final types - */ - private boolean enforceDecpRule2_cantExtendFinalClass(BcelClassWeaver weaver, ISourceLocation transformerLoc, - LazyClassGen targetType, ResolvedType newParent) { - if (newParent.isFinal()) { - error(weaver, "Cannot make type " + targetType.getName() + " extend final class " + newParent.getName(), targetType - .getType().getSourceLocation(), new ISourceLocation[] { transformerLoc }); - return false; - } - return true; - } - - /** - * Rule 3. Can't narrow visibility of methods when overriding - */ - private boolean enforceDecpRule3_visibilityChanges(BcelClassWeaver weaver, ResolvedType newParent, ResolvedMember superMethod, - LazyMethodGen subMethod) { - boolean cont = true; - if (Modifier.isPublic(superMethod.getModifiers())) { - if (subMethod.isProtected() || subMethod.isDefault() || subMethod.isPrivate()) { - weaver.getWorld() - .getMessageHandler() - .handleMessage( - MessageUtil.error("Cannot reduce the visibility of the inherited method '" + superMethod - + "' from " + newParent.getName(), superMethod.getSourceLocation())); - cont = false; - } - } else if (Modifier.isProtected(superMethod.getModifiers())) { - if (subMethod.isDefault() || subMethod.isPrivate()) { - weaver.getWorld() - .getMessageHandler() - .handleMessage( - MessageUtil.error("Cannot reduce the visibility of the inherited method '" + superMethod - + "' from " + newParent.getName(), superMethod.getSourceLocation())); - cont = false; - } - } else if (superMethod.isDefault()) { - if (subMethod.isPrivate()) { - weaver.getWorld() - .getMessageHandler() - .handleMessage( - MessageUtil.error("Cannot reduce the visibility of the inherited method '" + superMethod - + "' from " + newParent.getName(), superMethod.getSourceLocation())); - cont = false; - } - } - return cont; - } - - /** - * Rule 4. Can't have incompatible return types - */ - private boolean enforceDecpRule4_compatibleReturnTypes(BcelClassWeaver weaver, ResolvedMember superMethod, - LazyMethodGen subMethod) { - boolean cont = true; - String superReturnTypeSig = superMethod.getGenericReturnType().getSignature(); // eg. Pjava/util/Collection - String subReturnTypeSig = subMethod.getGenericReturnTypeSignature(); - superReturnTypeSig = superReturnTypeSig.replace('.', '/'); - subReturnTypeSig = subReturnTypeSig.replace('.', '/'); - if (!superReturnTypeSig.equals(subReturnTypeSig)) { - // Check for covariance - ResolvedType subType = weaver.getWorld().resolve(subMethod.getReturnType()); - ResolvedType superType = weaver.getWorld().resolve(superMethod.getReturnType()); - if (!superType.isAssignableFrom(subType)) { - weaver.getWorld() - .getMessageHandler() - .handleMessage( - MessageUtil.error("The return type is incompatible with " + superMethod.getDeclaringType() + "." - + superMethod.getName() + superMethod.getParameterSignature(), - subMethod.getSourceLocation())); - // this just might be a better error message... - // "The return type '"+subReturnTypeSig+ - // "' is incompatible with the overridden method " - // +superMethod.getDeclaringType()+"."+ - // superMethod.getName()+superMethod.getParameterSignature()+ - // " which returns '"+superReturnTypeSig+"'", - cont = false; - } - } - return cont; - } - - /** - * Rule5. Method overrides can't change the staticality (word?) - you can't override and make an instance method static or - * override and make a static method an instance method. - */ - private boolean enforceDecpRule5_cantChangeFromStaticToNonstatic(BcelClassWeaver weaver, ISourceLocation mungerLoc, - ResolvedMember superMethod, LazyMethodGen subMethod) { - boolean superMethodStatic = Modifier.isStatic(superMethod.getModifiers()); - if (superMethodStatic && !subMethod.isStatic()) { - error(weaver, "This instance method " + subMethod.getName() + subMethod.getParameterSignature() - + " cannot override the static method from " + superMethod.getDeclaringType().getName(), - subMethod.getSourceLocation(), new ISourceLocation[] { mungerLoc }); - return false; - } else if (!superMethodStatic && subMethod.isStatic()) { - error(weaver, "The static method " + subMethod.getName() + subMethod.getParameterSignature() - + " cannot hide the instance method from " + superMethod.getDeclaringType().getName(), - subMethod.getSourceLocation(), new ISourceLocation[] { mungerLoc }); - return false; - } - return true; - } - - public void error(BcelClassWeaver weaver, String text, ISourceLocation primaryLoc, ISourceLocation[] extraLocs) { - IMessage msg = new Message(text, primaryLoc, true, extraLocs); - weaver.getWorld().getMessageHandler().handleMessage(msg); - } - - /** - * Search the specified type for a particular method - do not use the return value in the comparison as it is not considered for - * overriding. - */ - private LazyMethodGen findMatchingMethod(LazyClassGen type, ResolvedMember searchMethod) { - String searchName = searchMethod.getName(); - String searchSig = searchMethod.getParameterSignature(); - for (LazyMethodGen method : type.getMethodGens()) { - if (method.getName().equals(searchName) && method.getParameterSignature().equals(searchSig)) { - return method; - } - } - return null; - } - - /** - * The main part of implementing declare parents extends. Modify super ctor calls to target the new type. - */ - public boolean attemptToModifySuperCalls(BcelClassWeaver weaver, LazyClassGen newParentTarget, ResolvedType newParent) { - ResolvedType currentParentType = newParentTarget.getSuperClass(); - if (currentParentType.getGenericType() != null) { - currentParentType = currentParentType.getGenericType(); - } - String currentParent = currentParentType.getName(); - if (newParent.getGenericType() != null) { - newParent = newParent.getGenericType(); // target new super calls at - } - // the generic type if its raw or parameterized - List mgs = newParentTarget.getMethodGens(); - - // Look for ctors to modify - for (LazyMethodGen aMethod : mgs) { - if (LazyMethodGen.isConstructor(aMethod)) { - InstructionList insList = aMethod.getBody(); - InstructionHandle handle = insList.getStart(); - while (handle != null) { - if (handle.getInstruction().opcode == Constants.INVOKESPECIAL) { - ConstantPool cpg = newParentTarget.getConstantPool(); - InvokeInstruction invokeSpecial = (InvokeInstruction) handle.getInstruction(); - if (invokeSpecial.getClassName(cpg).equals(currentParent) - && invokeSpecial.getMethodName(cpg).equals("")) { - // System.err.println("Transforming super call '" + invokeSpecial.getSignature(cpg) + "'"); - - // 1. Check there is a ctor in the new parent with - // the same signature - ResolvedMember newCtor = getConstructorWithSignature(newParent, invokeSpecial.getSignature(cpg)); - - if (newCtor == null) { - - // 2. Check ITDCs to see if the necessary ctor is provided that way - boolean satisfiedByITDC = false; - for (Iterator ii = newParentTarget.getType() - .getInterTypeMungersIncludingSupers().iterator(); ii.hasNext() && !satisfiedByITDC;) { - ConcreteTypeMunger m = ii.next(); - if (m.getMunger() instanceof NewConstructorTypeMunger) { - if (m.getSignature().getSignature().equals(invokeSpecial.getSignature(cpg))) { - satisfiedByITDC = true; - } - } - } - - if (!satisfiedByITDC) { - String csig = createReadableCtorSig(newParent, cpg, invokeSpecial); - weaver.getWorld() - .getMessageHandler() - .handleMessage( - MessageUtil.error( - "Unable to modify hierarchy for " + newParentTarget.getClassName() - + " - the constructor " + csig + " is missing", - this.getSourceLocation())); - return false; - } - } - - int idx = cpg.addMethodref(newParent.getName(), invokeSpecial.getMethodName(cpg), - invokeSpecial.getSignature(cpg)); - invokeSpecial.setIndex(idx); - } - } - handle = handle.getNext(); - } - } - } - return true; - } - - /** - * Creates a nice signature for the ctor, something like "(int,Integer,String)" - */ - private String createReadableCtorSig(ResolvedType newParent, ConstantPool cpg, InvokeInstruction invokeSpecial) { - StringBuffer sb = new StringBuffer(); - Type[] ctorArgs = invokeSpecial.getArgumentTypes(cpg); - sb.append(newParent.getClassName()); - sb.append("("); - for (int i = 0; i < ctorArgs.length; i++) { - String argtype = ctorArgs[i].toString(); - if (argtype.lastIndexOf(".") != -1) { - sb.append(argtype.substring(argtype.lastIndexOf(".") + 1)); - } else { - sb.append(argtype); - } - if (i + 1 < ctorArgs.length) { - sb.append(","); - } - } - sb.append(")"); - return sb.toString(); - } - - private ResolvedMember getConstructorWithSignature(ResolvedType type, String searchSig) { - for (ResolvedMember method : type.getDeclaredJavaMethods()) { - if (MemberUtils.isConstructor(method)) { - if (method.getSignature().equals(searchSig)) { - return method; - } - } - } - return null; - } - - private boolean mungePrivilegedAccess(BcelClassWeaver weaver, PrivilegedAccessMunger munger) { - LazyClassGen gen = weaver.getLazyClassGen(); - ResolvedMember member = munger.getMember(); - - ResolvedType onType = weaver.getWorld().resolve(member.getDeclaringType(), munger.getSourceLocation()); - if (onType.isRawType()) { - onType = onType.getGenericType(); - } - - // System.out.println("munging: " + gen + " with " + member); - if (onType.equals(gen.getType())) { - if (member.getKind() == Member.FIELD) { - // System.out.println("matched: " + gen); - addFieldGetter(gen, member, - AjcMemberMaker.privilegedAccessMethodForFieldGet(aspectType, member, munger.shortSyntax)); - addFieldSetter(gen, member, - AjcMemberMaker.privilegedAccessMethodForFieldSet(aspectType, member, munger.shortSyntax)); - return true; - } else if (member.getKind() == Member.METHOD) { - addMethodDispatch(gen, member, AjcMemberMaker.privilegedAccessMethodForMethod(aspectType, member)); - return true; - } else if (member.getKind() == Member.CONSTRUCTOR) { - for (Iterator i = gen.getMethodGens().iterator(); i.hasNext();) { - LazyMethodGen m = i.next(); - if (m.getMemberView() != null && m.getMemberView().getKind() == Member.CONSTRUCTOR) { - // m.getMemberView().equals(member)) { - m.forcePublic(); - // return true; - } - } - return true; - // throw new BCException("no match for " + member + " in " + - // gen); - } else if (member.getKind() == Member.STATIC_INITIALIZATION) { - gen.forcePublic(); - return true; - } else { - throw new RuntimeException("unimplemented"); - } - } - return false; - } - - private void addFieldGetter(LazyClassGen gen, ResolvedMember field, ResolvedMember accessMethod) { - LazyMethodGen mg = makeMethodGen(gen, accessMethod); - InstructionList il = new InstructionList(); - InstructionFactory fact = gen.getFactory(); - if (Modifier.isStatic(field.getModifiers())) { - il.append(fact.createFieldAccess(gen.getClassName(), field.getName(), BcelWorld.makeBcelType(field.getType()), - Constants.GETSTATIC)); - } else { - il.append(InstructionConstants.ALOAD_0); - il.append(fact.createFieldAccess(gen.getClassName(), field.getName(), BcelWorld.makeBcelType(field.getType()), - Constants.GETFIELD)); - } - il.append(InstructionFactory.createReturn(BcelWorld.makeBcelType(field.getType()))); - mg.getBody().insert(il); - - gen.addMethodGen(mg, getSignature().getSourceLocation()); - } - - private void addFieldSetter(LazyClassGen gen, ResolvedMember field, ResolvedMember accessMethod) { - LazyMethodGen mg = makeMethodGen(gen, accessMethod); - InstructionList il = new InstructionList(); - InstructionFactory fact = gen.getFactory(); - Type fieldType = BcelWorld.makeBcelType(field.getType()); - - if (Modifier.isStatic(field.getModifiers())) { - il.append(InstructionFactory.createLoad(fieldType, 0)); - il.append(fact.createFieldAccess(gen.getClassName(), field.getName(), fieldType, Constants.PUTSTATIC)); - } else { - il.append(InstructionConstants.ALOAD_0); - il.append(InstructionFactory.createLoad(fieldType, 1)); - il.append(fact.createFieldAccess(gen.getClassName(), field.getName(), fieldType, Constants.PUTFIELD)); - } - il.append(InstructionFactory.createReturn(Type.VOID)); - mg.getBody().insert(il); - - gen.addMethodGen(mg, getSignature().getSourceLocation()); - } - - private void addMethodDispatch(LazyClassGen gen, ResolvedMember method, ResolvedMember accessMethod) { - LazyMethodGen mg = makeMethodGen(gen, accessMethod); - InstructionList il = new InstructionList(); - InstructionFactory fact = gen.getFactory(); - Type[] paramTypes = BcelWorld.makeBcelTypes(method.getParameterTypes()); - - int pos = 0; - - if (!Modifier.isStatic(method.getModifiers())) { - il.append(InstructionConstants.ALOAD_0); - pos++; - } - for (int i = 0, len = paramTypes.length; i < len; i++) { - Type paramType = paramTypes[i]; - il.append(InstructionFactory.createLoad(paramType, pos)); - pos += paramType.getSize(); - } - il.append(Utility.createInvoke(fact, (BcelWorld) aspectType.getWorld(), method)); - il.append(InstructionFactory.createReturn(BcelWorld.makeBcelType(method.getReturnType()))); - - mg.getBody().insert(il); - - gen.addMethodGen(mg); - } - - protected LazyMethodGen makeMethodGen(LazyClassGen gen, ResolvedMember member) { - try { - Type returnType = BcelWorld.makeBcelType(member.getReturnType()); - Type[] parameterTypes = BcelWorld.makeBcelTypes(member.getParameterTypes()); - LazyMethodGen ret = new LazyMethodGen(member.getModifiers(), returnType, - member.getName(), parameterTypes, UnresolvedType.getNames(member - .getExceptions()), gen); - - // 43972 : Static crosscutting makes interfaces unusable for javac - // ret.makeSynthetic(); - return ret; - } catch (ClassFormatException cfe) { - throw new RuntimeException("Problem with makeMethodGen for method "+member.getName()+" in type "+gen.getName()+" ret="+member.getReturnType(),cfe); - } - } - - protected FieldGen makeFieldGen(LazyClassGen gen, ResolvedMember member) { - return new FieldGen(member.getModifiers(), BcelWorld.makeBcelType(member.getReturnType()), member.getName(), - gen.getConstantPool()); - } - - private boolean mungePerObjectInterface(BcelClassWeaver weaver, PerObjectInterfaceTypeMunger munger) { - // System.err.println("Munging perobject ["+munger+"] onto "+weaver. - // getLazyClassGen().getClassName()); - LazyClassGen gen = weaver.getLazyClassGen(); - - if (couldMatch(gen.getBcelObjectType(), munger.getTestPointcut())) { - FieldGen fg = makeFieldGen(gen, AjcMemberMaker.perObjectField(gen.getType(), aspectType)); - - gen.addField(fg, getSourceLocation()); - - Type fieldType = BcelWorld.makeBcelType(aspectType); - LazyMethodGen mg = new LazyMethodGen(Modifier.PUBLIC, fieldType, NameMangler.perObjectInterfaceGet(aspectType), - new Type[0], new String[0], gen); - InstructionList il = new InstructionList(); - InstructionFactory fact = gen.getFactory(); - il.append(InstructionConstants.ALOAD_0); - il.append(fact.createFieldAccess(gen.getClassName(), fg.getName(), fieldType, Constants.GETFIELD)); - il.append(InstructionFactory.createReturn(fieldType)); - mg.getBody().insert(il); - - gen.addMethodGen(mg); - - LazyMethodGen mg1 = new LazyMethodGen(Modifier.PUBLIC, Type.VOID, NameMangler.perObjectInterfaceSet(aspectType), - - new Type[] { fieldType, }, new String[0], gen); - InstructionList il1 = new InstructionList(); - il1.append(InstructionConstants.ALOAD_0); - il1.append(InstructionFactory.createLoad(fieldType, 1)); - il1.append(fact.createFieldAccess(gen.getClassName(), fg.getName(), fieldType, Constants.PUTFIELD)); - il1.append(InstructionFactory.createReturn(Type.VOID)); - mg1.getBody().insert(il1); - - gen.addMethodGen(mg1); - - gen.addInterface(munger.getInterfaceType().resolve(weaver.getWorld()), getSourceLocation()); - - return true; - } else { - return false; - } - } - - // PTWIMPL Add field to hold aspect instance and an accessor - private boolean mungePerTypeWithinTransformer(BcelClassWeaver weaver) { - LazyClassGen gen = weaver.getLazyClassGen(); - - // if (couldMatch(gen.getBcelObjectType(), munger.getTestPointcut())) { - - // Add (to the target type) the field that will hold the aspect instance - // e.g ajc$com_blah_SecurityAspect$ptwAspectInstance - FieldGen fg = makeFieldGen(gen, AjcMemberMaker.perTypeWithinField(gen.getType(), aspectType)); - gen.addField(fg, getSourceLocation()); - if (!gen.getType().canBeSeenBy(aspectType) && aspectType.isPrivilegedAspect()) { - gen.forcePublic(); - } - // Add an accessor for this new field, the - // ajc$$localAspectOf() method - // e.g. - // "public com_blah_SecurityAspect ajc$com_blah_SecurityAspect$localAspectOf()" - Type fieldType = BcelWorld.makeBcelType(aspectType); - LazyMethodGen mg = new LazyMethodGen(Modifier.PUBLIC | Modifier.STATIC, fieldType, - NameMangler.perTypeWithinLocalAspectOf(aspectType), new Type[0], new String[0], gen); - InstructionList il = new InstructionList(); - // PTWIMPL ?? Should check if it is null and throw - // NoAspectBoundException - InstructionFactory fact = gen.getFactory(); - il.append(fact.createFieldAccess(gen.getClassName(), fg.getName(), fieldType, Constants.GETSTATIC)); - il.append(InstructionFactory.createReturn(fieldType)); - mg.getBody().insert(il); - gen.addMethodGen(mg); - return true; - // } else { - // return false; - // } - } - - // ??? Why do we have this method? I thought by now we would know if it - // matched or not - private boolean couldMatch(BcelObjectType bcelObjectType, Pointcut pointcut) { - return !bcelObjectType.isInterface(); - } - - private boolean mungeNewMemberType(BcelClassWeaver classWeaver, NewMemberClassTypeMunger munger) { - World world = classWeaver.getWorld(); - ResolvedType onType = world.resolve(munger.getTargetType()); - if (onType.isRawType()) { - onType = onType.getGenericType(); - } - return onType.equals(classWeaver.getLazyClassGen().getType()); - } - - private boolean mungeNewMethod(BcelClassWeaver classWeaver, NewMethodTypeMunger munger) { - World world = classWeaver.getWorld(); - - // Resolving it will sort out the tvars - ResolvedMember unMangledInterMethod = munger.getSignature().resolve(world); - - // do matching on the unMangled one, but actually add them to the mangled method - ResolvedMember interMethodBody = munger.getDeclaredInterMethodBody(aspectType, world); - ResolvedMember interMethodDispatcher = munger.getDeclaredInterMethodDispatcher(aspectType, world); - ResolvedMember memberHoldingAnyAnnotations = interMethodDispatcher; - LazyClassGen classGen = classWeaver.getLazyClassGen(); - - ResolvedType onType = world.resolve(unMangledInterMethod.getDeclaringType(), munger.getSourceLocation()); - if (onType.isRawType()) { - onType = onType.getGenericType(); - } - - // Simple checks, can't ITD on annotations or enums - if (onType.isAnnotation()) { - signalError(WeaverMessages.ITDM_ON_ANNOTATION_NOT_ALLOWED, classWeaver, onType); - return false; - } - - if (onType.isEnum()) { - signalError(WeaverMessages.ITDM_ON_ENUM_NOT_ALLOWED, classWeaver, onType); - return false; - } - - boolean mungingInterface = classGen.isInterface(); - boolean onInterface = onType.isInterface(); - - if (onInterface - && classGen.getLazyMethodGen(unMangledInterMethod.getName(), unMangledInterMethod.getSignature(), true) != null) { - // this is ok, we could be providing the default implementation of a - // method - // that the target has already declared - return false; - } - - // If we are processing the intended ITD target type (might be an interface) - if (onType.equals(classGen.getType())) { - ResolvedMember mangledInterMethod = AjcMemberMaker.interMethod(unMangledInterMethod, aspectType, onInterface); - - LazyMethodGen newMethod = makeMethodGen(classGen, mangledInterMethod); - if (mungingInterface) { - // we want the modifiers of the ITD to be used for all *implementors* of the - // interface, but the method itself we add to the interface must be public abstract - newMethod.setAccessFlags(Modifier.PUBLIC | Modifier.ABSTRACT); - } - - // pr98901 - // For copying the annotations across, we have to discover the real - // member in the aspect which is holding them. - if (classWeaver.getWorld().isInJava5Mode()) { - AnnotationAJ annotationsOnRealMember[] = null; - ResolvedType toLookOn = aspectType; - if (aspectType.isRawType()) { - toLookOn = aspectType.getGenericType(); - } - ResolvedMember realMember = getRealMemberForITDFromAspect(toLookOn, memberHoldingAnyAnnotations, false); - // 266602 - consider it missing to mean that the corresponding aspect had errors - if (realMember == null) { - // signalWarning("Unable to apply any annotations attached to " + munger.getSignature(), weaver); - // throw new BCException("Couldn't find ITD init member '" + interMethodBody + "' on aspect " + aspectType); - } else { - annotationsOnRealMember = realMember.getAnnotations(); - } - Set addedAnnotations = new HashSet(); - if (annotationsOnRealMember != null) { - for (AnnotationAJ anno : annotationsOnRealMember) { - AnnotationGen a = ((BcelAnnotation) anno).getBcelAnnotation(); - AnnotationGen ag = new AnnotationGen(a, classGen.getConstantPool(), true); - newMethod.addAnnotation(new BcelAnnotation(ag, classWeaver.getWorld())); - addedAnnotations.add(anno.getType()); - } - } - if (realMember != null) { - copyOverParameterAnnotations(newMethod, realMember); - } - // the code below was originally added to cope with the case where an aspect declares an annotation on an ITD - // declared within itself (an unusual situation). However, it also addresses the case where we may not find the - // annotation on the real representation of the ITD. This can happen in a load-time weaving situation where - // we couldn't add the annotation in time - and so here we recheck the declare annotations. Not quite ideal but - // works. pr288635 - List allDecams = world.getDeclareAnnotationOnMethods(); - for (DeclareAnnotation declareAnnotationMC : allDecams) { - if (declareAnnotationMC.matches(unMangledInterMethod, world)) { - // && newMethod.getEnclosingClass().getType() == aspectType) { - AnnotationAJ annotation = declareAnnotationMC.getAnnotation(); - if (!addedAnnotations.contains(annotation.getType())) { - newMethod.addAnnotation(annotation); - } - } - } - } - - // If it doesn't target an interface and there is a body (i.e. it isnt abstract) - if (!onInterface && !Modifier.isAbstract(mangledInterMethod.getModifiers())) { - InstructionList body = newMethod.getBody(); - InstructionFactory fact = classGen.getFactory(); - int pos = 0; - - if (!Modifier.isStatic(unMangledInterMethod.getModifiers())) { - body.append(InstructionFactory.createThis()); - pos++; - } - Type[] paramTypes = BcelWorld.makeBcelTypes(mangledInterMethod.getParameterTypes()); - for (int i = 0, len = paramTypes.length; i < len; i++) { - Type paramType = paramTypes[i]; - body.append(InstructionFactory.createLoad(paramType, pos)); - pos += paramType.getSize(); - } - body.append(Utility.createInvoke(fact, classWeaver.getWorld(), interMethodBody)); - body.append(InstructionFactory.createReturn(BcelWorld.makeBcelType(mangledInterMethod.getReturnType()))); - - if (classWeaver.getWorld().isInJava5Mode()) { // Don't need bridge - // methods if not in - // 1.5 mode. - createAnyBridgeMethodsForCovariance(classWeaver, munger, unMangledInterMethod, onType, classGen, paramTypes); - } - - } else { - // ??? this is okay - // if (!(mg.getBody() == null)) throw new - // RuntimeException("bas"); - } - - if (world.isInJava5Mode()) { - String basicSignature = mangledInterMethod.getSignature(); - String genericSignature = ((ResolvedMemberImpl) mangledInterMethod).getSignatureForAttribute(); - if (!basicSignature.equals(genericSignature)) { - // Add a signature attribute to it - newMethod.addAttribute(createSignatureAttribute(classGen.getConstantPool(), genericSignature)); - } - } - // XXX make sure to check that we set exceptions properly on this - // guy. - classWeaver.addLazyMethodGen(newMethod); - classWeaver.getLazyClassGen().warnOnAddedMethod(newMethod.getMethod(), getSignature().getSourceLocation()); - - addNeededSuperCallMethods(classWeaver, onType, munger.getSuperMethodsCalled()); - - return true; - - } else if (onInterface && !Modifier.isAbstract(unMangledInterMethod.getModifiers())) { - - // This means the 'gen' should be the top most implementor - // - if it is *not* then something went wrong after we worked - // out that it was the top most implementor (see pr49657) - if (!classGen.getType().isTopmostImplementor(onType)) { - ResolvedType rtx = classGen.getType().getTopmostImplementor(onType); - if (rtx == null) { - // pr302460 - // null means there is something wrong with what we are looking at - ResolvedType rt = classGen.getType(); - if (rt.isInterface()) { - ISourceLocation sloc = munger.getSourceLocation(); - classWeaver - .getWorld() - .getMessageHandler() - .handleMessage( - MessageUtil.error( - "ITD target " - + rt.getName() - + " is an interface but has been incorrectly determined to be the topmost implementor of " - + onType.getName() + ". ITD is " + this.getSignature(), sloc)); - } - if (!onType.isAssignableFrom(rt)) { - ISourceLocation sloc = munger.getSourceLocation(); - classWeaver - .getWorld() - .getMessageHandler() - .handleMessage( - MessageUtil.error( - "ITD target " + rt.getName() + " doesn't appear to implement " + onType.getName() - + " why did we consider it the top most implementor? ITD is " - + this.getSignature(), sloc)); - } - } else if (!rtx.isExposedToWeaver()) { - ISourceLocation sLoc = munger.getSourceLocation(); - classWeaver - .getWorld() - .getMessageHandler() - .handleMessage( - MessageUtil.error(WeaverMessages.format(WeaverMessages.ITD_NON_EXPOSED_IMPLEMENTOR, rtx, - getAspectType().getName()), (sLoc == null ? getAspectType().getSourceLocation() : sLoc))); - } else { - // XXX what does this state mean? - // We have incorrectly identified what is the top most - // implementor and its not because - // a type wasn't exposed to the weaver - } - return false; - } else { - - ResolvedMember mangledInterMethod = AjcMemberMaker.interMethod(unMangledInterMethod, aspectType, false); - - LazyMethodGen mg = makeMethodGen(classGen, mangledInterMethod); - - // From 98901#29 - need to copy annotations across - if (classWeaver.getWorld().isInJava5Mode()) { - AnnotationAJ annotationsOnRealMember[] = null; - ResolvedType toLookOn = aspectType; - if (aspectType.isRawType()) { - toLookOn = aspectType.getGenericType(); - } - ResolvedMember realMember = getRealMemberForITDFromAspect(toLookOn, memberHoldingAnyAnnotations, false); - if (realMember == null) { - throw new BCException("Couldn't find ITD holder member '" + memberHoldingAnyAnnotations + "' on aspect " - + aspectType); - } - annotationsOnRealMember = realMember.getAnnotations(); - - if (annotationsOnRealMember != null) { - for (AnnotationAJ annotationX : annotationsOnRealMember) { - AnnotationGen a = ((BcelAnnotation) annotationX).getBcelAnnotation(); - AnnotationGen ag = new AnnotationGen(a, classWeaver.getLazyClassGen().getConstantPool(), true); - mg.addAnnotation(new BcelAnnotation(ag, classWeaver.getWorld())); - } - } - - copyOverParameterAnnotations(mg, realMember); - } - - if (mungingInterface) { - // we want the modifiers of the ITD to be used for all - // *implementors* of the - // interface, but the method itself we add to the interface - // must be public abstract - mg.setAccessFlags(Modifier.PUBLIC | Modifier.ABSTRACT); - } - - Type[] paramTypes = BcelWorld.makeBcelTypes(mangledInterMethod.getParameterTypes()); - Type returnType = BcelWorld.makeBcelType(mangledInterMethod.getReturnType()); - - InstructionList body = mg.getBody(); - InstructionFactory fact = classGen.getFactory(); - int pos = 0; - - if (!Modifier.isStatic(mangledInterMethod.getModifiers())) { - body.append(InstructionFactory.createThis()); - pos++; - } - for (int i = 0, len = paramTypes.length; i < len; i++) { - Type paramType = paramTypes[i]; - body.append(InstructionFactory.createLoad(paramType, pos)); - pos += paramType.getSize(); - } - - body.append(Utility.createInvoke(fact, classWeaver.getWorld(), interMethodBody)); - Type t = BcelWorld.makeBcelType(interMethodBody.getReturnType()); - if (!t.equals(returnType)) { - body.append(fact.createCast(t, returnType)); - } - body.append(InstructionFactory.createReturn(returnType)); - mg.definingType = onType; - - if (world.isInJava5Mode()) { - String basicSignature = mangledInterMethod.getSignature(); - String genericSignature = ((ResolvedMemberImpl) mangledInterMethod).getSignatureForAttribute(); - if (!basicSignature.equals(genericSignature)) { - // Add a signature attribute to it - mg.addAttribute(createSignatureAttribute(classGen.getConstantPool(), genericSignature)); - } - } - - classWeaver.addOrReplaceLazyMethodGen(mg); - - addNeededSuperCallMethods(classWeaver, onType, munger.getSuperMethodsCalled()); - - // Work out if we need a bridge method for the new method added to the topmostimplementor. - - // Check if the munger being processed is a parameterized form of the original munger - createBridgeIfNecessary(classWeaver, munger, unMangledInterMethod, classGen); - - return true; - } - } else { - return false; - } - } - - private void createBridgeIfNecessary(BcelClassWeaver classWeaver, NewMethodTypeMunger munger, - ResolvedMember unMangledInterMethod, LazyClassGen classGen) { - if (munger.getDeclaredSignature() != null) { - boolean needsbridging = false; - ResolvedMember mungerSignature = munger.getSignature(); - ResolvedMember toBridgeTo = munger.getDeclaredSignature().parameterizedWith(null, - mungerSignature.getDeclaringType().resolve(getWorld()), false, munger.getTypeVariableAliases()); - if (!toBridgeTo.getReturnType().getErasureSignature().equals(mungerSignature.getReturnType().getErasureSignature())) { - needsbridging = true; - } - UnresolvedType[] originalParams = toBridgeTo.getParameterTypes(); - UnresolvedType[] newParams = mungerSignature.getParameterTypes(); - for (int ii = 0; ii < originalParams.length; ii++) { - if (!originalParams[ii].getErasureSignature().equals(newParams[ii].getErasureSignature())) { - needsbridging = true; - } - } - if (needsbridging) { - createBridge(classWeaver, unMangledInterMethod, classGen, toBridgeTo); - } - } - } - - private void copyOverParameterAnnotations(LazyMethodGen receiverMethod, ResolvedMember donorMethod) { - AnnotationAJ[][] pAnnos = donorMethod.getParameterAnnotations(); - if (pAnnos != null) { - int offset = receiverMethod.isStatic() ? 0 : 1; - int param = 0; - for (int i = offset; i < pAnnos.length; i++) { - AnnotationAJ[] annosOnParam = pAnnos[i]; - if (annosOnParam != null) { - for (AnnotationAJ anno : annosOnParam) { - receiverMethod.addParameterAnnotation(param, anno); - } - } - param++; - } - } - } - - private void createBridge(BcelClassWeaver weaver, ResolvedMember unMangledInterMethod, LazyClassGen classGen, - ResolvedMember toBridgeTo) { - Type[] paramTypes; - Type returnType; - InstructionList body; - InstructionFactory fact; - int pos; - ResolvedMember bridgerMethod = AjcMemberMaker.bridgerToInterMethod(unMangledInterMethod, classGen.getType()); - ResolvedMember bridgingSetter = AjcMemberMaker.interMethodBridger(toBridgeTo, aspectType, false); // pr250493 - - LazyMethodGen bridgeMethod = makeMethodGen(classGen, bridgingSetter); - paramTypes = BcelWorld.makeBcelTypes(bridgingSetter.getParameterTypes()); - Type[] bridgingToParms = BcelWorld.makeBcelTypes(unMangledInterMethod.getParameterTypes()); - returnType = BcelWorld.makeBcelType(bridgingSetter.getReturnType()); - body = bridgeMethod.getBody(); - fact = classGen.getFactory(); - pos = 0; - if (!Modifier.isStatic(bridgingSetter.getModifiers())) { - body.append(InstructionFactory.createThis()); - pos++; - } - for (int i = 0, len = paramTypes.length; i < len; i++) { - Type paramType = paramTypes[i]; - body.append(InstructionFactory.createLoad(paramType, pos)); - if (!bridgingSetter.getParameterTypes()[i].getErasureSignature().equals( - unMangledInterMethod.getParameterTypes()[i].getErasureSignature())) { - // System.err.println("Putting in cast from "+ - // paramType+" to "+bridgingToParms[i]); - body.append(fact.createCast(paramType, bridgingToParms[i])); - } - pos += paramType.getSize(); - } - - body.append(Utility.createInvoke(fact, weaver.getWorld(), bridgerMethod)); - body.append(InstructionFactory.createReturn(returnType)); - classGen.addMethodGen(bridgeMethod); - // mg.definingType = onType; - } - - /** - * Helper method to create a signature attribute based on a string signature: e.g. "Ljava/lang/Object;LI;" - */ - private Signature createSignatureAttribute(ConstantPool cp, String signature) { - int nameIndex = cp.addUtf8("Signature"); - int sigIndex = cp.addUtf8(signature); - return new Signature(nameIndex, 2, sigIndex, cp); - } - - /** - * Create any bridge method required because of covariant returns being used. This method is used in the case where an ITD is - * applied to some type and it may be in an override relationship with a method from the supertype - but due to covariance there - * is a mismatch in return values. Example of when required: Super defines: Object m(String s) Sub defines: String m(String s) - * then we need a bridge method in Sub called 'Object m(String s)' that forwards to 'String m(String s)' - */ - private void createAnyBridgeMethodsForCovariance(BcelClassWeaver weaver, NewMethodTypeMunger munger, - ResolvedMember unMangledInterMethod, ResolvedType onType, LazyClassGen gen, Type[] paramTypes) { - // PERFORMANCE BOTTLENECK? Might need investigating, method analysis - // between types in a hierarchy just seems expensive... - // COVARIANCE BRIDGING - // Algorithm: Step1. Check in this type - has someone already created - // the bridge method? - // Step2. Look above us - do we 'override' a method and yet differ in - // return type (i.e. covariance) - // Step3. Create a forwarding bridge method - // ResolvedType superclass = onType.getSuperclass(); - boolean quitRightNow = false; - - String localMethodName = unMangledInterMethod.getName(); - String erasedSig = unMangledInterMethod.getSignatureErased(); // will be something like (LSuperB;)LFoo; - String localParameterSig = erasedSig.substring(0,erasedSig.lastIndexOf(')')+1);//unMangledInterMethod.getParameterSignature(); - // getParameterSignatureErased() does not include parens, which we do need. - String localReturnTypeESig = unMangledInterMethod.getReturnType().getErasureSignature(); - - // Step1 - boolean alreadyDone = false; // Compiler might have done it - ResolvedMember[] localMethods = onType.getDeclaredMethods(); - for (int i = 0; i < localMethods.length; i++) { - ResolvedMember member = localMethods[i]; - if (member.getName().equals(localMethodName)) { - // Check the params - if (member.getParameterSignature().equals(localParameterSig)) { - alreadyDone = true; - } - } - } - - // Step2 - if (!alreadyDone) { - // Use the iterator form of 'getMethods()' so we do as little work as necessary - ResolvedType supertype = onType.getSuperclass(); - if (supertype != null) { - for (Iterator iter = supertype.getMethods(true, true); iter.hasNext() && !quitRightNow;) { - ResolvedMember aMethod = iter.next(); - if (aMethod.getName().equals(localMethodName) && aMethod.getParameterSignature().equals(localParameterSig)) { - // check the return types, if they are different we need a - // bridging method. - if (!aMethod.getReturnType().getErasureSignature().equals(localReturnTypeESig) - && !Modifier.isPrivate(aMethod.getModifiers())) { - // Step3 - createBridgeMethod(weaver.getWorld(), munger, unMangledInterMethod, gen, paramTypes, aMethod); - quitRightNow = true; - } - } - } - } - } - } - - /** - * Create a bridge method for a particular munger. - * - * @param world - * @param munger - * @param unMangledInterMethod the method to bridge 'to' that we have already created in the 'subtype' - * @param clazz the class in which to put the bridge method - * @param paramTypes Parameter types for the bridge method, passed in as an optimization since the caller is likely to have - * already created them. - * @param theBridgeMethod - */ - private void createBridgeMethod(BcelWorld world, NewMethodTypeMunger munger, ResolvedMember unMangledInterMethod, - LazyClassGen clazz, Type[] paramTypes, ResolvedMember theBridgeMethod) { - InstructionList body; - InstructionFactory fact; - int pos = 0; - - // The bridge method in this type will have the same signature as the one in the supertype - LazyMethodGen bridgeMethod = makeMethodGen(clazz, theBridgeMethod); - bridgeMethod.setAccessFlags(bridgeMethod.getAccessFlags() | 0x00000040 /* BRIDGE = 0x00000040 */); - // UnresolvedType[] newParams = munger.getSignature().getParameterTypes(); - Type returnType = BcelWorld.makeBcelType(theBridgeMethod.getReturnType()); - body = bridgeMethod.getBody(); - fact = clazz.getFactory(); - - if (!Modifier.isStatic(unMangledInterMethod.getModifiers())) { - body.append(InstructionFactory.createThis()); - pos++; - } - for (int i = 0, len = paramTypes.length; i < len; i++) { - Type paramType = paramTypes[i]; - body.append(InstructionFactory.createLoad(paramType, pos)); - // if (!bridgingSetter.getParameterTypes()[i].getErasureSignature(). - // equals - // (unMangledInterMethod.getParameterTypes()[i].getErasureSignature - // ())) { - // System.err.println("Putting in cast from "+paramType+" to "+ - // bridgingToParms[i]); - // body.append(fact.createCast(paramType,bridgingToParms[i])); - // } - pos += paramType.getSize(); - } - - body.append(Utility.createInvoke(fact, world, unMangledInterMethod)); - body.append(InstructionFactory.createReturn(returnType)); - clazz.addMethodGen(bridgeMethod); - } - - // Unlike toString() on a member, this does not include the declaring type - private String stringifyMember(ResolvedMember member) { - StringBuffer buf = new StringBuffer(); - buf.append(member.getReturnType().getName()); - buf.append(' '); - buf.append(member.getName()); - if (member.getKind() != Member.FIELD) { - buf.append("("); - UnresolvedType[] params = member.getParameterTypes(); - if (params.length != 0) { - buf.append(params[0]); - for (int i = 1, len = params.length; i < len; i++) { - buf.append(", "); - buf.append(params[i].getName()); - } - } - buf.append(")"); - } - return buf.toString(); - } - - private boolean mungeMethodDelegate(BcelClassWeaver weaver, MethodDelegateTypeMunger munger) { - World world = weaver.getWorld(); - - LazyClassGen gen = weaver.getLazyClassGen(); - if (gen.getType().isAnnotation() || gen.getType().isEnum()) { - // don't signal error as it could be a consequence of a wild type pattern - return false; - } - - ResolvedMember introduced = munger.getSignature(); - - ResolvedType fromType = world.resolve(introduced.getDeclaringType(), munger.getSourceLocation()); - if (fromType.isRawType()) { - fromType = fromType.getGenericType(); - } - - boolean shouldApply = munger.matches(weaver.getLazyClassGen().getType(), aspectType); - - if (shouldApply) { - Type bcelReturnType = BcelWorld.makeBcelType(introduced.getReturnType()); - - // If no implementation class was specified, the intention was that - // the types matching the pattern - // already implemented the interface, let's check that now! - if (munger.getImplClassName() == null && !munger.specifiesDelegateFactoryMethod()) { - boolean isOK = false; - List existingMethods = gen.getMethodGens(); - for (LazyMethodGen m : existingMethods) { - if (m.getName().equals(introduced.getName()) - && m.getParameterSignature().equals(introduced.getParameterSignature()) - && m.getReturnType().equals(bcelReturnType)) { - isOK = true; - } - } - if (!isOK) { - // the class does not implement this method, they needed to - // supply a default impl class - IMessage msg = new Message("@DeclareParents: No defaultImpl was specified but the type '" + gen.getName() - + "' does not implement the method '" + stringifyMember(introduced) + "' defined on the interface '" - + introduced.getDeclaringType() + "'", weaver.getLazyClassGen().getType().getSourceLocation(), true, - new ISourceLocation[] { munger.getSourceLocation() }); - weaver.getWorld().getMessageHandler().handleMessage(msg); - return false; - } - - return true; - } - - LazyMethodGen mg = new LazyMethodGen(introduced.getModifiers() - Modifier.ABSTRACT, bcelReturnType, - introduced.getName(), BcelWorld.makeBcelTypes(introduced.getParameterTypes()), - BcelWorld.makeBcelTypesAsClassNames(introduced.getExceptions()), gen); - - // annotation copy from annotation on ITD interface - if (weaver.getWorld().isInJava5Mode()) { - AnnotationAJ annotationsOnRealMember[] = null; - ResolvedType toLookOn = weaver.getWorld().lookupOrCreateName(introduced.getDeclaringType()); - if (fromType.isRawType()) { - toLookOn = fromType.getGenericType(); - } - // lookup the method - ResolvedMember[] ms = toLookOn.getDeclaredJavaMethods(); - for (ResolvedMember m : ms) { - if (introduced.getName().equals(m.getName()) && introduced.getSignature().equals(m.getSignature())) { - annotationsOnRealMember = m.getAnnotations(); - break; - } - } - if (annotationsOnRealMember != null) { - for (AnnotationAJ anno : annotationsOnRealMember) { - AnnotationGen a = ((BcelAnnotation) anno).getBcelAnnotation(); - AnnotationGen ag = new AnnotationGen(a, weaver.getLazyClassGen().getConstantPool(), true); - mg.addAnnotation(new BcelAnnotation(ag, weaver.getWorld())); - } - } - } - - InstructionList body = new InstructionList(); - InstructionFactory fact = gen.getFactory(); - - // getfield - body.append(InstructionConstants.ALOAD_0); - body.append(Utility.createGet(fact, munger.getDelegate(weaver.getLazyClassGen().getType()))); - InstructionBranch ifNonNull = InstructionFactory.createBranchInstruction(Constants.IFNONNULL, null); - body.append(ifNonNull); - - // Create and store a new instance - body.append(InstructionConstants.ALOAD_0); // 'this' is where we'll store the field value - - // TODO for non-static case, call aspectOf() then call the factory method on the retval - // TODO decide whether the value can really be cached - - // locate the aspect and call the static method in it - if (munger.specifiesDelegateFactoryMethod()) { - ResolvedMember rm = munger.getDelegateFactoryMethod(weaver.getWorld()); - - // Check the method parameter is compatible with the type of the instance to be passed - if (rm.getArity() != 0) { - ResolvedType parameterType = rm.getParameterTypes()[0].resolve(weaver.getWorld()); - if (!parameterType.isAssignableFrom(weaver.getLazyClassGen().getType())) { - signalError("For mixin factory method '" + rm + "': Instance type '" + weaver.getLazyClassGen().getType() - + "' is not compatible with factory parameter type '" + parameterType + "'", weaver); - return false; - } - } - if (Modifier.isStatic(rm.getModifiers())) { - if (rm.getArity() != 0) { - body.append(InstructionConstants.ALOAD_0); - } - body.append(fact.createInvoke(rm.getDeclaringType().getName(), rm.getName(), rm.getSignature(), - Constants.INVOKESTATIC)); - body.append(Utility.createSet(fact, munger.getDelegate(weaver.getLazyClassGen().getType()))); - } else { - // Need to call aspectOf() to obtain the aspect instance then call the factory method upon that - UnresolvedType theAspect = munger.getAspect(); - body.append(fact.createInvoke(theAspect.getName(), "aspectOf", "()" + theAspect.getSignature(), - Constants.INVOKESTATIC)); - if (rm.getArity() != 0) { - body.append(InstructionConstants.ALOAD_0); - } - body.append(fact.createInvoke(rm.getDeclaringType().getName(), rm.getName(), rm.getSignature(), - Constants.INVOKEVIRTUAL)); - body.append(Utility.createSet(fact, munger.getDelegate(weaver.getLazyClassGen().getType()))); - } - } else { - body.append(fact.createNew(munger.getImplClassName())); - body.append(InstructionConstants.DUP); - body.append(fact.createInvoke(munger.getImplClassName(), "", Type.VOID, Type.NO_ARGS, Constants.INVOKESPECIAL)); - body.append(Utility.createSet(fact, munger.getDelegate(weaver.getLazyClassGen().getType()))); - } - - // if not null use the instance we've got - InstructionHandle ifNonNullElse = body.append(InstructionConstants.ALOAD_0); - ifNonNull.setTarget(ifNonNullElse); - body.append(Utility.createGet(fact, munger.getDelegate(weaver.getLazyClassGen().getType()))); - - // args - int pos = 0; - if (!Modifier.isStatic(introduced.getModifiers())) { // skip 'this' (?? can this really - // happen) - // body.append(InstructionFactory.createThis()); - pos++; - } - Type[] paramTypes = BcelWorld.makeBcelTypes(introduced.getParameterTypes()); - for (int i = 0, len = paramTypes.length; i < len; i++) { - Type paramType = paramTypes[i]; - body.append(InstructionFactory.createLoad(paramType, pos)); - pos += paramType.getSize(); - } - body.append(Utility.createInvoke(fact, Constants.INVOKEINTERFACE, introduced)); - body.append(InstructionFactory.createReturn(bcelReturnType)); - - mg.getBody().append(body); - weaver.addLazyMethodGen(mg); - weaver.getLazyClassGen().warnOnAddedMethod(mg.getMethod(), getSignature().getSourceLocation()); - return true; - } - return false; - } - - private boolean mungeFieldHost(BcelClassWeaver weaver, MethodDelegateTypeMunger.FieldHostTypeMunger munger) { - LazyClassGen gen = weaver.getLazyClassGen(); - if (gen.getType().isAnnotation() || gen.getType().isEnum()) { - // don't signal error as it could be a consequence of a wild type - // pattern - return false; - } - // boolean shouldApply = - munger.matches(weaver.getLazyClassGen().getType(), aspectType); // why - // do - // this? - ResolvedMember host = AjcMemberMaker.itdAtDeclareParentsField(weaver.getLazyClassGen().getType(), munger.getSignature() - .getType(), aspectType); - FieldGen field = makeFieldGen(weaver.getLazyClassGen(), host); - field.setModifiers(field.getModifiers() | BcelField.AccSynthetic); - weaver.getLazyClassGen().addField(field, null); - return true; - } - - private ResolvedMember getRealMemberForITDFromAspect(ResolvedType aspectType, ResolvedMember lookingFor, boolean isCtorRelated) { - World world = aspectType.getWorld(); - boolean debug = false; - if (debug) { - System.err.println("Searching for a member on type: " + aspectType); - System.err.println("Member we are looking for: " + lookingFor); - } - - ResolvedMember aspectMethods[] = aspectType.getDeclaredMethods(); - UnresolvedType[] lookingForParams = lookingFor.getParameterTypes(); - - ResolvedMember realMember = null; - for (int i = 0; realMember == null && i < aspectMethods.length; i++) { - ResolvedMember member = aspectMethods[i]; - if (member.getName().equals(lookingFor.getName())) { - UnresolvedType[] memberParams = member.getGenericParameterTypes(); - if (memberParams.length == lookingForParams.length) { - if (debug) { - System.err.println("Reviewing potential candidates: " + member); - } - boolean matchOK = true; - // If not related to a ctor ITD then the name is enough to - // confirm we have the - // right one. If it is ctor related we need to check the - // params all match, although - // only the erasure. - if (isCtorRelated) { - for (int j = 0; j < memberParams.length && matchOK; j++) { - ResolvedType pMember = memberParams[j].resolve(world); - ResolvedType pLookingFor = lookingForParams[j].resolve(world); - - if (pMember.isTypeVariableReference()) { - pMember = ((TypeVariableReference) pMember).getTypeVariable().getFirstBound().resolve(world); - } - if (pMember.isParameterizedType() || pMember.isGenericType()) { - pMember = pMember.getRawType().resolve(aspectType.getWorld()); - } - - if (pLookingFor.isTypeVariableReference()) { - pLookingFor = ((TypeVariableReference) pLookingFor).getTypeVariable().getFirstBound() - .resolve(world); - } - if (pLookingFor.isParameterizedType() || pLookingFor.isGenericType()) { - pLookingFor = pLookingFor.getRawType().resolve(world); - } - - if (debug) { - System.err.println("Comparing parameter " + j + " member=" + pMember + " lookingFor=" - + pLookingFor); - } - if (!pMember.equals(pLookingFor)) { - matchOK = false; - } - } - } - if (matchOK) { - realMember = member; - } - } - } - } - if (debug && realMember == null) { - System.err.println("Didn't find a match"); - } - return realMember; - } - - private void addNeededSuperCallMethods(BcelClassWeaver weaver, ResolvedType onType, Set neededSuperCalls) { - LazyClassGen gen = weaver.getLazyClassGen(); - for (ResolvedMember superMethod: neededSuperCalls) { - if (weaver.addDispatchTarget(superMethod)) { - // System.err.println("super type: " + superMethod.getDeclaringType() + ", " + gen.getType()); - boolean isSuper = !superMethod.getDeclaringType().equals(gen.getType()); - String dispatchName; - if (isSuper) { - dispatchName = NameMangler.superDispatchMethod(onType, superMethod.getName()); - } else { - dispatchName = NameMangler.protectedDispatchMethod(onType, superMethod.getName()); - } - superMethod = superMethod.resolve(weaver.getWorld()); - LazyMethodGen dispatcher = makeDispatcher(gen, dispatchName, superMethod, weaver.getWorld(), isSuper); - weaver.addLazyMethodGen(dispatcher); - } - } - } - - private void signalError(String msgid, BcelClassWeaver weaver, UnresolvedType onType) { - IMessage msg = MessageUtil.error(WeaverMessages.format(msgid, onType.getName()), getSourceLocation()); - weaver.getWorld().getMessageHandler().handleMessage(msg); - } - - // private void signalWarning(String msgString, BcelClassWeaver weaver) { - // IMessage msg = MessageUtil.warn(msgString, getSourceLocation()); - // weaver.getWorld().getMessageHandler().handleMessage(msg); - // } - - private void signalError(String msgString, BcelClassWeaver weaver) { - IMessage msg = MessageUtil.error(msgString, getSourceLocation()); - weaver.getWorld().getMessageHandler().handleMessage(msg); - } - - private boolean mungeNewConstructor(BcelClassWeaver weaver, NewConstructorTypeMunger newConstructorTypeMunger) { - - final LazyClassGen currentClass = weaver.getLazyClassGen(); - final InstructionFactory fact = currentClass.getFactory(); - - ResolvedMember newConstructorMember = newConstructorTypeMunger.getSyntheticConstructor(); - ResolvedType onType = newConstructorMember.getDeclaringType().resolve(weaver.getWorld()); - if (onType.isRawType()) { - onType = onType.getGenericType(); - } - - if (onType.isAnnotation()) { - signalError(WeaverMessages.ITDC_ON_ANNOTATION_NOT_ALLOWED, weaver, onType); - return false; - } - - if (onType.isEnum()) { - signalError(WeaverMessages.ITDC_ON_ENUM_NOT_ALLOWED, weaver, onType); - return false; - } - - if (!onType.equals(currentClass.getType())) { - return false; - } - - ResolvedMember explicitConstructor = newConstructorTypeMunger.getExplicitConstructor(); - // int declaredParameterCount = - // newConstructorTypeMunger.getDeclaredParameterCount(); - LazyMethodGen mg = makeMethodGen(currentClass, newConstructorMember); - mg.setEffectiveSignature(newConstructorTypeMunger.getSignature(), Shadow.ConstructorExecution, true); - - // pr98901 - // For copying the annotations across, we have to discover the real - // member in the aspect - // which is holding them. - if (weaver.getWorld().isInJava5Mode()) { - - ResolvedMember interMethodDispatcher = AjcMemberMaker.postIntroducedConstructor(aspectType, onType, - newConstructorTypeMunger.getSignature().getParameterTypes()); - AnnotationAJ annotationsOnRealMember[] = null; - ResolvedMember realMember = getRealMemberForITDFromAspect(aspectType, interMethodDispatcher, true); - // 266602 - consider it missing to mean that the corresponding aspect had errors - if (realMember == null) { - // signalWarning("Unable to apply any annotations attached to " + munger.getSignature(), weaver); - // throw new BCException("Couldn't find ITD init member '" + interMethodBody + "' on aspect " + aspectType); - } else { - annotationsOnRealMember = realMember.getAnnotations(); - } - if (annotationsOnRealMember != null) { - for (int i = 0; i < annotationsOnRealMember.length; i++) { - AnnotationAJ annotationX = annotationsOnRealMember[i]; - AnnotationGen a = ((BcelAnnotation) annotationX).getBcelAnnotation(); - AnnotationGen ag = new AnnotationGen(a, weaver.getLazyClassGen().getConstantPool(), true); - mg.addAnnotation(new BcelAnnotation(ag, weaver.getWorld())); - } - } - // the below loop fixes the very special (and very stupid) - // case where an aspect declares an annotation - // on an ITD it declared on itself. - List allDecams = weaver.getWorld().getDeclareAnnotationOnMethods(); - for (Iterator i = allDecams.iterator(); i.hasNext();) { - DeclareAnnotation decaMC = i.next(); - if (decaMC.matches(explicitConstructor, weaver.getWorld()) && mg.getEnclosingClass().getType() == aspectType) { - mg.addAnnotation(decaMC.getAnnotation()); - } - } - } - - // Might have to remove the default constructor - b275032 - // TODO could have tagged the type munger when the fact we needed to do this was detected earlier - if (mg.getArgumentTypes().length == 0) { - LazyMethodGen toRemove = null; - for (LazyMethodGen object : currentClass.getMethodGens()) { - if (object.getName().equals("") && object.getArgumentTypes().length == 0) { - toRemove = object; - } - } - if (toRemove != null) { - currentClass.removeMethodGen(toRemove); - } - } - - currentClass.addMethodGen(mg); - // weaver.addLazyMethodGen(freshConstructor); - - InstructionList body = mg.getBody(); - - // add to body: push arts for call to pre, from actual args starting at - // 1 (skipping this), going to - // declared argcount + 1 - UnresolvedType[] declaredParams = newConstructorTypeMunger.getSignature().getParameterTypes(); - Type[] paramTypes = mg.getArgumentTypes(); - int frameIndex = 1; - for (int i = 0, len = declaredParams.length; i < len; i++) { - body.append(InstructionFactory.createLoad(paramTypes[i], frameIndex)); - frameIndex += paramTypes[i].getSize(); - } - // do call to pre - Member preMethod = AjcMemberMaker.preIntroducedConstructor(aspectType, onType, declaredParams); - body.append(Utility.createInvoke(fact, null, preMethod)); - - // create a local, and store return pre stuff into it. - int arraySlot = mg.allocateLocal(1); - body.append(InstructionFactory.createStore(Type.OBJECT, arraySlot)); - - // put this on the stack - body.append(InstructionConstants.ALOAD_0); - - // unpack pre args onto stack - UnresolvedType[] superParamTypes = explicitConstructor.getParameterTypes(); - - for (int i = 0, len = superParamTypes.length; i < len; i++) { - body.append(InstructionFactory.createLoad(Type.OBJECT, arraySlot)); - body.append(Utility.createConstant(fact, i)); - body.append(InstructionFactory.createArrayLoad(Type.OBJECT)); - body.append(Utility.createConversion(fact, Type.OBJECT, BcelWorld.makeBcelType(superParamTypes[i]))); - } - - // call super/this - - body.append(Utility.createInvoke(fact, null, explicitConstructor)); - - // put this back on the stack - - body.append(InstructionConstants.ALOAD_0); - - // unpack params onto stack - Member postMethod = AjcMemberMaker.postIntroducedConstructor(aspectType, onType, declaredParams); - UnresolvedType[] postParamTypes = postMethod.getParameterTypes(); - - for (int i = 1, len = postParamTypes.length; i < len; i++) { - body.append(InstructionFactory.createLoad(Type.OBJECT, arraySlot)); - body.append(Utility.createConstant(fact, superParamTypes.length + i - 1)); - body.append(InstructionFactory.createArrayLoad(Type.OBJECT)); - body.append(Utility.createConversion(fact, Type.OBJECT, BcelWorld.makeBcelType(postParamTypes[i]))); - } - - // call post - body.append(Utility.createInvoke(fact, null, postMethod)); - - // don't forget to return!! - body.append(InstructionConstants.RETURN); - - addNeededSuperCallMethods(weaver, onType, munger.getSuperMethodsCalled()); - - return true; - } - - private static LazyMethodGen makeDispatcher(LazyClassGen onGen, String dispatchName, ResolvedMember superMethod, - BcelWorld world, boolean isSuper) { - Type[] paramTypes = BcelWorld.makeBcelTypes(superMethod.getParameterTypes()); - Type returnType = BcelWorld.makeBcelType(superMethod.getReturnType()); - - int modifiers = Modifier.PUBLIC; - if (onGen.isInterface()) { - modifiers |= Modifier.ABSTRACT; - } - - LazyMethodGen mg = new LazyMethodGen(modifiers, returnType, dispatchName, paramTypes, UnresolvedType.getNames(superMethod - .getExceptions()), onGen); - InstructionList body = mg.getBody(); - - if (onGen.isInterface()) { - return mg; - } - - // assert (!superMethod.isStatic()) - InstructionFactory fact = onGen.getFactory(); - int pos = 0; - - body.append(InstructionFactory.createThis()); - pos++; - for (int i = 0, len = paramTypes.length; i < len; i++) { - Type paramType = paramTypes[i]; - body.append(InstructionFactory.createLoad(paramType, pos)); - pos += paramType.getSize(); - } - if (isSuper) { - body.append(Utility.createSuperInvoke(fact, world, superMethod)); - } else { - body.append(Utility.createInvoke(fact, world, superMethod)); - } - body.append(InstructionFactory.createReturn(returnType)); - - return mg; - } - - private boolean mungeNewField(BcelClassWeaver weaver, NewFieldTypeMunger munger) { - /* ResolvedMember initMethod = */munger.getInitMethod(aspectType); - LazyClassGen gen = weaver.getLazyClassGen(); - ResolvedMember field = munger.getSignature(); - - ResolvedType onType = weaver.getWorld().resolve(field.getDeclaringType(), munger.getSourceLocation()); - if (onType.isRawType()) { - onType = onType.getGenericType(); - } - - boolean onInterface = onType.isInterface(); - - if (onType.isAnnotation()) { - signalError(WeaverMessages.ITDF_ON_ANNOTATION_NOT_ALLOWED, weaver, onType); - return false; - } - - if (onType.isEnum()) { - signalError(WeaverMessages.ITDF_ON_ENUM_NOT_ALLOWED, weaver, onType); - return false; - } - - ResolvedMember interMethodBody = munger.getInitMethod(aspectType); - - AnnotationAJ annotationsOnRealMember[] = null; - // pr98901 - // For copying the annotations across, we have to discover the real - // member in the aspect - // which is holding them. - if (weaver.getWorld().isInJava5Mode()) { - // the below line just gets the method with the same name in - // aspectType.getDeclaredMethods(); - ResolvedType toLookOn = aspectType; - if (aspectType.isRawType()) { - toLookOn = aspectType.getGenericType(); - } - ResolvedMember realMember = getRealMemberForITDFromAspect(toLookOn, interMethodBody, false); - if (realMember == null) { - // signalWarning("Unable to apply any annotations attached to " + munger.getSignature(), weaver); - // throw new BCException("Couldn't find ITD init member '" + interMethodBody + "' on aspect " + aspectType); - } else { - annotationsOnRealMember = realMember.getAnnotations(); - } - } - - if (onType.equals(gen.getType())) { - if (onInterface) { - ResolvedMember itdfieldGetter = AjcMemberMaker.interFieldInterfaceGetter(field, onType, aspectType); - LazyMethodGen mg = makeMethodGen(gen, itdfieldGetter); - gen.addMethodGen(mg); - - LazyMethodGen mg1 = makeMethodGen(gen, AjcMemberMaker.interFieldInterfaceSetter(field, onType, aspectType)); - gen.addMethodGen(mg1); - } else { - weaver.addInitializer(this); - ResolvedMember newField = AjcMemberMaker.interFieldClassField(field, aspectType, - munger.version == NewFieldTypeMunger.VersionTwo); - FieldGen fg = makeFieldGen(gen, newField); - - if (annotationsOnRealMember != null) { - for (int i = 0; i < annotationsOnRealMember.length; i++) { - AnnotationAJ annotationX = annotationsOnRealMember[i]; - AnnotationGen a = ((BcelAnnotation) annotationX).getBcelAnnotation(); - AnnotationGen ag = new AnnotationGen(a, weaver.getLazyClassGen().getConstantPool(), true); - fg.addAnnotation(ag); - } - } - - if (weaver.getWorld().isInJava5Mode()) { - String basicSignature = field.getSignature(); - String genericSignature = field.getReturnType().resolve(weaver.getWorld()).getSignatureForAttribute(); - // String genericSignature = - // ((ResolvedMemberImpl)field).getSignatureForAttribute(); - if (!basicSignature.equals(genericSignature)) { - // Add a signature attribute to it - fg.addAttribute(createSignatureAttribute(gen.getConstantPool(), genericSignature)); - } - } - gen.addField(fg, getSourceLocation()); - - } - return true; - } else if (onInterface && gen.getType().isTopmostImplementor(onType)) { - // we know that we can't be static since we don't allow statics on interfaces - if (Modifier.isStatic(field.getModifiers())) { - throw new RuntimeException("unimplemented"); - } - - boolean alreadyExists = false; - // only need to check for version 2 style mungers - if (munger.version==NewFieldTypeMunger.VersionTwo) { - for (BcelField fieldgen: gen.getFieldGens()) { - if (fieldgen.getName().equals(field.getName())) { - alreadyExists=true; - break; - } - } - } - - // FieldGen fg = makeFieldGen(gen, AjcMemberMaker.interFieldInterfaceField(field, onType, aspectType)); - ResolvedMember newField = AjcMemberMaker.interFieldInterfaceField(field, onType, aspectType, munger.version == NewFieldTypeMunger.VersionTwo); - String fieldName = newField.getName(); - - Type fieldType = BcelWorld.makeBcelType(field.getType()); - if (!alreadyExists) { - weaver.addInitializer(this); - FieldGen fg = makeFieldGen(gen,newField); - if (annotationsOnRealMember != null) { - for (int i = 0; i < annotationsOnRealMember.length; i++) { - AnnotationAJ annotationX = annotationsOnRealMember[i]; - AnnotationGen a = ((BcelAnnotation) annotationX).getBcelAnnotation(); - AnnotationGen ag = new AnnotationGen(a, weaver.getLazyClassGen().getConstantPool(), true); - fg.addAnnotation(ag); - } - } - - if (weaver.getWorld().isInJava5Mode()) { - String basicSignature = field.getSignature(); - String genericSignature = field.getReturnType().resolve(weaver.getWorld()).getSignatureForAttribute(); - // String genericSignature = - // ((ResolvedMemberImpl)field).getSignatureForAttribute(); - if (!basicSignature.equals(genericSignature)) { - // Add a signature attribute to it - fg.addAttribute(createSignatureAttribute(gen.getConstantPool(), genericSignature)); - } - } - gen.addField(fg, getSourceLocation()); - } - // this uses a shadow munger to add init method to constructors - // weaver.getShadowMungers().add(makeInitCallShadowMunger(initMethod) - // ); - - ResolvedMember itdfieldGetter = AjcMemberMaker.interFieldInterfaceGetter(field, gen.getType()/* onType */, aspectType); - LazyMethodGen mg = makeMethodGen(gen, itdfieldGetter); - InstructionList il = new InstructionList(); - InstructionFactory fact = gen.getFactory(); - if (Modifier.isStatic(field.getModifiers())) { - il.append(fact.createFieldAccess(gen.getClassName(), fieldName, fieldType, Constants.GETSTATIC)); - } else { - il.append(InstructionConstants.ALOAD_0); - il.append(fact.createFieldAccess(gen.getClassName(), fieldName, fieldType, Constants.GETFIELD)); - } - il.append(InstructionFactory.createReturn(fieldType)); - mg.getBody().insert(il); - - gen.addMethodGen(mg); - - // Check if we need bridge methods for the field getter and setter - if (munger.getDeclaredSignature() != null) { // is this munger a - // parameterized - // form of some - // original munger? - ResolvedMember toBridgeTo = munger.getDeclaredSignature().parameterizedWith(null, - munger.getSignature().getDeclaringType().resolve(getWorld()), false, munger.getTypeVariableAliases()); - boolean needsbridging = false; - if (!toBridgeTo.getReturnType().getErasureSignature() - .equals(munger.getSignature().getReturnType().getErasureSignature())) { - needsbridging = true; - } - if (needsbridging) { - ResolvedMember bridgingGetter = AjcMemberMaker.interFieldInterfaceGetter(toBridgeTo, gen.getType(), aspectType); - createBridgeMethodForITDF(weaver, gen, itdfieldGetter, bridgingGetter); - } - } - - ResolvedMember itdfieldSetter = AjcMemberMaker.interFieldInterfaceSetter(field, gen.getType(), aspectType); - LazyMethodGen mg1 = makeMethodGen(gen, itdfieldSetter); - InstructionList il1 = new InstructionList(); - if (Modifier.isStatic(field.getModifiers())) { - il1.append(InstructionFactory.createLoad(fieldType, 0)); - il1.append(fact.createFieldAccess(gen.getClassName(), fieldName, fieldType, Constants.PUTSTATIC)); - } else { - il1.append(InstructionConstants.ALOAD_0); - il1.append(InstructionFactory.createLoad(fieldType, 1)); - il1.append(fact.createFieldAccess(gen.getClassName(), fieldName, fieldType, Constants.PUTFIELD)); - } - il1.append(InstructionFactory.createReturn(Type.VOID)); - mg1.getBody().insert(il1); - - gen.addMethodGen(mg1); - - if (munger.getDeclaredSignature() != null) { - ResolvedMember toBridgeTo = munger.getDeclaredSignature().parameterizedWith(null, - munger.getSignature().getDeclaringType().resolve(getWorld()), false, munger.getTypeVariableAliases()); - boolean needsbridging = false; - if (!toBridgeTo.getReturnType().getErasureSignature() - .equals(munger.getSignature().getReturnType().getErasureSignature())) { - needsbridging = true; - } - if (needsbridging) { - ResolvedMember bridgingSetter = AjcMemberMaker.interFieldInterfaceSetter(toBridgeTo, gen.getType(), aspectType); - createBridgeMethodForITDF(weaver, gen, itdfieldSetter, bridgingSetter); - } - } - - return true; - } else { - return false; - } - } - - // FIXME asc combine with other createBridge.. method in this class, avoid - // the duplication... - private void createBridgeMethodForITDF(BcelClassWeaver weaver, LazyClassGen gen, ResolvedMember itdfieldSetter, - ResolvedMember bridgingSetter) { - InstructionFactory fact; - LazyMethodGen bridgeMethod = makeMethodGen(gen, bridgingSetter); - bridgeMethod.setAccessFlags(bridgeMethod.getAccessFlags() | 0x00000040); // BRIDGE = 0x00000040 - Type[] paramTypes = BcelWorld.makeBcelTypes(bridgingSetter.getParameterTypes()); - Type[] bridgingToParms = BcelWorld.makeBcelTypes(itdfieldSetter.getParameterTypes()); - Type returnType = BcelWorld.makeBcelType(bridgingSetter.getReturnType()); - InstructionList body = bridgeMethod.getBody(); - fact = gen.getFactory(); - int pos = 0; - - if (!Modifier.isStatic(bridgingSetter.getModifiers())) { - body.append(InstructionFactory.createThis()); - pos++; - } - for (int i = 0, len = paramTypes.length; i < len; i++) { - Type paramType = paramTypes[i]; - body.append(InstructionFactory.createLoad(paramType, pos)); - if (!bridgingSetter.getParameterTypes()[i].getErasureSignature().equals( - itdfieldSetter.getParameterTypes()[i].getErasureSignature())) { - body.append(fact.createCast(paramType, bridgingToParms[i])); - } - pos += paramType.getSize(); - } - - body.append(Utility.createInvoke(fact, weaver.getWorld(), itdfieldSetter)); - body.append(InstructionFactory.createReturn(returnType)); - gen.addMethodGen(bridgeMethod); - } - - @Override - public ConcreteTypeMunger parameterizedFor(ResolvedType target) { - return new BcelTypeMunger(munger.parameterizedFor(target), aspectType); - } - - @Override - public ConcreteTypeMunger parameterizeWith(Map m, World w) { - return new BcelTypeMunger(munger.parameterizeWith(m, w), aspectType); - } - - /** - * Returns a list of type variable aliases used in this munger. For example, if the ITD is 'int I.m(List las,List - * lbs) {}' then this returns a list containing the strings "A" and "B". - */ - public List getTypeVariableAliases() { - return munger.getTypeVariableAliases(); - } - - @Override - public boolean equals(Object other) { - if (!(other instanceof BcelTypeMunger)) { - return false; - } - BcelTypeMunger o = (BcelTypeMunger) other; - return ((o.getMunger() == null) ? (getMunger() == null) : o.getMunger().equals(getMunger())) - && ((o.getAspectType() == null) ? (getAspectType() == null) : o.getAspectType().equals(getAspectType())); - // && (AsmManager.getDefault().getHandleProvider().dependsOnLocation() ? ((o.getSourceLocation() == null) ? - // (getSourceLocation() == null) - // : o.getSourceLocation().equals(getSourceLocation())) - // : true); // pr134471 - remove when handles are improved - // to be independent of location - - } - - private volatile int hashCode = 0; - - @Override - public int hashCode() { - if (hashCode == 0) { - int result = 17; - result = 37 * result + ((getMunger() == null) ? 0 : getMunger().hashCode()); - result = 37 * result + ((getAspectType() == null) ? 0 : getAspectType().hashCode()); - hashCode = result; - } - return hashCode; - } -} diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelVar.java b/weaver/src/org/aspectj/weaver/bcel/BcelVar.java deleted file mode 100644 index ce45fdf12..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/BcelVar.java +++ /dev/null @@ -1,117 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - -package org.aspectj.weaver.bcel; - -import org.aspectj.apache.bcel.generic.Instruction; -import org.aspectj.apache.bcel.generic.InstructionFactory; -import org.aspectj.apache.bcel.generic.InstructionList; -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.ast.Var; - -public class BcelVar extends Var { - - private int positionInAroundState = -1; - - private int slot; - - public BcelVar(ResolvedType type, int slot) { - super(type); - this.slot = slot; - } - - public String toString() { - return "BcelVar(" + getType() + " " + slot + ((positionInAroundState != -1) ? (" " + positionInAroundState) : "") + - - ")"; - } - - public int getSlot() { - return slot; - } - - // fact is used in the subtypes - public Instruction createLoad(InstructionFactory fact) { - return InstructionFactory.createLoad(BcelWorld.makeBcelType(getType()), slot); - } - - public Instruction createStore(InstructionFactory fact) { - return InstructionFactory.createStore(BcelWorld.makeBcelType(getType()), slot); - } - - public void appendStore(InstructionList il, InstructionFactory fact) { - il.append(createStore(fact)); - } - - public void appendLoad(InstructionList il, InstructionFactory fact) { - il.append(createLoad(fact)); - } - - public void appendLoadAndConvert(InstructionList il, InstructionFactory fact, ResolvedType toType) { - il.append(createLoad(fact)); - Utility.appendConversion(il, fact, getType(), toType); - } - - public void insertLoad(InstructionList il, InstructionFactory fact) { - il.insert(createLoad(fact)); - } - - public InstructionList createCopyFrom(InstructionFactory fact, int oldSlot) { - InstructionList il = new InstructionList(); - il.append(InstructionFactory.createLoad(BcelWorld.makeBcelType(getType()), oldSlot)); - il.append(createStore(fact)); - return il; - } - - // this is an array var - void appendConvertableArrayLoad(InstructionList il, InstructionFactory fact, int index, ResolvedType convertTo) { - ResolvedType convertFromType = getType().getResolvedComponentType(); - appendLoad(il, fact); - il.append(Utility.createConstant(fact, index)); - il.append(InstructionFactory.createArrayLoad(BcelWorld.makeBcelType(convertFromType))); - Utility.appendConversion(il, fact, convertFromType, convertTo); - } - - void appendConvertableArrayStore(InstructionList il, InstructionFactory fact, int index, BcelVar storee) { - ResolvedType convertToType = getType().getResolvedComponentType(); - appendLoad(il, fact); - il.append(Utility.createConstant(fact, index)); - storee.appendLoad(il, fact); - Utility.appendConversion(il, fact, storee.getType(), convertToType); - il.append(InstructionFactory.createArrayStore(BcelWorld.makeBcelType(convertToType))); - } - - InstructionList createConvertableArrayStore(InstructionFactory fact, int index, BcelVar storee) { - InstructionList il = new InstructionList(); - appendConvertableArrayStore(il, fact, index, storee); - return il; - } - - InstructionList createConvertableArrayLoad(InstructionFactory fact, int index, ResolvedType convertTo) { - InstructionList il = new InstructionList(); - appendConvertableArrayLoad(il, fact, index, convertTo); - return il; - } - - public int getPositionInAroundState() { - return positionInAroundState; - } - - public void setPositionInAroundState(int positionInAroundState) { - this.positionInAroundState = positionInAroundState; - } - - // random useful fields - - public static final BcelVar[] NONE = new BcelVar[] {}; - -} diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelWeakClassLoaderReference.java b/weaver/src/org/aspectj/weaver/bcel/BcelWeakClassLoaderReference.java deleted file mode 100644 index aac294fa4..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/BcelWeakClassLoaderReference.java +++ /dev/null @@ -1,49 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2008 Contributors - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Andy Clement initial implementation - * ******************************************************************/ -package org.aspectj.weaver.bcel; - -import org.aspectj.apache.bcel.util.ClassLoaderReference; -import org.aspectj.weaver.WeakClassLoaderReference; - -/** - * Wraps a reference to a classloader inside a WeakReference. This should be used where we do not want the existence of a - * classloader reference to prevent garbage collection of that classloader (and possibly an associated weaver instance in the case - * of load time weaving). - *

- * In more detail:
- * When load time weaving, the class Aj maintains a WeakHashMap from the classloader instance to a weaver instance. The aim is that - * the weaver is around as long as the classloader is and should the classloader be dereferenced then the weaver can also be garbage - * collected. The problem is that if there are many references to the classloader from within the weaver, these are considered hard - * references and cause the classloader to be long lived - even if the user of the classloader has dereferenced it in their code. - * The solution is that the weaver should use instances of WeakClassLoaderReference objects - so that when the users hard reference - * to the classloader goes, nothing in the weaver will cause it to hang around. There is a big assertion here that the - * WeakClassLoaderReference instances will not 'lose' their ClassLoader references until the top level ClassLoader reference is - * null'd. This means there is no need to check for the null case on get() in this WeakReference logic below, because we shouldn't - * be using this weaver if its associated ClassLoader has been collected. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=210470 - * - * - * @author Andy Clement - */ -public class BcelWeakClassLoaderReference extends WeakClassLoaderReference implements ClassLoaderReference { - - public BcelWeakClassLoaderReference(ClassLoader loader) { - super(loader); - } - - public boolean equals(Object obj) { - if (!(obj instanceof BcelWeakClassLoaderReference)) - return false; - BcelWeakClassLoaderReference other = (BcelWeakClassLoaderReference) obj; - return (other.hashcode == hashcode); - } - -} diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelWeaver.java b/weaver/src/org/aspectj/weaver/bcel/BcelWeaver.java deleted file mode 100644 index f83a79379..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/BcelWeaver.java +++ /dev/null @@ -1,2043 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002-2010 Contributors - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * ******************************************************************/ -package org.aspectj.weaver.bcel; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileFilter; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.jar.Attributes; -import java.util.jar.Attributes.Name; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import java.util.jar.Manifest; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; -import java.util.zip.ZipOutputStream; - -import org.aspectj.apache.bcel.classfile.ClassParser; -import org.aspectj.apache.bcel.classfile.JavaClass; -import org.aspectj.asm.AsmManager; -import org.aspectj.asm.IProgramElement; -import org.aspectj.asm.internal.AspectJElementHierarchy; -import org.aspectj.bridge.IMessage; -import org.aspectj.bridge.ISourceLocation; -import org.aspectj.bridge.Message; -import org.aspectj.bridge.MessageUtil; -import org.aspectj.bridge.SourceLocation; -import org.aspectj.bridge.WeaveMessage; -import org.aspectj.bridge.context.CompilationAndWeavingContext; -import org.aspectj.bridge.context.ContextToken; -import org.aspectj.util.FileUtil; -import org.aspectj.util.FuzzyBoolean; -import org.aspectj.weaver.Advice; -import org.aspectj.weaver.AdviceKind; -import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; -import org.aspectj.weaver.AnnotationAJ; -import org.aspectj.weaver.AnnotationOnTypeMunger; -import org.aspectj.weaver.BCException; -import org.aspectj.weaver.CompressingDataOutputStream; -import org.aspectj.weaver.ConcreteTypeMunger; -import org.aspectj.weaver.CrosscuttingMembersSet; -import org.aspectj.weaver.CustomMungerFactory; -import org.aspectj.weaver.IClassFileProvider; -import org.aspectj.weaver.IUnwovenClassFile; -import org.aspectj.weaver.IWeaveRequestor; -import org.aspectj.weaver.NewParentTypeMunger; -import org.aspectj.weaver.ReferenceType; -import org.aspectj.weaver.ReferenceTypeDelegate; -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.ResolvedTypeMunger; -import org.aspectj.weaver.Shadow; -import org.aspectj.weaver.ShadowMunger; -import org.aspectj.weaver.UnresolvedType; -import org.aspectj.weaver.WeaverMessages; -import org.aspectj.weaver.WeaverStateInfo; -import org.aspectj.weaver.World; -import org.aspectj.weaver.model.AsmRelationshipProvider; -import org.aspectj.weaver.patterns.AndPointcut; -import org.aspectj.weaver.patterns.BindingPattern; -import org.aspectj.weaver.patterns.BindingTypePattern; -import org.aspectj.weaver.patterns.ConcreteCflowPointcut; -import org.aspectj.weaver.patterns.DeclareAnnotation; -import org.aspectj.weaver.patterns.DeclareParents; -import org.aspectj.weaver.patterns.DeclareTypeErrorOrWarning; -import org.aspectj.weaver.patterns.FastMatchInfo; -import org.aspectj.weaver.patterns.IfPointcut; -import org.aspectj.weaver.patterns.KindedPointcut; -import org.aspectj.weaver.patterns.NameBindingPointcut; -import org.aspectj.weaver.patterns.NotPointcut; -import org.aspectj.weaver.patterns.OrPointcut; -import org.aspectj.weaver.patterns.Pointcut; -import org.aspectj.weaver.patterns.PointcutRewriter; -import org.aspectj.weaver.patterns.WithinPointcut; -import org.aspectj.weaver.tools.Trace; -import org.aspectj.weaver.tools.TraceFactory; - -/** - * - * @author PARC - * @author Andy Clement - * @author Alexandre Vasseur - */ -public class BcelWeaver { - - public static final String CLOSURE_CLASS_PREFIX = "$Ajc"; - public static final String SYNTHETIC_CLASS_POSTFIX = "$ajc"; - - private static Trace trace = TraceFactory.getTraceFactory().getTrace(BcelWeaver.class); - - private transient final BcelWorld world; - private final CrosscuttingMembersSet xcutSet; - - private boolean inReweavableMode = false; - - private transient List addedClasses = new ArrayList(); - private transient List deletedTypenames = new ArrayList(); - - // These four are setup by prepareForWeave - private transient List shadowMungerList = null; - private transient List typeMungerList = null; - private transient List lateTypeMungerList = null; - private transient List declareParentsList = null; - - private Manifest manifest = null; - private boolean needToReweaveWorld = false; - - private boolean isBatchWeave = true; - - private ZipOutputStream zipOutputStream; - private CustomMungerFactory customMungerFactory; - - public BcelWeaver(BcelWorld world) { - super(); - if (trace.isTraceEnabled()) { - trace.enter("", this, world); - } - this.world = world; - this.xcutSet = world.getCrosscuttingMembersSet(); - if (trace.isTraceEnabled()) { - trace.exit(""); - } - } - - /** - * Add the given aspect to the weaver. The type is resolved to support DOT for static inner classes as well as DOLLAR - * - * @param aspectName - * @return aspect - */ - public ResolvedType addLibraryAspect(String aspectName) { - if (trace.isTraceEnabled()) { - trace.enter("addLibraryAspect", this, aspectName); - } - - // 1 - resolve as is - UnresolvedType unresolvedT = UnresolvedType.forName(aspectName); - unresolvedT.setNeedsModifiableDelegate(true); - ResolvedType type = world.resolve(unresolvedT, true); - if (type.isMissing()) { - // fallback on inner class lookup mechanism - String fixedName = aspectName; - int hasDot = fixedName.lastIndexOf('.'); - while (hasDot > 0) { - // System.out.println("BcelWeaver.addLibraryAspect " + fixedName); - char[] fixedNameChars = fixedName.toCharArray(); - fixedNameChars[hasDot] = '$'; - fixedName = new String(fixedNameChars); - hasDot = fixedName.lastIndexOf('.'); - UnresolvedType ut = UnresolvedType.forName(fixedName); - ut.setNeedsModifiableDelegate(true); - type = world.resolve(ut, true); - if (!type.isMissing()) { - break; - } - } - } - - // System.out.println("type: " + type + " for " + aspectName); - if (type.isAspect()) { - // Bug 119657 ensure we use the unwoven aspect - WeaverStateInfo wsi = type.getWeaverState(); - if (wsi != null && wsi.isReweavable()) { - BcelObjectType classType = getClassType(type.getName()); - JavaClass wovenJavaClass = classType.getJavaClass(); - byte[] bytes = wsi.getUnwovenClassFileData(wovenJavaClass.getBytes()); - JavaClass unwovenJavaClass = Utility.makeJavaClass(wovenJavaClass.getFileName(), bytes); - world.storeClass(unwovenJavaClass); - classType.setJavaClass(unwovenJavaClass, true); - // classType.setJavaClass(Utility.makeJavaClass(classType. - // getJavaClass().getFileName(), - // wsi.getUnwovenClassFileData(classType.getJavaClass().getBytes( - // )))); - } - - // TODO AV - happens to reach that a lot of time: for each type - // flagged reweavable X for each aspect in the weaverstate - // => mainly for nothing for LTW - pbly for something in incremental - // build... - xcutSet.addOrReplaceAspect(type); - if (trace.isTraceEnabled()) { - trace.exit("addLibraryAspect", type); - } - if (type.getSuperclass().isAspect()) { - // If the supertype includes ITDs and the user has not included - // that aspect in the aop.xml, they will - // not get picked up, which can give unusual behaviour! See bug - // 223094 - // This change causes us to pick up the super aspect regardless - // of what was said in the aop.xml - giving - // predictable behaviour. If the user also supplied it, there - // will be no problem other than the second - // addition overriding the first - addLibraryAspect(type.getSuperclass().getName()); - } - return type; - } else { - if (type.isMissing()) { - // May not be found if not visible to the classloader that can see the aop.xml during LTW - IMessage message = new Message("The specified aspect '"+aspectName+"' cannot be found", null, true); - world.getMessageHandler().handleMessage(message); - } else { - IMessage message = new Message("Cannot register '"+aspectName+"' because the type found with that name is not an aspect", null, true); - world.getMessageHandler().handleMessage(message); - } - return null; - } - } - - /** - * - * @param inFile directory containing classes or zip/jar class archive - */ - public void addLibraryJarFile(File inFile) throws IOException { - List addedAspects = null; - if (inFile.isDirectory()) { - addedAspects = addAspectsFromDirectory(inFile); - } else { - addedAspects = addAspectsFromJarFile(inFile); - } - for (ResolvedType addedAspect : addedAspects) { - xcutSet.addOrReplaceAspect(addedAspect); - } - } - - private List addAspectsFromJarFile(File inFile) throws FileNotFoundException, IOException { - ZipInputStream inStream = new ZipInputStream(new FileInputStream(inFile)); // ??? buffered - List addedAspects = new ArrayList(); - try { - while (true) { - ZipEntry entry = inStream.getNextEntry(); - if (entry == null) { - break; - } - - if (entry.isDirectory() || !entry.getName().endsWith(".class")) { - continue; - } - - // FIXME ASC performance? of this alternative soln. - int size = (int) entry.getSize(); - ClassParser parser = new ClassParser(new ByteArrayInputStream(FileUtil.readAsByteArray(inStream)), entry.getName()); - JavaClass jc = parser.parse(); - inStream.closeEntry(); - - ResolvedType type = world.addSourceObjectType(jc, false).getResolvedTypeX(); - type.setBinaryPath(inFile.getAbsolutePath()); - if (type.isAspect()) { - addedAspects.add(type); - } else { - world.demote(type); - } - - } - } finally { - inStream.close(); - } - return addedAspects; - } - - /** - * Look for .class files that represent aspects in the supplied directory - return the list of accumulated aspects. - * - * @param directory the directory in which to look for Aspect .class files - * @return the list of discovered aspects - * @throws FileNotFoundException - * @throws IOException - */ - private List addAspectsFromDirectory(File directory) throws FileNotFoundException, IOException { - List addedAspects = new ArrayList(); - File[] classFiles = FileUtil.listFiles(directory, new FileFilter() { - public boolean accept(File pathname) { - return pathname.getName().endsWith(".class"); - } - }); - for (File classFile : classFiles) { - FileInputStream fis = new FileInputStream(classFile); - byte[] classBytes = FileUtil.readAsByteArray(fis); - ResolvedType aspectType = isAspect(classBytes, classFile.getAbsolutePath(), directory); - if (aspectType != null) { - addedAspects.add(aspectType); - } - fis.close(); - } - return addedAspects; - } - - /** - * Determine if the supplied bytes represent an aspect, if they do then create a ResolvedType instance for the aspect and return - * it, otherwise return null - * - * @param classbytes the classbytes that might represent an aspect - * @param name the name of the class - * @param directory directory which contained the class file - * @return a ResolvedType if the classbytes represent an aspect, otherwise null - */ - private ResolvedType isAspect(byte[] classbytes, String name, File dir) throws IOException { - ClassParser parser = new ClassParser(new ByteArrayInputStream(classbytes), name); - JavaClass jc = parser.parse(); - ResolvedType type = world.addSourceObjectType(jc, false).getResolvedTypeX(); - String typeName = type.getName().replace('.', File.separatorChar); - int end = name.lastIndexOf(typeName + ".class"); - String binaryPath = null; - // if end is -1 then something weird happened, the class file is not in - // the correct place, something like - // bin/A.class when the declaration for A specifies it is in a package. - if (end == -1) { - binaryPath = dir.getAbsolutePath(); - } else { - binaryPath = name.substring(0, end - 1); - } - type.setBinaryPath(binaryPath); - if (type.isAspect()) { - return type; - } else { - // immediately demote the type we just added since it will have - // have been stuffed into the permanent map (assumed to be - // an aspect) - world.demote(type); - return null; - } - } - - // // The ANT copy task should be used to copy resources across. - // private final static boolean - // CopyResourcesFromInpathDirectoriesToOutput=false; - - /** - * Add any .class files in the directory to the outdir. Anything other than .class files in the directory (or its - * subdirectories) are considered resources and are also copied. - * - */ - public List addDirectoryContents(File inFile, File outDir) throws IOException { - List addedClassFiles = new ArrayList(); - - // Get a list of all files (i.e. everything that isnt a directory) - File[] files = FileUtil.listFiles(inFile, new FileFilter() { - public boolean accept(File f) { - boolean accept = !f.isDirectory(); - return accept; - } - }); - - // For each file, add it either as a real .class file or as a resource - for (int i = 0; i < files.length; i++) { - addedClassFiles.add(addClassFile(files[i], inFile, outDir)); - } - - return addedClassFiles; - } - - /** - * Adds all class files in the jar - */ - public List addJarFile(File inFile, File outDir, boolean canBeDirectory) { - // System.err.println("? addJarFile(" + inFile + ", " + outDir + ")"); - List addedClassFiles = new ArrayList(); - needToReweaveWorld = true; - JarFile inJar = null; - - try { - // Is this a directory we are looking at? - if (inFile.isDirectory() && canBeDirectory) { - addedClassFiles.addAll(addDirectoryContents(inFile, outDir)); - } else { - - inJar = new JarFile(inFile); - try { - addManifest(inJar.getManifest()); - Enumeration entries = inJar.entries(); - - while (entries.hasMoreElements()) { - JarEntry entry = (JarEntry) entries.nextElement(); - InputStream inStream = inJar.getInputStream(entry); - - byte[] bytes = FileUtil.readAsByteArray(inStream); - String filename = entry.getName(); - // System.out.println("? addJarFile() filename='" + filename - // + "'"); - UnwovenClassFile classFile = new UnwovenClassFile(new File(outDir, filename).getAbsolutePath(), bytes); - - if (filename.endsWith(".class")) { - ReferenceType type = this.addClassFile(classFile, false); - StringBuffer sb = new StringBuffer(); - sb.append(inFile.getAbsolutePath()); - sb.append("!"); - sb.append(entry.getName()); - type.setBinaryPath(sb.toString()); - addedClassFiles.add(classFile); - } - // else if (!entry.isDirectory()) { - // - // /* bug-44190 Copy meta-data */ - // addResource(filename,classFile); - // } - - inStream.close(); - } - } finally { - inJar.close(); - } - inJar.close(); - } - } catch (FileNotFoundException ex) { - IMessage message = new Message("Could not find input jar file " + inFile.getPath() + ", ignoring", new SourceLocation( - inFile, 0), false); - world.getMessageHandler().handleMessage(message); - } catch (IOException ex) { - IMessage message = new Message("Could not read input jar file " + inFile.getPath() + "(" + ex.getMessage() + ")", - new SourceLocation(inFile, 0), true); - world.getMessageHandler().handleMessage(message); - } finally { - if (inJar != null) { - try { - inJar.close(); - } catch (IOException ex) { - IMessage message = new Message("Could not close input jar file " + inFile.getPath() + "(" + ex.getMessage() - + ")", new SourceLocation(inFile, 0), true); - world.getMessageHandler().handleMessage(message); - } - } - } - - return addedClassFiles; - } - - public boolean needToReweaveWorld() { - return needToReweaveWorld; - } - - /** - * Should be addOrReplace - */ - public ReferenceType addClassFile(UnwovenClassFile classFile, boolean fromInpath) { - addedClasses.add(classFile); - ReferenceType type = world.addSourceObjectType(classFile.getJavaClass(), false).getResolvedTypeX(); - if (fromInpath) { - type.setBinaryPath(classFile.getFilename()); - } - return type; - } - - public UnwovenClassFile addClassFile(File classFile, File inPathDir, File outDir) throws IOException { - FileInputStream fis = new FileInputStream(classFile); - byte[] bytes = FileUtil.readAsByteArray(fis); - // String relativePath = files[i].getPath(); - - // ASSERT: - // files[i].getAbsolutePath().startsWith(inFile.getAbsolutePath() - // or we are in trouble... - String filename = classFile.getAbsolutePath().substring(inPathDir.getAbsolutePath().length() + 1); - UnwovenClassFile ucf = new UnwovenClassFile(new File(outDir, filename).getAbsolutePath(), bytes); - if (filename.endsWith(".class")) { - // System.err.println( - // "BCELWeaver: processing class from input directory "+classFile); - StringBuffer sb = new StringBuffer(); - sb.append(inPathDir.getAbsolutePath()); - sb.append("!"); - sb.append(filename); - ReferenceType type = this.addClassFile(ucf, false); - type.setBinaryPath(sb.toString()); - } - fis.close(); - return ucf; - } - - public void deleteClassFile(String typename) { - deletedTypenames.add(typename); - world.deleteSourceObjectType(UnresolvedType.forName(typename)); - } - - // ---- weave preparation - - public void setIsBatchWeave(boolean b) { - isBatchWeave = b; - } - - public void prepareForWeave() { - if (trace.isTraceEnabled()) { - trace.enter("prepareForWeave", this); - } - needToReweaveWorld = xcutSet.hasChangedSinceLastReset(); - - // update mungers - for (Iterator i = addedClasses.iterator(); i.hasNext();) { - UnwovenClassFile jc = i.next(); - String name = jc.getClassName(); - ResolvedType type = world.resolve(name); - // No overweaving guard. If you have one then when overweaving is on the - // addOrReplaceAspect will not be called when the aspect delegate changes from - // EclipseSourceType to BcelObjectType. This will mean the mungers - // are not picked up. - if (type.isAspect()) { - needToReweaveWorld |= xcutSet.addOrReplaceAspect(type); - } - } - - for (Iterator i = deletedTypenames.iterator(); i.hasNext();) { - String name = i.next(); - if (xcutSet.deleteAspect(UnresolvedType.forName(name))) { - needToReweaveWorld = true; - } - } - - shadowMungerList = xcutSet.getShadowMungers(); - // world.debug("shadow mungers=" + shadowMungerList); - rewritePointcuts(shadowMungerList); - // Sometimes an error occurs during rewriting pointcuts (for example, if - // ambiguous bindings - // are detected) - we ought to fail the prepare when this happens - // because continuing with - // inconsistent pointcuts could lead to problems - typeMungerList = xcutSet.getTypeMungers(); - lateTypeMungerList = xcutSet.getLateTypeMungers(); - declareParentsList = xcutSet.getDeclareParents(); - - addCustomMungers(); - - // The ordering here used to be based on a string compare on toString() - // for the two mungers - - // that breaks for the @AJ style where advice names aren't - // programmatically generated. So we - // have changed the sorting to be based on source location in the file - - // this is reliable, in - // the case of source locations missing, we assume they are 'sorted' - - // i.e. the order in - // which they were added to the collection is correct, this enables the - // @AJ stuff to work properly. - - // When @AJ processing starts filling in source locations for mungers, - // this code may need - // a bit of alteration... - - Collections.sort(shadowMungerList, new Comparator() { - public int compare(ShadowMunger sm1, ShadowMunger sm2) { - if (sm1.getSourceLocation() == null) { - return (sm2.getSourceLocation() == null ? 0 : 1); - } - if (sm2.getSourceLocation() == null) { - return -1; - } - - return (sm2.getSourceLocation().getOffset() - sm1.getSourceLocation().getOffset()); - } - }); - - if (inReweavableMode) { - world.showMessage(IMessage.INFO, WeaverMessages.format(WeaverMessages.REWEAVABLE_MODE), null, null); - } - - if (trace.isTraceEnabled()) { - trace.exit("prepareForWeave"); - } - } - - private void addCustomMungers() { - if (customMungerFactory != null) { - for (Iterator i = addedClasses.iterator(); i.hasNext();) { - UnwovenClassFile jc = i.next(); - String name = jc.getClassName(); - ResolvedType type = world.resolve(name); - if (type.isAspect()) { - Collection shadowMungers = customMungerFactory.createCustomShadowMungers(type); - if (shadowMungers != null) { - shadowMungerList.addAll(shadowMungers); - } - Collection typeMungers = customMungerFactory.createCustomTypeMungers(type); - if (typeMungers != null) { - typeMungerList.addAll(typeMungers); - } - } - } - } - } - - public void setCustomMungerFactory(CustomMungerFactory factory) { - customMungerFactory = factory; - } - - /* - * Rewrite all of the pointcuts in the world into their most efficient form for subsequent matching. Also ensure that if - * pc1.equals(pc2) then pc1 == pc2 (for non-binding pcds) by making references all point to the same instance. Since pointcuts - * remember their match decision on the last shadow, this makes matching faster when many pointcuts share common elements, or - * even when one single pointcut has one common element (which can be a side-effect of DNF rewriting). - */ - private void rewritePointcuts(List shadowMungers) { - PointcutRewriter rewriter = new PointcutRewriter(); - for (ShadowMunger munger : shadowMungers) { - Pointcut p = munger.getPointcut(); - Pointcut newP = rewriter.rewrite(p); - // validateBindings now whilst we still have around the pointcut - // that resembles what the user actually wrote in their program - // text. - if (munger instanceof Advice) { - Advice advice = (Advice) munger; - if (advice.getSignature() != null) { - final int numFormals; - final String names[]; - // If the advice is being concretized in a @AJ aspect *and* - // the advice was declared in - // an @AJ aspect (it could have been inherited from a code - // style aspect) then - // evaluate the alternative set of formals. pr125699 - if ((advice.getConcreteAspect().isAnnotationStyleAspect() && advice.getDeclaringAspect() != null && advice - .getDeclaringAspect().resolve(world).isAnnotationStyleAspect()) - || advice.isAnnotationStyle()) { - numFormals = advice.getBaseParameterCount(); - int numArgs = advice.getSignature().getParameterTypes().length; - if (numFormals > 0) { - names = advice.getSignature().getParameterNames(world); - validateBindings(newP, p, numArgs, names); - } - } else { - numFormals = advice.getBaseParameterCount(); - if (numFormals > 0) { - names = advice.getBaseParameterNames(world); - validateBindings(newP, p, numFormals, names); - } - } - } - } - newP.m_ignoreUnboundBindingForNames = p.m_ignoreUnboundBindingForNames; - munger.setPointcut(newP); - } - // now that we have optimized individual pointcuts, optimize - // across the set of pointcuts.... - // Use a map from key based on pc equality, to value based on - // pc identity. - Map pcMap = new HashMap(); - for (ShadowMunger munger: shadowMungers) { - Pointcut p = munger.getPointcut(); - Pointcut newP = shareEntriesFromMap(p, pcMap); - newP.m_ignoreUnboundBindingForNames = p.m_ignoreUnboundBindingForNames; - munger.setPointcut(newP); - } - } - - private Pointcut shareEntriesFromMap(Pointcut p, Map pcMap) { - // some things cant be shared... - if (p instanceof NameBindingPointcut) { - return p; - } - if (p instanceof IfPointcut) { - return p; - } - if (p instanceof ConcreteCflowPointcut) { - return p; - } - if (p instanceof AndPointcut) { - AndPointcut apc = (AndPointcut) p; - Pointcut left = shareEntriesFromMap(apc.getLeft(), pcMap); - Pointcut right = shareEntriesFromMap(apc.getRight(), pcMap); - return new AndPointcut(left, right); - } else if (p instanceof OrPointcut) { - OrPointcut opc = (OrPointcut) p; - Pointcut left = shareEntriesFromMap(opc.getLeft(), pcMap); - Pointcut right = shareEntriesFromMap(opc.getRight(), pcMap); - return new OrPointcut(left, right); - } else if (p instanceof NotPointcut) { - NotPointcut npc = (NotPointcut) p; - Pointcut not = shareEntriesFromMap(npc.getNegatedPointcut(), pcMap); - return new NotPointcut(not); - } else { - // primitive pcd - if (pcMap.containsKey(p)) { // based on equality - return pcMap.get(p); // same instance (identity) - } else { - pcMap.put(p, p); - return p; - } - } - } - - // userPointcut is the pointcut that the user wrote in the program text. - // dnfPointcut is the same pointcut rewritten in DNF - // numFormals is the number of formal parameters in the pointcut - // if numFormals > 0 then every branch of a disjunction must bind each - // formal once and only once. - // in addition, the left and right branches of a disjunction must hold on - // join point kinds in common. - private void validateBindings(Pointcut dnfPointcut, Pointcut userPointcut, int numFormals, String[] names) { - if (numFormals == 0) { - return; // nothing to check - } - if (dnfPointcut.couldMatchKinds() == Shadow.NO_SHADOW_KINDS_BITS) { - return; // cant have problems if you dont match! - } - if (dnfPointcut instanceof OrPointcut) { - OrPointcut orBasedDNFPointcut = (OrPointcut) dnfPointcut; - Pointcut[] leftBindings = new Pointcut[numFormals]; - Pointcut[] rightBindings = new Pointcut[numFormals]; - validateOrBranch(orBasedDNFPointcut, userPointcut, numFormals, names, leftBindings, rightBindings); - } else { - Pointcut[] bindings = new Pointcut[numFormals]; - validateSingleBranch(dnfPointcut, userPointcut, numFormals, names, bindings); - } - } - - private void validateOrBranch(OrPointcut pc, Pointcut userPointcut, int numFormals, String[] names, Pointcut[] leftBindings, - Pointcut[] rightBindings) { - Pointcut left = pc.getLeft(); - Pointcut right = pc.getRight(); - if (left instanceof OrPointcut) { - Pointcut[] newRightBindings = new Pointcut[numFormals]; - validateOrBranch((OrPointcut) left, userPointcut, numFormals, names, leftBindings, newRightBindings); - } else { - if (left.couldMatchKinds() != Shadow.NO_SHADOW_KINDS_BITS) { - validateSingleBranch(left, userPointcut, numFormals, names, leftBindings); - } - } - if (right instanceof OrPointcut) { - Pointcut[] newLeftBindings = new Pointcut[numFormals]; - validateOrBranch((OrPointcut) right, userPointcut, numFormals, names, newLeftBindings, rightBindings); - } else { - if (right.couldMatchKinds() != Shadow.NO_SHADOW_KINDS_BITS) { - validateSingleBranch(right, userPointcut, numFormals, names, rightBindings); - } - } - int kindsInCommon = left.couldMatchKinds() & right.couldMatchKinds(); - if (kindsInCommon != Shadow.NO_SHADOW_KINDS_BITS && couldEverMatchSameJoinPoints(left, right)) { - // we know that every branch binds every formal, so there is no - // ambiguity if each branch binds it in exactly the same way... - List ambiguousNames = new ArrayList(); - for (int i = 0; i < numFormals; i++) { - if (leftBindings[i] == null) { - if (rightBindings[i] != null) { - ambiguousNames.add(names[i]); - } - } else if (!leftBindings[i].equals(rightBindings[i])) { - ambiguousNames.add(names[i]); - } - } - if (!ambiguousNames.isEmpty()) { - raiseAmbiguityInDisjunctionError(userPointcut, ambiguousNames); - } - } - } - - // pc is a pointcut that does not contain any disjunctions - // check that every formal is bound (negation doesn't count). - // we know that numFormals > 0 or else we would not be called - private void validateSingleBranch(Pointcut pc, Pointcut userPointcut, int numFormals, String[] names, Pointcut[] bindings) { - boolean[] foundFormals = new boolean[numFormals]; - for (int i = 0; i < foundFormals.length; i++) { - foundFormals[i] = false; - } - validateSingleBranchRecursion(pc, userPointcut, foundFormals, names, bindings); - for (int i = 0; i < foundFormals.length; i++) { - if (!foundFormals[i]) { - boolean ignore = false; - // ATAJ soften the unbound error for implicit bindings like - // JoinPoint in @AJ style - for (int j = 0; j < userPointcut.m_ignoreUnboundBindingForNames.length; j++) { - if (names[i] != null && names[i].equals(userPointcut.m_ignoreUnboundBindingForNames[j])) { - ignore = true; - break; - } - } - if (!ignore) { - raiseUnboundFormalError(names[i], userPointcut); - } - } - } - } - - // each formal must appear exactly once - private void validateSingleBranchRecursion(Pointcut pc, Pointcut userPointcut, boolean[] foundFormals, String[] names, - Pointcut[] bindings) { - if (pc instanceof NotPointcut) { - // nots can only appear at leaves in DNF - NotPointcut not = (NotPointcut) pc; - if (not.getNegatedPointcut() instanceof NameBindingPointcut) { - NameBindingPointcut nnbp = (NameBindingPointcut) not.getNegatedPointcut(); - if (!nnbp.getBindingAnnotationTypePatterns().isEmpty() && !nnbp.getBindingTypePatterns().isEmpty()) { - raiseNegationBindingError(userPointcut); - } - } - } else if (pc instanceof AndPointcut) { - AndPointcut and = (AndPointcut) pc; - validateSingleBranchRecursion(and.getLeft(), userPointcut, foundFormals, names, bindings); - validateSingleBranchRecursion(and.getRight(), userPointcut, foundFormals, names, bindings); - } else if (pc instanceof NameBindingPointcut) { - List bindingTypePatterns = ((NameBindingPointcut) pc).getBindingTypePatterns(); - for (BindingTypePattern bindingTypePattern: bindingTypePatterns) { - int index = bindingTypePattern.getFormalIndex(); - bindings[index] = pc; - if (foundFormals[index]) { - raiseAmbiguousBindingError(names[index], userPointcut); - } else { - foundFormals[index] = true; - } - } - List bindingAnnotationTypePatterns = ((NameBindingPointcut) pc).getBindingAnnotationTypePatterns(); - for (BindingPattern bindingAnnotationTypePattern: bindingAnnotationTypePatterns) { - int index = bindingAnnotationTypePattern.getFormalIndex(); - bindings[index] = pc; - if (foundFormals[index]) { - raiseAmbiguousBindingError(names[index], userPointcut); - } else { - foundFormals[index] = true; - } - } - } else if (pc instanceof ConcreteCflowPointcut) { - ConcreteCflowPointcut cfp = (ConcreteCflowPointcut) pc; - int[] slots = cfp.getUsedFormalSlots(); - for (int i = 0; i < slots.length; i++) { - bindings[slots[i]] = cfp; - if (foundFormals[slots[i]]) { - raiseAmbiguousBindingError(names[slots[i]], userPointcut); - } else { - foundFormals[slots[i]] = true; - } - } - } - } - - // By returning false from this method, we are allowing binding of the same - // variable on either side of an or. - // Be conservative :- have to consider overriding, varargs, autoboxing, - // the effects of itds (on within for example), interfaces, the fact that - // join points can have multiple signatures and so on. - private boolean couldEverMatchSameJoinPoints(Pointcut left, Pointcut right) { - - if (left instanceof OrPointcut) { - OrPointcut leftOrPointcut = (OrPointcut) left; - if (couldEverMatchSameJoinPoints(leftOrPointcut.getLeft(), right)) { - return true; - } - if (couldEverMatchSameJoinPoints(leftOrPointcut.getRight(), right)) { - return true; - } - return false; - } - - if (right instanceof OrPointcut) { - OrPointcut rightOrPointcut = (OrPointcut) right; - if (couldEverMatchSameJoinPoints(left, rightOrPointcut.getLeft())) { - return true; - } - if (couldEverMatchSameJoinPoints(left, rightOrPointcut.getRight())) { - return true; - } - return false; - } - - // look for withins - WithinPointcut leftWithin = (WithinPointcut) findFirstPointcutIn(left, WithinPointcut.class); - WithinPointcut rightWithin = (WithinPointcut) findFirstPointcutIn(right, WithinPointcut.class); - if ((leftWithin != null) && (rightWithin != null)) { - if (!leftWithin.couldEverMatchSameJoinPointsAs(rightWithin)) { - return false; - } - } - // look for kinded - KindedPointcut leftKind = (KindedPointcut) findFirstPointcutIn(left, KindedPointcut.class); - KindedPointcut rightKind = (KindedPointcut) findFirstPointcutIn(right, KindedPointcut.class); - if ((leftKind != null) && (rightKind != null)) { - if (!leftKind.couldEverMatchSameJoinPointsAs(rightKind)) { - return false; - } - } - return true; - } - - private Pointcut findFirstPointcutIn(Pointcut toSearch, Class toLookFor) { - if (toSearch instanceof NotPointcut) { - return null; - } - if (toLookFor.isInstance(toSearch)) { - return toSearch; - } - if (toSearch instanceof AndPointcut) { - AndPointcut apc = (AndPointcut) toSearch; - Pointcut left = findFirstPointcutIn(apc.getLeft(), toLookFor); - if (left != null) { - return left; - } - return findFirstPointcutIn(apc.getRight(), toLookFor); - } - return null; - } - - /** - * @param userPointcut - */ - private void raiseNegationBindingError(Pointcut userPointcut) { - world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.NEGATION_DOESNT_ALLOW_BINDING), userPointcut - .getSourceContext().makeSourceLocation(userPointcut), null); - } - - private void raiseAmbiguousBindingError(String name, Pointcut pointcut) { - world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.AMBIGUOUS_BINDING, name), pointcut - .getSourceContext().makeSourceLocation(pointcut), null); - } - - /** - * @param userPointcut - */ - private void raiseAmbiguityInDisjunctionError(Pointcut userPointcut, List names) { - StringBuffer formalNames = new StringBuffer(names.get(0).toString()); - for (int i = 1; i < names.size(); i++) { - formalNames.append(", "); - formalNames.append(names.get(i)); - } - world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.AMBIGUOUS_BINDING_IN_OR, formalNames), userPointcut - .getSourceContext().makeSourceLocation(userPointcut), null); - } - - /** - * @param name - * @param userPointcut - */ - private void raiseUnboundFormalError(String name, Pointcut userPointcut) { - world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.UNBOUND_FORMAL, name), - userPointcut.getSourceLocation(), null); - } - - public void addManifest(Manifest newManifest) { - // System.out.println("? addManifest() newManifest=" + newManifest); - if (manifest == null) { - manifest = newManifest; - } - } - - public Manifest getManifest(boolean shouldCreate) { - if (manifest == null && shouldCreate) { - String WEAVER_MANIFEST_VERSION = "1.0"; - Attributes.Name CREATED_BY = new Name("Created-By"); - String WEAVER_CREATED_BY = "AspectJ Compiler"; - - manifest = new Manifest(); - - Attributes attributes = manifest.getMainAttributes(); - attributes.put(Name.MANIFEST_VERSION, WEAVER_MANIFEST_VERSION); - attributes.put(CREATED_BY, WEAVER_CREATED_BY); - } - - return manifest; - } - - // ---- weaving - - // FOR TESTING - public Collection weave(File file) throws IOException { - OutputStream os = FileUtil.makeOutputStream(file); - this.zipOutputStream = new ZipOutputStream(os); - prepareForWeave(); - Collection c = weave(new IClassFileProvider() { - - public boolean isApplyAtAspectJMungersOnly() { - return false; - } - - public Iterator getClassFileIterator() { - return addedClasses.iterator(); - } - - public IWeaveRequestor getRequestor() { - return new IWeaveRequestor() { - public void acceptResult(IUnwovenClassFile result) { - try { - writeZipEntry(result.getFilename(), result.getBytes()); - } catch (IOException ex) { - } - } - - public void processingReweavableState() { - } - - public void addingTypeMungers() { - } - - public void weavingAspects() { - } - - public void weavingClasses() { - } - - public void weaveCompleted() { - } - }; - } - }); - // /* BUG 40943*/ - // dumpResourcesToOutJar(); - zipOutputStream.close(); // this flushes and closes the acutal file - return c; - } - - private Set candidatesForRemoval = null; - - // variation of "weave" that sources class files from an external source. - public Collection weave(IClassFileProvider input) throws IOException { - if (trace.isTraceEnabled()) { - trace.enter("weave", this, input); - } - ContextToken weaveToken = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.WEAVING, ""); - Collection wovenClassNames = new ArrayList(); - IWeaveRequestor requestor = input.getRequestor(); - - if (world.getModel() != null && world.isMinimalModel()) { - candidatesForRemoval = new HashSet(); - } - if (world.getModel() != null && !isBatchWeave) { - AsmManager manager = world.getModelAsAsmManager(); - for (Iterator i = input.getClassFileIterator(); i.hasNext();) { - UnwovenClassFile classFile = i.next(); - // remove all relationships where this file being woven is - // the target of the relationship - manager.removeRelationshipsTargettingThisType(classFile.getClassName()); - } - } - - // Go through the types and ensure any 'damaged' during compile time are - // repaired prior to weaving - for (Iterator i = input.getClassFileIterator(); i.hasNext();) { - UnwovenClassFile classFile = i.next(); - if (classFile.shouldBeWoven()) { - String className = classFile.getClassName(); - ResolvedType theType = world.resolve(className); - if (theType != null) { - theType.ensureConsistent(); - } - } - } - - // special case for AtAspectJMungerOnly - see #113587 - if (input.isApplyAtAspectJMungersOnly()) { - ContextToken atAspectJMungersOnly = CompilationAndWeavingContext.enteringPhase( - CompilationAndWeavingContext.PROCESSING_ATASPECTJTYPE_MUNGERS_ONLY, ""); - requestor.weavingAspects(); - // ContextToken aspectToken = - CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.WEAVING_ASPECTS, ""); - for (Iterator i = input.getClassFileIterator(); i.hasNext();) { - UnwovenClassFile classFile = i.next(); - if (classFile.shouldBeWoven()) { - String className = classFile.getClassName(); - ResolvedType theType = world.resolve(className); - if (theType.isAnnotationStyleAspect()) { - BcelObjectType classType = BcelWorld.getBcelObjectType(theType); - if (classType == null) { - throw new BCException("Can't find bcel delegate for " + className + " type=" + theType.getClass()); - } - LazyClassGen clazz = classType.getLazyClassGen(); - BcelPerClauseAspectAdder selfMunger = new BcelPerClauseAspectAdder(theType, theType.getPerClause().getKind()); - selfMunger.forceMunge(clazz, true); - classType.finishedWith(); - UnwovenClassFile[] newClasses = getClassFilesFor(clazz); - for (int news = 0; news < newClasses.length; news++) { - requestor.acceptResult(newClasses[news]); - } - wovenClassNames.add(classFile.getClassName()); - } - } - } - requestor.weaveCompleted(); - CompilationAndWeavingContext.leavingPhase(atAspectJMungersOnly); - return wovenClassNames; - } - - requestor.processingReweavableState(); - ContextToken reweaveToken = CompilationAndWeavingContext.enteringPhase( - CompilationAndWeavingContext.PROCESSING_REWEAVABLE_STATE, ""); - prepareToProcessReweavableState(); - // clear all state from files we'll be reweaving - for (Iterator i = input.getClassFileIterator(); i.hasNext();) { - UnwovenClassFile classFile = i.next(); - if (classFile.shouldBeWoven()) { - String className = classFile.getClassName(); - BcelObjectType classType = getClassType(className); - - // null return from getClassType() means the delegate is an eclipse - // source type - so - // there *cant* be any reweavable state... (he bravely claimed...) - if (classType != null) { - ContextToken tok = CompilationAndWeavingContext.enteringPhase( - CompilationAndWeavingContext.PROCESSING_REWEAVABLE_STATE, className); - processReweavableStateIfPresent(className, classType); - CompilationAndWeavingContext.leavingPhase(tok); - } - } - } - - CompilationAndWeavingContext.leavingPhase(reweaveToken); - - ContextToken typeMungingToken = CompilationAndWeavingContext.enteringPhase( - CompilationAndWeavingContext.PROCESSING_TYPE_MUNGERS, ""); - requestor.addingTypeMungers(); - - // We process type mungers in two groups, first mungers that change the - // type - // hierarchy, then 'normal' ITD type mungers. - - // Process the types in a predictable order (rather than the order - // encountered). - // For class A, the order is superclasses of A then superinterfaces of A - // (and this mechanism is applied recursively) - List typesToProcess = new ArrayList(); - for (Iterator iter = input.getClassFileIterator(); iter.hasNext();) { - UnwovenClassFile clf = iter.next(); - if (clf.shouldBeWoven()) { - typesToProcess.add(clf.getClassName()); - } - } - while (typesToProcess.size() > 0) { - weaveParentsFor(typesToProcess, typesToProcess.get(0), null); - } - - for (Iterator i = input.getClassFileIterator(); i.hasNext();) { - UnwovenClassFile classFile = i.next(); - if (classFile.shouldBeWoven()) { - String className = classFile.getClassName(); - addNormalTypeMungers(className); - } - } - - CompilationAndWeavingContext.leavingPhase(typeMungingToken); - - requestor.weavingAspects(); - ContextToken aspectToken = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.WEAVING_ASPECTS, ""); - // first weave into aspects - for (Iterator i = input.getClassFileIterator(); i.hasNext();) { - UnwovenClassFile classFile = i.next(); - if (classFile.shouldBeWoven()) { - String className = classFile.getClassName(); - ResolvedType theType = world.resolve(className); - if (theType.isAspect()) { - BcelObjectType classType = BcelWorld.getBcelObjectType(theType); - if (classType == null) { - - // Sometimes.. if the Bcel Delegate couldn't be found then a - // problem occurred at compile time - on - // a previous compiler run. In this case I assert the - // delegate will still be an EclipseSourceType - // and we can ignore the problem here (the original compile - // error will be reported again from - // the eclipse source type) - pr113531 - ReferenceTypeDelegate theDelegate = ((ReferenceType) theType).getDelegate(); - if (theDelegate.getClass().getName().endsWith("EclipseSourceType")) { - continue; - } - - throw new BCException("Can't find bcel delegate for " + className + " type=" + theType.getClass()); - } - weaveAndNotify(classFile, classType, requestor); - wovenClassNames.add(className); - } - } - } - - CompilationAndWeavingContext.leavingPhase(aspectToken); - - requestor.weavingClasses(); - ContextToken classToken = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.WEAVING_CLASSES, ""); - // then weave into non-aspects - for (Iterator i = input.getClassFileIterator(); i.hasNext();) { - UnwovenClassFile classFile = i.next(); - if (classFile.shouldBeWoven()) { - String className = classFile.getClassName(); - ResolvedType theType = world.resolve(className); - if (!theType.isAspect()) { - BcelObjectType classType = BcelWorld.getBcelObjectType(theType); - if (classType == null) { - - // bug 119882 - see above comment for bug 113531 - ReferenceTypeDelegate theDelegate = ((ReferenceType) theType).getDelegate(); - - // TODO urgh - put a method on the interface to check this, - // string compare is hideous - if (theDelegate.getClass().getName().endsWith("EclipseSourceType")) { - continue; - } - - throw new BCException("Can't find bcel delegate for " + className + " type=" + theType.getClass()); - } - weaveAndNotify(classFile, classType, requestor); - wovenClassNames.add(className); - } - } - } - CompilationAndWeavingContext.leavingPhase(classToken); - - addedClasses.clear(); - deletedTypenames.clear(); - - requestor.weaveCompleted(); - CompilationAndWeavingContext.leavingPhase(weaveToken); - if (trace.isTraceEnabled()) { - trace.exit("weave", wovenClassNames); - } - if (world.getModel() != null && world.isMinimalModel()) { - candidatesForRemoval.clear(); - } - return wovenClassNames; - } - - public void allWeavingComplete() { - warnOnUnmatchedAdvice(); - } - - /** - * In 1.5 mode and with XLint:adviceDidNotMatch enabled, put out messages for any mungers that did not match anything. - */ - private void warnOnUnmatchedAdvice() { - - class AdviceLocation { - private final int lineNo; - private final UnresolvedType inAspect; - - public AdviceLocation(BcelAdvice advice) { - this.lineNo = advice.getSourceLocation().getLine(); - this.inAspect = advice.getDeclaringAspect(); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof AdviceLocation)) { - return false; - } - AdviceLocation other = (AdviceLocation) obj; - if (this.lineNo != other.lineNo) { - return false; - } - if (!this.inAspect.equals(other.inAspect)) { - return false; - } - return true; - } - - @Override - public int hashCode() { - return 37 + 17 * lineNo + 17 * inAspect.hashCode(); - } - } - - // FIXME asc Should be factored out into Xlint code and done - // automatically for all xlint messages, ideally. - // if a piece of advice hasn't matched anywhere and we are in -1.5 mode, - // put out a warning - if (world.isInJava5Mode() && world.getLint().adviceDidNotMatch.isEnabled()) { - List l = world.getCrosscuttingMembersSet().getShadowMungers(); - Set alreadyWarnedLocations = new HashSet(); - - for (Iterator iter = l.iterator(); iter.hasNext();) { - ShadowMunger element = (ShadowMunger) iter.next(); - // This will stop us incorrectly reporting deow checkers: - if (element instanceof BcelAdvice) { - BcelAdvice ba = (BcelAdvice) element; - if (ba.getKind() == AdviceKind.CflowEntry || ba.getKind() == AdviceKind.CflowBelowEntry) { - continue; - } - if (!ba.hasMatchedSomething()) { - // Because we implement some features of AJ itself by - // creating our own kind of mungers, you sometimes - // find that ba.getSignature() is not a BcelMethod - for - // example it might be a cflow entry munger. - if (ba.getSignature() != null) { - // check we haven't already warned on this advice and line - // (cflow creates multiple mungers for the same advice) - AdviceLocation loc = new AdviceLocation(ba); - if (alreadyWarnedLocations.contains(loc)) { - continue; - } else { - alreadyWarnedLocations.add(loc); - } - - if (!(ba.getSignature() instanceof BcelMethod) - || !Utility.isSuppressing(ba.getSignature(), "adviceDidNotMatch")) { - world.getLint().adviceDidNotMatch.signal(ba.getDeclaringAspect().toString(), new SourceLocation( - element.getSourceLocation().getSourceFile(), element.getSourceLocation().getLine())); - } - } - } - } - } - } - } - - /** - * 'typeToWeave' is one from the 'typesForWeaving' list. This routine ensures we process supertypes (classes/interfaces) of - * 'typeToWeave' that are in the 'typesForWeaving' list before 'typeToWeave' itself. 'typesToWeave' is then removed from the - * 'typesForWeaving' list. - * - * Note: Future gotcha in here ... when supplying partial hierarchies, this algorithm may break down. If you have a hierarchy - * A>B>C and only give A and C to the weaver, it may choose to weave them in either order - but you'll probably have other - * problems if you are supplying partial hierarchies like that ! - */ - private void weaveParentsFor(List typesForWeaving, String typeToWeave, ResolvedType resolvedTypeToWeave) { - if (resolvedTypeToWeave == null) { - // resolve it if the caller could not pass in the resolved type - resolvedTypeToWeave = world.resolve(typeToWeave); - } - ResolvedType superclassType = resolvedTypeToWeave.getSuperclass(); - String superclassTypename = (superclassType == null ? null : superclassType.getName()); - - // PR336654 added the 'typesForWeaving.contains(superclassTypename)' clause. - // Without it we can delete all type mungers on the parents and yet we only - // add back in the declare parents related ones, not the regular ITDs. - if (superclassType != null && !superclassType.isTypeHierarchyComplete() && superclassType.isExposedToWeaver() - && typesForWeaving.contains(superclassTypename)) { - weaveParentsFor(typesForWeaving, superclassTypename, superclassType); - } - - ResolvedType[] interfaceTypes = resolvedTypeToWeave.getDeclaredInterfaces(); - for (ResolvedType resolvedSuperInterface : interfaceTypes) { - if (!resolvedSuperInterface.isTypeHierarchyComplete()) { - String interfaceTypename = resolvedSuperInterface.getName(); - if (resolvedSuperInterface.isExposedToWeaver()) { // typesForWeaving.contains(interfaceTypename)) { - weaveParentsFor(typesForWeaving, interfaceTypename, resolvedSuperInterface); - } - } - } - ContextToken tok = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.PROCESSING_DECLARE_PARENTS, - resolvedTypeToWeave.getName()); - // If A was processed before B (and was declared 'class A implements B') then there is no need to complete B again, it - // will have been done whilst processing A. - if (!resolvedTypeToWeave.isTypeHierarchyComplete()) { - weaveParentTypeMungers(resolvedTypeToWeave); - } - CompilationAndWeavingContext.leavingPhase(tok); - typesForWeaving.remove(typeToWeave); - resolvedTypeToWeave.tagAsTypeHierarchyComplete(); - } - - public void prepareToProcessReweavableState() { - } - - public void processReweavableStateIfPresent(String className, BcelObjectType classType) { - // If the class is marked reweavable, check any aspects around when it - // was built are in this world - WeaverStateInfo wsi = classType.getWeaverState(); - // System.out.println(">> processReweavableStateIfPresent " + className + " wsi=" + wsi); - if (wsi != null && wsi.isReweavable()) { // Check all necessary types - // are around! - world.showMessage(IMessage.INFO, WeaverMessages.format(WeaverMessages.PROCESSING_REWEAVABLE, className, classType - .getSourceLocation().getSourceFile()), null, null); - Set aspectsPreviouslyInWorld = wsi.getAspectsAffectingType(); - // keep track of them just to ensure unique missing aspect error - // reporting - Set alreadyConfirmedReweavableState = new HashSet(); - for (String requiredTypeSignature : aspectsPreviouslyInWorld) { - // for (Iterator iter = aspectsPreviouslyInWorld.iterator(); iter.hasNext();) { - // String requiredTypeName = (String) iter.next(); - if (!alreadyConfirmedReweavableState.contains(requiredTypeSignature)) { - ResolvedType rtx = world.resolve(UnresolvedType.forSignature(requiredTypeSignature), true); - boolean exists = !rtx.isMissing(); - if (!world.isOverWeaving()) { - if (!exists) { - world.getLint().missingAspectForReweaving.signal(new String[] { rtx.getName(), className }, - classType.getSourceLocation(), null); - // world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.MISSING_REWEAVABLE_TYPE, - // requiredTypeName, className), classType.getSourceLocation(), null); - } else { - // weaved in aspect that are not declared in aop.xml - // trigger an error for now - // may cause headhache for LTW and packaged lib - // without aop.xml in - // see #104218 - if (!xcutSet.containsAspect(rtx)) { - world.showMessage(IMessage.ERROR, WeaverMessages.format( - WeaverMessages.REWEAVABLE_ASPECT_NOT_REGISTERED, rtx.getName(), className), null, null); - } else if (!world.getMessageHandler().isIgnoring(IMessage.INFO)) { - world.showMessage(IMessage.INFO, WeaverMessages.format(WeaverMessages.VERIFIED_REWEAVABLE_TYPE, - rtx.getName(), rtx.getSourceLocation().getSourceFile()), null, null); - } - alreadyConfirmedReweavableState.add(requiredTypeSignature); - } - } - } - } - // old: - // classType.setJavaClass(Utility.makeJavaClass(classType.getJavaClass - // ().getFileName(), wsi.getUnwovenClassFileData())); - // new: reweavable default with clever diff - if (!world.isOverWeaving()) { - byte[] ucfd = wsi.getUnwovenClassFileData(); - if (ucfd.length == 0) { - // Size 0 indicates the class was previously overwoven, so you need to be overweaving now! - world.getMessageHandler().handleMessage( - MessageUtil.error( - WeaverMessages.format(WeaverMessages.MUST_KEEP_OVERWEAVING_ONCE_START, - className))); -// onType.getName(), annoX.getTypeName(), annoX.getValidTargets()), -// decA.getSourceLocation())); - } else { - byte[] bytes = wsi.getUnwovenClassFileData(classType.getJavaClass().getBytes()); - JavaClass newJavaClass = Utility.makeJavaClass(classType.getJavaClass().getFileName(), bytes); - classType.setJavaClass(newJavaClass, true); - classType.getResolvedTypeX().ensureConsistent(); - } - } - // } else { - // classType.resetState(); - } - } - - private void weaveAndNotify(UnwovenClassFile classFile, BcelObjectType classType, IWeaveRequestor requestor) throws IOException { - trace.enter("weaveAndNotify", this, new Object[] { classFile, classType, requestor }); - - ContextToken tok = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.WEAVING_TYPE, classType - .getResolvedTypeX().getName()); - LazyClassGen clazz = weaveWithoutDump(classFile, classType); - classType.finishedWith(); - // clazz is null if the classfile was unchanged by weaving... - if (clazz != null) { - UnwovenClassFile[] newClasses = getClassFilesFor(clazz); - // OPTIMIZE can we avoid using the string name at all in - // UnwovenClassFile instances? - // Copy the char[] across as it means the - // WeaverAdapter.removeFromMap() can be fast! - if (newClasses[0].getClassName().equals(classFile.getClassName())) { - newClasses[0].setClassNameAsChars(classFile.getClassNameAsChars()); - } - for (int i = 0; i < newClasses.length; i++) { - requestor.acceptResult(newClasses[i]); - } - } else { - requestor.acceptResult(classFile); - } - classType.weavingCompleted(); - CompilationAndWeavingContext.leavingPhase(tok); - - trace.exit("weaveAndNotify"); - } - - /** - * helper method - will return NULL if the underlying delegate is an EclipseSourceType and not a BcelObjectType - */ - public BcelObjectType getClassType(String forClass) { - return BcelWorld.getBcelObjectType(world.resolve(forClass)); - } - - public void addParentTypeMungers(String typeName) { - weaveParentTypeMungers(world.resolve(typeName)); - } - - public void addNormalTypeMungers(String typeName) { - weaveNormalTypeMungers(world.resolve(typeName)); - } - - public UnwovenClassFile[] getClassFilesFor(LazyClassGen clazz) { - List childClasses = clazz.getChildClasses(world); - UnwovenClassFile[] ret = new UnwovenClassFile[1 + childClasses.size()]; - ret[0] = new UnwovenClassFile(clazz.getFileName(), clazz.getClassName(), clazz.getJavaClassBytesIncludingReweavable(world)); - int index = 1; - for (Iterator iter = childClasses.iterator(); iter.hasNext();) { - UnwovenClassFile.ChildClass element = iter.next(); - UnwovenClassFile childClass = new UnwovenClassFile(clazz.getFileName() + "$" + element.name, element.bytes); - ret[index++] = childClass; - } - return ret; - } - - /** - * Weaves new parents and annotations onto a type ("declare parents" and "declare @type") - * - * Algorithm: 1. First pass, do parents then do annotations. During this pass record: - any parent mungers that don't match but - * have a non-wild annotation type pattern - any annotation mungers that don't match 2. Multiple subsequent passes which go over - * the munger lists constructed in the first pass, repeatedly applying them until nothing changes. FIXME asc confirm that - * algorithm is optimal ?? - */ - public void weaveParentTypeMungers(ResolvedType onType) { - if (onType.isRawType() || onType.isParameterizedType()) { - onType = onType.getGenericType(); - } - onType.clearInterTypeMungers(); - - List decpToRepeat = new ArrayList(); - - boolean aParentChangeOccurred = false; - boolean anAnnotationChangeOccurred = false; - // First pass - apply all decp mungers - for (DeclareParents decp : declareParentsList) { - boolean typeChanged = applyDeclareParents(decp, onType); - if (typeChanged) { - aParentChangeOccurred = true; - } else { - decpToRepeat.add(decp); - } - } - - // Still first pass - apply all dec @type mungers - for (DeclareAnnotation decA : xcutSet.getDeclareAnnotationOnTypes()) { - boolean typeChanged = applyDeclareAtType(decA, onType, true); - if (typeChanged) { - anAnnotationChangeOccurred = true; - } - } - - while ((aParentChangeOccurred || anAnnotationChangeOccurred) && !decpToRepeat.isEmpty()) { - anAnnotationChangeOccurred = aParentChangeOccurred = false; - List decpToRepeatNextTime = new ArrayList(); - for (Iterator iter = decpToRepeat.iterator(); iter.hasNext();) { - DeclareParents decp = iter.next(); - boolean typeChanged = applyDeclareParents(decp, onType); - if (typeChanged) { - aParentChangeOccurred = true; - } else { - decpToRepeatNextTime.add(decp); - } - } - - for (DeclareAnnotation decA : xcutSet.getDeclareAnnotationOnTypes()) { - boolean typeChanged = applyDeclareAtType(decA, onType, false); - if (typeChanged) { - anAnnotationChangeOccurred = true; - } - } - decpToRepeat = decpToRepeatNextTime; - } - } - - /** - * Apply a declare @type - return true if we change the type - */ - private boolean applyDeclareAtType(DeclareAnnotation decA, ResolvedType onType, boolean reportProblems) { - boolean didSomething = false; - if (decA.matches(onType)) { - AnnotationAJ theAnnotation = decA.getAnnotation(); - // can be null for broken code! - if (theAnnotation == null) { - return false; - } - if (onType.hasAnnotation(theAnnotation.getType())) { - // Could put out a lint here for an already annotated type ... - // if (reportProblems) { - // world.getLint().elementAlreadyAnnotated.signal( - // new - // String[]{onType.toString(),decA.getAnnotationTypeX().toString - // ()}, - // onType.getSourceLocation(),new - // ISourceLocation[]{decA.getSourceLocation()}); - // } - return false; - } - - AnnotationAJ annoX = decA.getAnnotation(); - - // check the annotation is suitable for the target - boolean problemReported = verifyTargetIsOK(decA, onType, annoX, reportProblems); - - if (!problemReported) { - AsmRelationshipProvider.addDeclareAnnotationRelationship(world.getModelAsAsmManager(), decA.getSourceLocation(), - onType.getSourceLocation(), false); - // TAG: WeavingMessage - if (!getWorld().getMessageHandler().isIgnoring(IMessage.WEAVEINFO)) { - getWorld().getMessageHandler().handleMessage( - WeaveMessage.constructWeavingMessage( - WeaveMessage.WEAVEMESSAGE_ANNOTATES, - new String[] { onType.toString(), Utility.beautifyLocation(onType.getSourceLocation()), - decA.getAnnotationString(), "type", decA.getAspect().toString(), - Utility.beautifyLocation(decA.getSourceLocation()) })); - } - didSomething = true; - ResolvedTypeMunger newAnnotationTM = new AnnotationOnTypeMunger(annoX); - newAnnotationTM.setSourceLocation(decA.getSourceLocation()); - onType.addInterTypeMunger(new BcelTypeMunger(newAnnotationTM, decA.getAspect().resolve(world)), false); - decA.copyAnnotationTo(onType); - } - } - return didSomething; - } - - /** - * Checks for an @target() on the annotation and if found ensures it allows the annotation to be attached to the target type - * that matched. - */ - private boolean verifyTargetIsOK(DeclareAnnotation decA, ResolvedType onType, AnnotationAJ annoX, boolean outputProblems) { - boolean problemReported = false; - if (annoX.specifiesTarget()) { - if ((onType.isAnnotation() && !annoX.allowedOnAnnotationType()) || (!annoX.allowedOnRegularType())) { - if (outputProblems) { - if (decA.isExactPattern()) { - world.getMessageHandler().handleMessage( - MessageUtil.error( - WeaverMessages.format(WeaverMessages.INCORRECT_TARGET_FOR_DECLARE_ANNOTATION, - onType.getName(), annoX.getTypeName(), annoX.getValidTargets()), - decA.getSourceLocation())); - } else { - if (world.getLint().invalidTargetForAnnotation.isEnabled()) { - world.getLint().invalidTargetForAnnotation.signal(new String[] { onType.getName(), annoX.getTypeName(), - annoX.getValidTargets() }, decA.getSourceLocation(), - new ISourceLocation[] { onType.getSourceLocation() }); - } - } - } - problemReported = true; - } - } - return problemReported; - } - - /** - * Apply a single declare parents - return true if we change the type - */ - private boolean applyDeclareParents(DeclareParents p, ResolvedType onType) { - boolean didSomething = false; - List newParents = p.findMatchingNewParents(onType, true); - if (!newParents.isEmpty()) { - didSomething = true; - BcelWorld.getBcelObjectType(onType); - // System.err.println("need to do declare parents for: " + onType); - for (ResolvedType newParent : newParents) { - // We set it here so that the imminent matching for ITDs can - // succeed - we still haven't done the necessary changes to the class file - // itself (like transform super calls) - that is done in - // BcelTypeMunger.mungeNewParent() - // classType.addParent(newParent); - onType.addParent(newParent); - NewParentTypeMunger newParentMunger = new NewParentTypeMunger(newParent, p.getDeclaringType()); - if (p.isMixin()) { - newParentMunger.setIsMixin(true); - } - newParentMunger.setSourceLocation(p.getSourceLocation()); - onType.addInterTypeMunger(new BcelTypeMunger(newParentMunger, xcutSet.findAspectDeclaringParents(p)), false); - } - } - return didSomething; - } - - public void weaveNormalTypeMungers(ResolvedType onType) { - ContextToken tok = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.PROCESSING_TYPE_MUNGERS, - onType.getName()); - if (onType.isRawType() || onType.isParameterizedType()) { - onType = onType.getGenericType(); - } - for (ConcreteTypeMunger m : typeMungerList) { - if (!m.isLateMunger() && m.matches(onType)) { - onType.addInterTypeMunger(m, false); - } - } - CompilationAndWeavingContext.leavingPhase(tok); - } - - // exposed for ClassLoader dynamic weaving - public LazyClassGen weaveWithoutDump(UnwovenClassFile classFile, BcelObjectType classType) throws IOException { - return weave(classFile, classType, false); - } - - // FOR TESTING - LazyClassGen weave(UnwovenClassFile classFile, BcelObjectType classType) throws IOException { - LazyClassGen ret = weave(classFile, classType, true); - return ret; - } - - private LazyClassGen weave(UnwovenClassFile classFile, BcelObjectType classType, boolean dump) throws IOException { - - try { - if (classType.isSynthetic()) { // Don't touch synthetic classes - if (dump) { - dumpUnchanged(classFile); - } - return null; - } - ReferenceType resolvedClassType = classType.getResolvedTypeX(); - - if (world.isXmlConfigured() && world.getXmlConfiguration().excludesType(resolvedClassType)) { - if (!world.getMessageHandler().isIgnoring(IMessage.INFO)) { - world.getMessageHandler().handleMessage( - MessageUtil.info("Type '" + resolvedClassType.getName() - + "' not woven due to exclusion via XML weaver exclude section")); - - } - if (dump) { - dumpUnchanged(classFile); - } - return null; - } - - List shadowMungers = fastMatch(shadowMungerList, resolvedClassType); - List typeMungers = classType.getResolvedTypeX().getInterTypeMungers(); - - resolvedClassType.checkInterTypeMungers(); - - // Decide if we need to do actual weaving for this class - boolean mightNeedToWeave = shadowMungers.size() > 0 || typeMungers.size() > 0 || classType.isAspect() - || world.getDeclareAnnotationOnMethods().size() > 0 || world.getDeclareAnnotationOnFields().size() > 0; - - // May need bridge methods if on 1.5 and something in our hierarchy is - // affected by ITDs - boolean mightNeedBridgeMethods = world.isInJava5Mode() && !classType.isInterface() - && resolvedClassType.getInterTypeMungersIncludingSupers().size() > 0; - - LazyClassGen clazz = null; - if (mightNeedToWeave || mightNeedBridgeMethods) { - clazz = classType.getLazyClassGen(); - // System.err.println("got lazy gen: " + clazz + ", " + - // clazz.getWeaverState()); - try { - boolean isChanged = false; - - if (mightNeedToWeave) { - isChanged = BcelClassWeaver.weave(world, clazz, shadowMungers, typeMungers, lateTypeMungerList, - inReweavableMode); - } - - checkDeclareTypeErrorOrWarning(world, classType); - - if (mightNeedBridgeMethods) { - isChanged = BcelClassWeaver.calculateAnyRequiredBridgeMethods(world, clazz) || isChanged; - } - - if (isChanged) { - if (dump) { - dump(classFile, clazz); - } - return clazz; - } - } catch (RuntimeException re) { - String classDebugInfo = null; - try { - classDebugInfo = clazz.toLongString(); - } catch (Throwable e) { - new RuntimeException("Crashed whilst crashing with this exception: " + e, e).printStackTrace(); - // recover from crash whilst producing debug string - classDebugInfo = clazz.getClassName(); - } - String messageText = "trouble in: \n" + classDebugInfo; - getWorld().getMessageHandler().handleMessage(new Message(messageText, IMessage.ABORT, re, null)); - } catch (Error re) { - String classDebugInfo = null; - try { - classDebugInfo = clazz.toLongString(); - } catch (OutOfMemoryError oome) { - System.err.println("Ran out of memory creating debug info for an error"); - re.printStackTrace(System.err); - // recover from crash whilst producing debug string - classDebugInfo = clazz.getClassName(); - } catch (Throwable e) { - // recover from crash whilst producing debug string - classDebugInfo = clazz.getClassName(); - } - String messageText = "trouble in: \n" + classDebugInfo; - getWorld().getMessageHandler().handleMessage(new Message(messageText, IMessage.ABORT, re, null)); - } - } else { - checkDeclareTypeErrorOrWarning(world, classType); - } - // this is very odd return behavior trying to keep everyone happy - - // can we remove it from the model now? we know it contains no relationship endpoints... - AsmManager model = world.getModelAsAsmManager(); - if (world.isMinimalModel() && model != null && !classType.isAspect()) { - AspectJElementHierarchy hierarchy = (AspectJElementHierarchy) model.getHierarchy(); - String pkgname = classType.getResolvedTypeX().getPackageName(); - String tname = classType.getResolvedTypeX().getSimpleBaseName(); - IProgramElement typeElement = hierarchy.findElementForType(pkgname, tname); - if (typeElement != null && hasInnerType(typeElement)) { - // Cannot remove it right now (has inner type), schedule it - // for possible deletion later if all inner types are - // removed - candidatesForRemoval.add(typeElement); - } - if (typeElement != null && !hasInnerType(typeElement)) { - IProgramElement parent = typeElement.getParent(); - // parent may have children: PACKAGE DECL, IMPORT-REFERENCE, TYPE_DECL - if (parent != null) { - // if it was the only type we should probably remove - // the others too. - parent.removeChild(typeElement); - if (parent.getKind().isSourceFile()) { - removeSourceFileIfNoMoreTypeDeclarationsInside(hierarchy, typeElement, parent); - } else { - hierarchy.forget(null, typeElement); - // At this point, the child has been removed. We - // should now check if the parent is in our - // 'candidatesForRemoval' set. If it is then that - // means we were going to remove it but it had a - // child. Now we can check if it still has a child - - // if it doesn't it can also be removed! - - walkUpRemovingEmptyTypesAndPossiblyEmptySourceFile(hierarchy, tname, parent); - } - - } - } - } - - if (dump) { - dumpUnchanged(classFile); - return clazz; - } else { - // ATAJ: the class was not weaved, but since it gets there early it - // may have new generated inner classes - // attached to it to support LTW perX aspectOf support (see - // BcelPerClauseAspectAdder) - // that aggressively defines the inner $mayHaveAspect - // interface. - if (clazz != null && !clazz.getChildClasses(world).isEmpty()) { - return clazz; - } - return null; - } - } finally { - world.demote(); - } - } - - private void walkUpRemovingEmptyTypesAndPossiblyEmptySourceFile(AspectJElementHierarchy hierarchy, String tname, - IProgramElement typeThatHasChildRemoved) { - // typeThatHasChildRemoved might be a source file, type or a method/ctor - // - for a method/ctor find the type/sourcefile - while (typeThatHasChildRemoved != null - && !(typeThatHasChildRemoved.getKind().isType() || typeThatHasChildRemoved.getKind().isSourceFile())) { - // this will take us 'up' through methods that contain anonymous - // inner classes - typeThatHasChildRemoved = typeThatHasChildRemoved.getParent(); - } - // now typeThatHasChildRemoved points to the type or sourcefile that has - // had something removed - if (candidatesForRemoval.contains(typeThatHasChildRemoved) && !hasInnerType(typeThatHasChildRemoved)) { - // now we can get rid of it - IProgramElement parent = typeThatHasChildRemoved.getParent(); - if (parent != null) { - parent.removeChild(typeThatHasChildRemoved); - candidatesForRemoval.remove(typeThatHasChildRemoved); - if (parent.getKind().isSourceFile()) { - removeSourceFileIfNoMoreTypeDeclarationsInside(hierarchy, typeThatHasChildRemoved, parent); - // System.out.println("Removed on second pass: " + - // typeThatHasChildRemoved.getName()); - } else { - // System.out.println("On later pass, parent of type " + - // typeThatHasChildRemoved.getName() - // + " was found not to be a sourcefile, recursing up..."); - walkUpRemovingEmptyTypesAndPossiblyEmptySourceFile(hierarchy, tname, parent); - } - } - } - } - - private void removeSourceFileIfNoMoreTypeDeclarationsInside(AspectJElementHierarchy hierarchy, IProgramElement typeElement, - IProgramElement sourceFileNode) { - IProgramElement compilationUnit = sourceFileNode; - boolean anyOtherTypeDeclarations = false; - for (IProgramElement child : compilationUnit.getChildren()) { - IProgramElement.Kind k = child.getKind(); - if (k.isType()) { - anyOtherTypeDeclarations = true; - break; - } - } - // If the compilation unit node contained no - // other types, there is no need to keep it - if (!anyOtherTypeDeclarations) { - IProgramElement cuParent = compilationUnit.getParent(); - if (cuParent != null) { - compilationUnit.setParent(null); - cuParent.removeChild(compilationUnit); - } - // need to update some caches and structures too? - hierarchy.forget(sourceFileNode, typeElement); - } else { - hierarchy.forget(null, typeElement); - } - } - - // ---- writing - - // TODO could be smarter - really only matters if inner type has been woven, but there is a chance we haven't woven it *yet* - private boolean hasInnerType(IProgramElement typeNode) { - for (IProgramElement child : typeNode.getChildren()) { - IProgramElement.Kind kind = child.getKind(); - if (kind.isType()) { - return true; - } - // if (kind == IProgramElement.Kind.ASPECT) { - // return true; - // } - if (kind.isType() || kind == IProgramElement.Kind.METHOD || kind == IProgramElement.Kind.CONSTRUCTOR) { - boolean b = hasInnerType(child); - if (b) { - return b; - } - } - } - return false; - } - - private void checkDeclareTypeErrorOrWarning(BcelWorld world2, BcelObjectType classType) { - List dteows = world.getDeclareTypeEows(); - for (DeclareTypeErrorOrWarning dteow : dteows) { - if (dteow.getTypePattern().matchesStatically(classType.getResolvedTypeX())) { - if (dteow.isError()) { - world.getMessageHandler().handleMessage( - MessageUtil.error(dteow.getMessage(), classType.getResolvedTypeX().getSourceLocation())); - } else { - world.getMessageHandler().handleMessage( - MessageUtil.warn(dteow.getMessage(), classType.getResolvedTypeX().getSourceLocation())); - } - } - } - } - - private void dumpUnchanged(UnwovenClassFile classFile) throws IOException { - if (zipOutputStream != null) { - writeZipEntry(getEntryName(classFile.getJavaClass().getClassName()), classFile.getBytes()); - } else { - classFile.writeUnchangedBytes(); - } - } - - private String getEntryName(String className) { - // XXX what does bcel's getClassName do for inner names - return className.replace('.', '/') + ".class"; - } - - private void dump(UnwovenClassFile classFile, LazyClassGen clazz) throws IOException { - if (zipOutputStream != null) { - String mainClassName = classFile.getJavaClass().getClassName(); - writeZipEntry(getEntryName(mainClassName), clazz.getJavaClass(world).getBytes()); - List childClasses = clazz.getChildClasses(world); - if (!childClasses.isEmpty()) { - for (Iterator i = childClasses.iterator(); i.hasNext();) { - UnwovenClassFile.ChildClass c = i.next(); - writeZipEntry(getEntryName(mainClassName + "$" + c.name), c.bytes); - } - } - } else { - classFile.writeWovenBytes(clazz.getJavaClass(world).getBytes(), clazz.getChildClasses(world)); - } - } - - private void writeZipEntry(String name, byte[] bytes) throws IOException { - ZipEntry newEntry = new ZipEntry(name); // ??? get compression scheme - // right - - zipOutputStream.putNextEntry(newEntry); - zipOutputStream.write(bytes); - zipOutputStream.closeEntry(); - } - - /** - * Perform a fast match of the specified list of shadowmungers against the specified type. A subset of those that might match is - * returned. - * - * @param list list of all shadow mungers that might match - * @param type the target type - * @return a list of shadow mungers that might match with those that cannot (according to fast match rules) removed - */ - private List fastMatch(List list, ResolvedType type) { - if (list == null) { - return Collections.emptyList(); - } - boolean isOverweaving = world.isOverWeaving(); - WeaverStateInfo typeWeaverState = (isOverweaving ? type.getWeaverState() : null); - - // here we do the coarsest grained fast match with no kind constraints - // this will remove all obvious non-matches and see if we need to do any - // weaving - FastMatchInfo info = new FastMatchInfo(type, null, world); - - List result = new ArrayList(); - - if (world.areInfoMessagesEnabled() && world.isTimingEnabled()) { - for (ShadowMunger munger : list) { - if (typeWeaverState != null) { // will only be null if overweaving is ON and there is weaverstate - ResolvedType declaringAspect = munger.getDeclaringType(); - if (typeWeaverState.isAspectAlreadyApplied(declaringAspect)) { - continue; - } - } - Pointcut pointcut = munger.getPointcut(); - long starttime = System.nanoTime(); - FuzzyBoolean fb = pointcut.fastMatch(info); - long endtime = System.nanoTime(); - world.recordFastMatch(pointcut, endtime - starttime); - if (fb.maybeTrue()) { - result.add(munger); - } - } - } else { - for (ShadowMunger munger : list) { - if (typeWeaverState != null) { // will only be null if overweaving is ON and there is weaverstate - ResolvedType declaringAspect = munger.getConcreteAspect();// getDeclaringType(); - if (typeWeaverState.isAspectAlreadyApplied(declaringAspect)) { - continue; - } - } - Pointcut pointcut = munger.getPointcut(); - FuzzyBoolean fb = pointcut.fastMatch(info); - if (fb.maybeTrue()) { - result.add(munger); - } - } - } - return result; - } - - public void setReweavableMode(boolean xNotReweavable) { - inReweavableMode = !xNotReweavable; - WeaverStateInfo.setReweavableModeDefaults(!xNotReweavable, false, true); - } - - public boolean isReweavable() { - return inReweavableMode; - } - - public World getWorld() { - return world; - } - - public void tidyUp() { - if (trace.isTraceEnabled()) { - trace.enter("tidyUp", this); - } - shadowMungerList = null; // setup by prepareForWeave - typeMungerList = null; // setup by prepareForWeave - lateTypeMungerList = null; // setup by prepareForWeave - declareParentsList = null; // setup by prepareForWeave - if (trace.isTraceEnabled()) { - trace.exit("tidyUp"); - } - } - - public void write(CompressingDataOutputStream dos) throws IOException { - xcutSet.write(dos); - } - - // only called for testing - public void setShadowMungers(List shadowMungers) { - shadowMungerList = shadowMungers; - } -} diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelWeavingSupport.java b/weaver/src/org/aspectj/weaver/bcel/BcelWeavingSupport.java deleted file mode 100644 index 120df80c2..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/BcelWeavingSupport.java +++ /dev/null @@ -1,71 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2008 Contributors - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Andy Clement initial implementation - * ******************************************************************/ -package org.aspectj.weaver.bcel; - -import org.aspectj.weaver.Advice; -import org.aspectj.weaver.AjAttribute; -import org.aspectj.weaver.ConcreteTypeMunger; -import org.aspectj.weaver.IWeavingSupport; -import org.aspectj.weaver.Member; -import org.aspectj.weaver.ResolvedMember; -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.ResolvedTypeMunger; -import org.aspectj.weaver.ast.Var; -import org.aspectj.weaver.patterns.PerClause; -import org.aspectj.weaver.patterns.Pointcut; - -/** - * Bcel implementation of the weaving support required in a BcelWorld which will actually modify bytecode. - * - * @author Andy Clement - */ -public class BcelWeavingSupport implements IWeavingSupport { - - public Advice createAdviceMunger(AjAttribute.AdviceAttribute attribute, Pointcut pointcut, Member signature, - ResolvedType concreteAspect) { - // System.err.println("concrete advice: " + signature + " context " + - // sourceContext); - return new BcelAdvice(attribute, pointcut, signature, concreteAspect); - } - - public ConcreteTypeMunger makeCflowStackFieldAdder(ResolvedMember cflowField) { - return new BcelCflowStackFieldAdder(cflowField); - } - - public ConcreteTypeMunger makeCflowCounterFieldAdder(ResolvedMember cflowField) { - return new BcelCflowCounterFieldAdder(cflowField); - } - - /** - * Register a munger for perclause @AJ aspect so that we add aspectOf(..) to them as needed - * - * @param aspect - * @param kind - * @return munger - */ - public ConcreteTypeMunger makePerClauseAspect(ResolvedType aspect, PerClause.Kind kind) { - return new BcelPerClauseAspectAdder(aspect, kind); - } - - public Var makeCflowAccessVar(ResolvedType formalType, Member cflowField, int arrayIndex) { - return new BcelCflowAccessVar(formalType, cflowField, arrayIndex); - } - - public ConcreteTypeMunger concreteTypeMunger(ResolvedTypeMunger munger, ResolvedType aspectType) { - return new BcelTypeMunger(munger, aspectType); - } - - public ConcreteTypeMunger createAccessForInlineMunger(ResolvedType aspect) { - return new BcelAccessForInlineMunger(aspect); - } - -} diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelWorld.java b/weaver/src/org/aspectj/weaver/bcel/BcelWorld.java deleted file mode 100644 index 4ade1e125..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/BcelWorld.java +++ /dev/null @@ -1,1329 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * Alexandre Vasseur perClause support for @AJ aspects - * ******************************************************************/ - -package org.aspectj.weaver.bcel; - -import java.io.File; -import java.io.IOException; -import java.lang.reflect.Modifier; -import java.net.MalformedURLException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.StringTokenizer; - -import org.aspectj.apache.bcel.Constants; -import org.aspectj.apache.bcel.classfile.ClassParser; -import org.aspectj.apache.bcel.classfile.ConstantPool; -import org.aspectj.apache.bcel.classfile.JavaClass; -import org.aspectj.apache.bcel.generic.FieldInstruction; -import org.aspectj.apache.bcel.generic.INVOKEINTERFACE; -import org.aspectj.apache.bcel.generic.Instruction; -import org.aspectj.apache.bcel.generic.InstructionHandle; -import org.aspectj.apache.bcel.generic.InvokeInstruction; -import org.aspectj.apache.bcel.generic.MULTIANEWARRAY; -import org.aspectj.apache.bcel.generic.ObjectType; -import org.aspectj.apache.bcel.generic.Type; -import org.aspectj.apache.bcel.util.ClassLoaderReference; -import org.aspectj.apache.bcel.util.ClassLoaderRepository; -import org.aspectj.apache.bcel.util.ClassPath; -import org.aspectj.apache.bcel.util.NonCachingClassLoaderRepository; -import org.aspectj.apache.bcel.util.Repository; -import org.aspectj.asm.AsmManager; -import org.aspectj.asm.IRelationship; -import org.aspectj.asm.internal.CharOperation; -import org.aspectj.bridge.IMessage; -import org.aspectj.bridge.IMessageHandler; -import org.aspectj.bridge.ISourceLocation; -import org.aspectj.bridge.Message; -import org.aspectj.bridge.MessageUtil; -import org.aspectj.bridge.WeaveMessage; -import org.aspectj.weaver.Advice; -import org.aspectj.weaver.AdviceKind; -import org.aspectj.weaver.AnnotationAJ; -import org.aspectj.weaver.AnnotationOnTypeMunger; -import org.aspectj.weaver.BCException; -import org.aspectj.weaver.Checker; -import org.aspectj.weaver.ICrossReferenceHandler; -import org.aspectj.weaver.IWeavingSupport; -import org.aspectj.weaver.Member; -import org.aspectj.weaver.MemberImpl; -import org.aspectj.weaver.MemberKind; -import org.aspectj.weaver.NewParentTypeMunger; -import org.aspectj.weaver.ReferenceType; -import org.aspectj.weaver.ReferenceTypeDelegate; -import org.aspectj.weaver.ResolvedMember; -import org.aspectj.weaver.ResolvedMemberImpl; -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.ResolvedTypeMunger; -import org.aspectj.weaver.Shadow; -import org.aspectj.weaver.ShadowMunger; -import org.aspectj.weaver.UnresolvedType; -import org.aspectj.weaver.World; -import org.aspectj.weaver.loadtime.definition.Definition; -import org.aspectj.weaver.loadtime.definition.DocumentParser; -import org.aspectj.weaver.model.AsmRelationshipProvider; -import org.aspectj.weaver.patterns.DeclareAnnotation; -import org.aspectj.weaver.patterns.DeclareParents; -import org.aspectj.weaver.patterns.ParserException; -import org.aspectj.weaver.patterns.PatternParser; -import org.aspectj.weaver.patterns.TypePattern; -import org.aspectj.weaver.tools.Trace; -import org.aspectj.weaver.tools.TraceFactory; - -public class BcelWorld extends World implements Repository { - - private final ClassPathManager classPath; - protected Repository delegate; - private BcelWeakClassLoaderReference loaderRef; - private final BcelWeavingSupport bcelWeavingSupport = new BcelWeavingSupport(); - private boolean isXmlConfiguredWorld = false; - private WeavingXmlConfig xmlConfiguration; - private List typeDelegateResolvers; - - private static Trace trace = TraceFactory.getTraceFactory().getTrace(BcelWorld.class); - - public BcelWorld() { - this(""); - } - - public BcelWorld(String cp) { - this(makeDefaultClasspath(cp), IMessageHandler.THROW, null); - } - - public IRelationship.Kind determineRelKind(ShadowMunger munger) { - AdviceKind ak = ((Advice) munger).getKind(); - if (ak.getKey() == AdviceKind.Before.getKey()) { - return IRelationship.Kind.ADVICE_BEFORE; - } else if (ak.getKey() == AdviceKind.After.getKey()) { - return IRelationship.Kind.ADVICE_AFTER; - } else if (ak.getKey() == AdviceKind.AfterThrowing.getKey()) { - return IRelationship.Kind.ADVICE_AFTERTHROWING; - } else if (ak.getKey() == AdviceKind.AfterReturning.getKey()) { - return IRelationship.Kind.ADVICE_AFTERRETURNING; - } else if (ak.getKey() == AdviceKind.Around.getKey()) { - return IRelationship.Kind.ADVICE_AROUND; - } else if (ak.getKey() == AdviceKind.CflowEntry.getKey() || ak.getKey() == AdviceKind.CflowBelowEntry.getKey() - || ak.getKey() == AdviceKind.InterInitializer.getKey() || ak.getKey() == AdviceKind.PerCflowEntry.getKey() - || ak.getKey() == AdviceKind.PerCflowBelowEntry.getKey() || ak.getKey() == AdviceKind.PerThisEntry.getKey() - || ak.getKey() == AdviceKind.PerTargetEntry.getKey() || ak.getKey() == AdviceKind.Softener.getKey() - || ak.getKey() == AdviceKind.PerTypeWithinEntry.getKey()) { - // System.err.println("Dont want a message about this: "+ak); - return null; - } - throw new RuntimeException("Shadow.determineRelKind: What the hell is it? " + ak); - } - - @Override - public void reportMatch(ShadowMunger munger, Shadow shadow) { - if (getCrossReferenceHandler() != null) { - getCrossReferenceHandler().addCrossReference(munger.getSourceLocation(), // What is being applied - shadow.getSourceLocation(), // Where is it being applied - determineRelKind(munger).getName(), // What kind of advice? - ((Advice) munger).hasDynamicTests() // Is a runtime test being stuffed in the code? - ); - } - - if (!getMessageHandler().isIgnoring(IMessage.WEAVEINFO)) { - reportWeavingMessage(munger, shadow); - } - - if (getModel() != null) { - AsmRelationshipProvider.addAdvisedRelationship(getModelAsAsmManager(), shadow, munger); - } - } - - /* - * Report a message about the advice weave that has occurred. Some messing about to make it pretty ! This code is just asking - * for an NPE to occur ... - */ - private void reportWeavingMessage(ShadowMunger munger, Shadow shadow) { - Advice advice = (Advice) munger; - AdviceKind aKind = advice.getKind(); - // Only report on interesting advice kinds ... - if (aKind == null || advice.getConcreteAspect() == null) { - // We suspect someone is programmatically driving the weaver - // (e.g. IdWeaveTestCase in the weaver testcases) - return; - } - if (!(aKind.equals(AdviceKind.Before) || aKind.equals(AdviceKind.After) || aKind.equals(AdviceKind.AfterReturning) - || aKind.equals(AdviceKind.AfterThrowing) || aKind.equals(AdviceKind.Around) || aKind.equals(AdviceKind.Softener))) { - return; - } - - // synchronized blocks are implemented with multiple monitor_exit instructions in the bytecode - // (one for normal exit from the method, one for abnormal exit), we only want to tell the user - // once we have advised the end of the sync block, even though under the covers we will have - // woven both exit points - if (shadow.getKind() == Shadow.SynchronizationUnlock) { - if (advice.lastReportedMonitorExitJoinpointLocation == null) { - // this is the first time through, let's continue... - advice.lastReportedMonitorExitJoinpointLocation = shadow.getSourceLocation(); - } else { - if (areTheSame(shadow.getSourceLocation(), advice.lastReportedMonitorExitJoinpointLocation)) { - // Don't report it again! - advice.lastReportedMonitorExitJoinpointLocation = null; - return; - } - // hmmm, this means some kind of nesting is going on, urgh - advice.lastReportedMonitorExitJoinpointLocation = shadow.getSourceLocation(); - } - } - - String description = advice.getKind().toString(); - String advisedType = shadow.getEnclosingType().getName(); - String advisingType = advice.getConcreteAspect().getName(); - Message msg = null; - if (advice.getKind().equals(AdviceKind.Softener)) { - msg = WeaveMessage.constructWeavingMessage(WeaveMessage.WEAVEMESSAGE_SOFTENS, new String[] { advisedType, - beautifyLocation(shadow.getSourceLocation()), advisingType, beautifyLocation(munger.getSourceLocation()) }, - advisedType, advisingType); - } else { - boolean runtimeTest = advice.hasDynamicTests(); - String joinPointDescription = shadow.toString(); - msg = WeaveMessage - .constructWeavingMessage(WeaveMessage.WEAVEMESSAGE_ADVISES, - new String[] { joinPointDescription, advisedType, beautifyLocation(shadow.getSourceLocation()), - description, advisingType, beautifyLocation(munger.getSourceLocation()), - (runtimeTest ? " [with runtime test]" : "") }, advisedType, advisingType); - // Boolean.toString(runtimeTest)}); - } - getMessageHandler().handleMessage(msg); - } - - private boolean areTheSame(ISourceLocation locA, ISourceLocation locB) { - if (locA == null) { - return locB == null; - } - if (locB == null) { - return false; - } - if (locA.getLine() != locB.getLine()) { - return false; - } - File fA = locA.getSourceFile(); - File fB = locA.getSourceFile(); - if (fA == null) { - return fB == null; - } - if (fB == null) { - return false; - } - return fA.getName().equals(fB.getName()); - } - - /* - * Ensure we report a nice source location - particular in the case where the source info is missing (binary weave). - */ - private String beautifyLocation(ISourceLocation isl) { - StringBuffer nice = new StringBuffer(); - if (isl == null || isl.getSourceFile() == null || isl.getSourceFile().getName().indexOf("no debug info available") != -1) { - nice.append("no debug info available"); - } else { - // can't use File.getName() as this fails when a Linux box encounters a path created on Windows and vice-versa - int takeFrom = isl.getSourceFile().getPath().lastIndexOf('/'); - if (takeFrom == -1) { - takeFrom = isl.getSourceFile().getPath().lastIndexOf('\\'); - } - int binary = isl.getSourceFile().getPath().lastIndexOf('!'); - if (binary != -1 && binary < takeFrom) { - // we have been woven by a binary aspect - String pathToBinaryLoc = isl.getSourceFile().getPath().substring(0, binary + 1); - if (pathToBinaryLoc.indexOf(".jar") != -1) { - // only want to add the extra info if we're from a jar file - int lastSlash = pathToBinaryLoc.lastIndexOf('/'); - if (lastSlash == -1) { - lastSlash = pathToBinaryLoc.lastIndexOf('\\'); - } - nice.append(pathToBinaryLoc.substring(lastSlash + 1)); - } - } - nice.append(isl.getSourceFile().getPath().substring(takeFrom + 1)); - if (isl.getLine() != 0) { - nice.append(":").append(isl.getLine()); - } - // if it's a binary file then also want to give the file name - if (isl.getSourceFileName() != null) { - nice.append("(from " + isl.getSourceFileName() + ")"); - } - } - return nice.toString(); - } - - private static List makeDefaultClasspath(String cp) { - List classPath = new ArrayList(); - classPath.addAll(getPathEntries(cp)); - classPath.addAll(getPathEntries(ClassPath.getClassPath())); - return classPath; - - } - - private static List getPathEntries(String s) { - List ret = new ArrayList(); - StringTokenizer tok = new StringTokenizer(s, File.pathSeparator); - while (tok.hasMoreTokens()) { - ret.add(tok.nextToken()); - } - return ret; - } - - public BcelWorld(List classPath, IMessageHandler handler, ICrossReferenceHandler xrefHandler) { - // this.aspectPath = new ClassPathManager(aspectPath, handler); - this.classPath = new ClassPathManager(classPath, handler); - setMessageHandler(handler); - setCrossReferenceHandler(xrefHandler); - // Tell BCEL to use us for resolving any classes - delegate = this; - } - - public BcelWorld(ClassPathManager cpm, IMessageHandler handler, ICrossReferenceHandler xrefHandler) { - classPath = cpm; - setMessageHandler(handler); - setCrossReferenceHandler(xrefHandler); - // Tell BCEL to use us for resolving any classes - delegate = this; - } - - /** - * Build a World from a ClassLoader, for LTW support - * - * @param loader - * @param handler - * @param xrefHandler - */ - public BcelWorld(ClassLoader loader, IMessageHandler handler, ICrossReferenceHandler xrefHandler) { - classPath = null; - loaderRef = new BcelWeakClassLoaderReference(loader); - setMessageHandler(handler); - setCrossReferenceHandler(xrefHandler); - // Tell BCEL to use us for resolving any classes - // delegate = getClassLoaderRepositoryFor(loader); - } - - public void ensureRepositorySetup() { - if (delegate == null) { - delegate = getClassLoaderRepositoryFor(loaderRef); - } - } - - public Repository getClassLoaderRepositoryFor(ClassLoaderReference loader) { - if (bcelRepositoryCaching) { - return new ClassLoaderRepository(loader); - } else { - return new NonCachingClassLoaderRepository(loader); - } - } - - public void addPath(String name) { - classPath.addPath(name, this.getMessageHandler()); - } - - // ---- various interactions with bcel - - public static Type makeBcelType(UnresolvedType type) { - return Type.getType(type.getErasureSignature()); - } - - static Type[] makeBcelTypes(UnresolvedType[] types) { - Type[] ret = new Type[types.length]; - for (int i = 0, len = types.length; i < len; i++) { - ret[i] = makeBcelType(types[i]); - } - return ret; - } - - public static Type[] makeBcelTypes(String[] types) { - if (types == null || types.length==0 ) { - return null; - } - Type[] ret = new Type[types.length]; - for (int i=0, len=types.length; i", new ResolvedType[] { INT }); - } else if (i instanceof MULTIANEWARRAY) { - MULTIANEWARRAY arrayInstruction = (MULTIANEWARRAY) i; - UnresolvedType ut = null; - short dimensions = arrayInstruction.getDimensions(); - ObjectType ot = arrayInstruction.getLoadClassType(cpg); - if (ot != null) { - ut = fromBcel(ot); - ut = UnresolvedType.makeArray(ut, dimensions); - } else { - Type t = arrayInstruction.getType(cpg); - ut = fromBcel(t); - } - ResolvedType[] parms = new ResolvedType[dimensions]; - for (int ii = 0; ii < dimensions; ii++) { - parms[ii] = INT; - } - retval = MemberImpl.method(ut, Modifier.PUBLIC, UnresolvedType.VOID, "", parms); - - } else if (i.opcode == Constants.NEWARRAY) { - // NEWARRAY arrayInstruction = (NEWARRAY)i; - Type ot = i.getType(); - UnresolvedType ut = fromBcel(ot); - retval = MemberImpl.method(ut, Modifier.PUBLIC, UnresolvedType.VOID, "", new ResolvedType[] { INT }); - } else { - throw new BCException("Cannot create array construction signature for this non-array instruction:" + i); - } - return retval; - } - - public Member makeJoinPointSignatureForMethodInvocation(LazyClassGen cg, InvokeInstruction ii) { - ConstantPool cpg = cg.getConstantPool(); - String name = ii.getName(cpg); - String declaring = ii.getClassName(cpg); - UnresolvedType declaringType = null; - - String signature = ii.getSignature(cpg); - - // 307147 - if (name.startsWith("ajc$privMethod$")) { - // The invoke is on a privileged accessor. These may be created for different - // kinds of target, not necessarily just private methods. In bug 307147 it is - // for a private method. This code is identifying the particular case in 307147 - try { - declaringType = UnresolvedType.forName(declaring); - String typeNameAsFoundInAccessorName = declaringType.getName().replace('.', '_'); - int indexInAccessorName = name.lastIndexOf(typeNameAsFoundInAccessorName); - if (indexInAccessorName != -1) { - String methodName = name.substring(indexInAccessorName+typeNameAsFoundInAccessorName.length()+1); - ResolvedType resolvedDeclaringType = declaringType.resolve(this); - ResolvedMember[] methods = resolvedDeclaringType.getDeclaredMethods(); - for (ResolvedMember method: methods) { - if (method.getName().equals(methodName) && method.getSignature().equals(signature) && Modifier.isPrivate(method.getModifiers())) { - return method; - } - } - } - } catch (Exception e) { - // Remove this once confident above code isn't having unexpected side effects - // Added 1.8.7 - e.printStackTrace(); - } - } - - int modifier = (ii instanceof INVOKEINTERFACE) ? Modifier.INTERFACE - : (ii.opcode == Constants.INVOKESTATIC) ? Modifier.STATIC : (ii.opcode == Constants.INVOKESPECIAL && !name - .equals("")) ? Modifier.PRIVATE : 0; - - // in Java 1.4 and after, static method call of super class within - // subclass method appears - // as declared by the subclass in the bytecode - but they are not - // see #104212 - if (ii.opcode == Constants.INVOKESTATIC) { - ResolvedType appearsDeclaredBy = resolve(declaring); - // look for the method there - for (Iterator iterator = appearsDeclaredBy.getMethods(true, true); iterator.hasNext();) { - ResolvedMember method = iterator.next(); - if (Modifier.isStatic(method.getModifiers())) { - if (name.equals(method.getName()) && signature.equals(method.getSignature())) { - // we found it - declaringType = method.getDeclaringType(); - break; - } - } - } - } - - if (declaringType == null) { - if (declaring.charAt(0) == '[') { - declaringType = UnresolvedType.forSignature(declaring); - } else { - declaringType = UnresolvedType.forName(declaring); - } - } - return MemberImpl.method(declaringType, modifier, name, signature); - } - - @Override - public String toString() { - StringBuffer buf = new StringBuffer(); - buf.append("BcelWorld("); - // buf.append(shadowMungerMap); - buf.append(")"); - return buf.toString(); - } - - /** - * Retrieve a bcel delegate for an aspect - this will return NULL if the delegate is an EclipseSourceType and not a - * BcelObjectType - this happens quite often when incrementally compiling. - */ - public static BcelObjectType getBcelObjectType(ResolvedType concreteAspect) { - if (concreteAspect == null) { - return null; - } - if (!(concreteAspect instanceof ReferenceType)) { // Might be Missing - return null; - } - ReferenceTypeDelegate rtDelegate = ((ReferenceType) concreteAspect).getDelegate(); - if (rtDelegate instanceof BcelObjectType) { - return (BcelObjectType) rtDelegate; - } else { - return null; - } - } - - public void tidyUp() { - // At end of compile, close any open files so deletion of those archives - // is possible - classPath.closeArchives(); - typeMap.report(); - typeMap.demote(true); - // ResolvedType.resetPrimitives(); - } - - // / The repository interface methods - - @Override - public JavaClass findClass(String className) { - return lookupJavaClass(classPath, className); - } - - @Override - public JavaClass loadClass(String className) throws ClassNotFoundException { - return lookupJavaClass(classPath, className); - } - - @Override - public void storeClass(JavaClass clazz) { - // doesn't need to do anything - } - - @Override - public void removeClass(JavaClass clazz) { - throw new RuntimeException("Not implemented"); - } - - @Override - public JavaClass loadClass(Class clazz) throws ClassNotFoundException { - throw new RuntimeException("Not implemented"); - } - - @Override - public void clear() { - delegate.clear(); - // throw new RuntimeException("Not implemented"); - } - - /** - * The aim of this method is to make sure a particular type is 'ok'. Some operations on the delegate for a type modify it and - * this method is intended to undo that... see pr85132 - */ - @Override - public void validateType(UnresolvedType type) { - ResolvedType result = typeMap.get(type.getSignature()); - if (result == null) { - return; // We haven't heard of it yet - } - if (!result.isExposedToWeaver()) { - return; // cant need resetting - } - result.ensureConsistent(); - // If we want to rebuild it 'from scratch' then: - // ClassParser cp = new ClassParser(new - // ByteArrayInputStream(newbytes),new String(cs)); - // try { - // rt.setDelegate(makeBcelObjectType(rt,cp.parse(),true)); - // } catch (ClassFormatException e) { - // e.printStackTrace(); - // } catch (IOException e) { - // e.printStackTrace(); - // } - } - - /** - * Apply a single declare parents - return true if we change the type - */ - private boolean applyDeclareParents(DeclareParents p, ResolvedType onType) { - boolean didSomething = false; - List newParents = p.findMatchingNewParents(onType, true); - if (!newParents.isEmpty()) { - didSomething = true; - BcelObjectType classType = BcelWorld.getBcelObjectType(onType); - // System.err.println("need to do declare parents for: " + onType); - for (ResolvedType newParent : newParents) { - // We set it here so that the imminent matching for ITDs can - // succeed - we still haven't done the necessary changes to the class file - // itself (like transform super calls) - that is done in - // BcelTypeMunger.mungeNewParent() - // classType.addParent(newParent); - onType.addParent(newParent); - ResolvedTypeMunger newParentMunger = new NewParentTypeMunger(newParent, p.getDeclaringType()); - newParentMunger.setSourceLocation(p.getSourceLocation()); - onType.addInterTypeMunger(new BcelTypeMunger(newParentMunger, getCrosscuttingMembersSet() - .findAspectDeclaringParents(p)), false); - } - } - return didSomething; - } - - /** - * Apply a declare @type - return true if we change the type - */ - private boolean applyDeclareAtType(DeclareAnnotation decA, ResolvedType onType, boolean reportProblems) { - boolean didSomething = false; - if (decA.matches(onType)) { - - if (onType.hasAnnotation(decA.getAnnotation().getType())) { - // already has it - return false; - } - - AnnotationAJ annoX = decA.getAnnotation(); - - // check the annotation is suitable for the target - boolean isOK = checkTargetOK(decA, onType, annoX); - - if (isOK) { - didSomething = true; - ResolvedTypeMunger newAnnotationTM = new AnnotationOnTypeMunger(annoX); - newAnnotationTM.setSourceLocation(decA.getSourceLocation()); - onType.addInterTypeMunger(new BcelTypeMunger(newAnnotationTM, decA.getAspect().resolve(this)), false); - decA.copyAnnotationTo(onType); - } - } - return didSomething; - } - - /** - * Apply the specified declare @field construct to any matching fields in the specified type. - * @param deca the declare annotation targeting fields - * @param type the type to check for members matching the declare annotation - * @return true if something matched and the type was modified - */ - private boolean applyDeclareAtField(DeclareAnnotation deca, ResolvedType type) { - boolean changedType = false; - ResolvedMember[] fields = type.getDeclaredFields(); - for (ResolvedMember field: fields) { - if (deca.matches(field, this)) { - AnnotationAJ anno = deca.getAnnotation(); - if (!field.hasAnnotation(anno.getType())) { - field.addAnnotation(anno); - changedType=true; - } - } - } - return changedType; - } - - /** - * Checks for an @target() on the annotation and if found ensures it allows the annotation to be attached to the target type - * that matched. - */ - private boolean checkTargetOK(DeclareAnnotation decA, ResolvedType onType, AnnotationAJ annoX) { - if (annoX.specifiesTarget()) { - if ((onType.isAnnotation() && !annoX.allowedOnAnnotationType()) || (!annoX.allowedOnRegularType())) { - return false; - } - } - return true; - } - - // Hmmm - very similar to the code in BcelWeaver.weaveParentTypeMungers - - // this code - // doesn't need to produce errors/warnings though as it won't really be - // weaving. - protected void weaveInterTypeDeclarations(ResolvedType onType) { - - List declareParentsList = getCrosscuttingMembersSet().getDeclareParents(); - if (onType.isRawType()) { - onType = onType.getGenericType(); - } - onType.clearInterTypeMungers(); - - List decpToRepeat = new ArrayList(); - - boolean aParentChangeOccurred = false; - boolean anAnnotationChangeOccurred = false; - // First pass - apply all decp mungers - for (Iterator i = declareParentsList.iterator(); i.hasNext();) { - DeclareParents decp = i.next(); - boolean typeChanged = applyDeclareParents(decp, onType); - if (typeChanged) { - aParentChangeOccurred = true; - } else { // Perhaps it would have matched if a 'dec @type' had - // modified the type - if (!decp.getChild().isStarAnnotation()) { - decpToRepeat.add(decp); - } - } - } - - // Still first pass - apply all dec @type mungers - for (DeclareAnnotation decA : getCrosscuttingMembersSet().getDeclareAnnotationOnTypes()) { - boolean typeChanged = applyDeclareAtType(decA, onType, true); - if (typeChanged) { - anAnnotationChangeOccurred = true; - } - } - - // apply declare @field - for (DeclareAnnotation deca: getCrosscuttingMembersSet().getDeclareAnnotationOnFields()) { - if (applyDeclareAtField(deca,onType)) { - anAnnotationChangeOccurred = true; - } - } - - while ((aParentChangeOccurred || anAnnotationChangeOccurred) && !decpToRepeat.isEmpty()) { - anAnnotationChangeOccurred = aParentChangeOccurred = false; - List decpToRepeatNextTime = new ArrayList(); - for (DeclareParents decp: decpToRepeat) { - if (applyDeclareParents(decp, onType)) { - aParentChangeOccurred = true; - } else { - decpToRepeatNextTime.add(decp); - } - } - - for (DeclareAnnotation deca: getCrosscuttingMembersSet().getDeclareAnnotationOnTypes()) { - if (applyDeclareAtType(deca, onType, false)) { - anAnnotationChangeOccurred = true; - } - } - - for (DeclareAnnotation deca: getCrosscuttingMembersSet().getDeclareAnnotationOnFields()) { - if (applyDeclareAtField(deca, onType)) { - anAnnotationChangeOccurred = true; - } - } - decpToRepeat = decpToRepeatNextTime; - } - - } - - @Override - public IWeavingSupport getWeavingSupport() { - return bcelWeavingSupport; - } - - @Override - public void reportCheckerMatch(Checker checker, Shadow shadow) { - IMessage iMessage = new Message(checker.getMessage(shadow), shadow.toString(), checker.isError() ? IMessage.ERROR - : IMessage.WARNING, shadow.getSourceLocation(), null, new ISourceLocation[] { checker.getSourceLocation() }, true, - 0, -1, -1); - - getMessageHandler().handleMessage(iMessage); - - if (getCrossReferenceHandler() != null) { - getCrossReferenceHandler() - .addCrossReference( - checker.getSourceLocation(), - shadow.getSourceLocation(), - (checker.isError() ? IRelationship.Kind.DECLARE_ERROR.getName() : IRelationship.Kind.DECLARE_WARNING - .getName()), false); - - } - - if (getModel() != null) { - AsmRelationshipProvider.addDeclareErrorOrWarningRelationship(getModelAsAsmManager(), shadow, checker); - } - - } - - public AsmManager getModelAsAsmManager() { - return (AsmManager) getModel(); // For now... always an AsmManager in a bcel environment - } - - void raiseError(String message) { - getMessageHandler().handleMessage(MessageUtil.error(message)); - } - - /** - * These are aop.xml files that can be used to alter the aspects that actually apply from those passed in - and also their scope - * of application to other files in the system. - * - * @param xmlFiles list of File objects representing any aop.xml files passed in to configure the build process - */ - public void setXmlFiles(List xmlFiles) { - if (!isXmlConfiguredWorld && !xmlFiles.isEmpty()) { - raiseError("xml configuration files only supported by the compiler when -xmlConfigured option specified"); - return; - } - if (!xmlFiles.isEmpty()) { - xmlConfiguration = new WeavingXmlConfig(this, WeavingXmlConfig.MODE_COMPILE); - } - for (File xmlfile : xmlFiles) { - try { - Definition d = DocumentParser.parse(xmlfile.toURI().toURL()); - xmlConfiguration.add(d); - } catch (MalformedURLException e) { - raiseError("Unexpected problem processing XML config file '" + xmlfile.getName() + "' :" + e.getMessage()); - } catch (Exception e) { - raiseError("Unexpected problem processing XML config file '" + xmlfile.getName() + "' :" + e.getMessage()); - } - } - } - - /** - * Add a scoped aspects where the scoping was defined in an aop.xml file and this world is being used in a LTW configuration - */ - public void addScopedAspect(String name, String scope) { - this.isXmlConfiguredWorld = true; - if (xmlConfiguration == null) { - xmlConfiguration = new WeavingXmlConfig(this, WeavingXmlConfig.MODE_LTW); - } - xmlConfiguration.addScopedAspect(name, scope); - } - - public void setXmlConfigured(boolean b) { - this.isXmlConfiguredWorld = b; - } - - @Override - public boolean isXmlConfigured() { - return isXmlConfiguredWorld && xmlConfiguration != null; - } - - public WeavingXmlConfig getXmlConfiguration() { - return xmlConfiguration; - } - - @Override - public boolean isAspectIncluded(ResolvedType aspectType) { - if (!isXmlConfigured()) { - return true; - } - return xmlConfiguration.specifiesInclusionOfAspect(aspectType.getName()); - } - - @Override - public TypePattern getAspectScope(ResolvedType declaringType) { - return xmlConfiguration.getScopeFor(declaringType.getName()); - } - - @Override - public boolean hasUnsatisfiedDependency(ResolvedType aspectType) { - String aspectName = aspectType.getName(); - - if (aspectType.hasAnnotations()) { - AnnotationAJ[] annos = aspectType.getAnnotations(); - for (AnnotationAJ anno: annos) { - if (anno.getTypeName().equals("org.aspectj.lang.annotation.RequiredTypes")) { - String values = anno.getStringFormOfValue("value"); // Example: "[A,org.foo.Bar]" - if (values != null && values.length() > 2) { - values = values.substring(1,values.length()-1); - StringTokenizer tokenizer = new StringTokenizer(values,","); - boolean anythingMissing = false; - while (tokenizer.hasMoreElements()) { - String requiredTypeName = tokenizer.nextToken(); - ResolvedType rt = resolve(UnresolvedType.forName(requiredTypeName)); - if (rt.isMissing()) { - if (!getMessageHandler().isIgnoring(IMessage.INFO)) { - getMessageHandler().handleMessage( - MessageUtil.info("deactivating aspect '" + aspectName + "' as it requires type '" - + requiredTypeName + "' which cannot be found on the classpath")); - } - anythingMissing = true; - if (aspectRequiredTypes == null) { - aspectRequiredTypes = new HashMap(); - } - // Record that it has an invalid type reference - aspectRequiredTypes.put(aspectName,requiredTypeName); - } - } - if (anythingMissing) { - return true; - } - else { - return false; - } - } - else { - // no value specified for annotation - return false; - } - } - } - } - if (aspectRequiredTypes == null) { - // no aspects require anything, so there can be no unsatisfied dependencies - return false; - } - if (!aspectRequiredTypesProcessed.contains(aspectName)) { - String requiredTypeName = aspectRequiredTypes.get(aspectName); - if (requiredTypeName==null) { - aspectRequiredTypesProcessed.add(aspectName); - return false; - } else { - ResolvedType rt = resolve(UnresolvedType.forName(requiredTypeName)); - if (!rt.isMissing()) { - aspectRequiredTypesProcessed.add(aspectName); - aspectRequiredTypes.remove(aspectName); - return false; - } else { - if (!getMessageHandler().isIgnoring(IMessage.INFO)) { - getMessageHandler().handleMessage( - MessageUtil.info("deactivating aspect '" + aspectName + "' as it requires type '" - + requiredTypeName + "' which cannot be found on the classpath")); - } - aspectRequiredTypesProcessed.add(aspectName); - return true; - } - } - } - return aspectRequiredTypes.containsKey(aspectName); - } - - private List aspectRequiredTypesProcessed = new ArrayList(); - private Map aspectRequiredTypes = null; - - public void addAspectRequires(String aspectClassName, String requiredType) { - if (aspectRequiredTypes == null) { - aspectRequiredTypes = new HashMap(); - } - aspectRequiredTypes.put(aspectClassName,requiredType); - } - - /** - * A WeavingXmlConfig is initially a collection of definitions from XML files - once the world is ready and weaving is running - * it will initialize and transform those definitions into an optimized set of values (eg. resolve type patterns and string - * names to real entities). It can then answer questions quickly: (1) is this aspect included in the weaving? (2) Is there a - * scope specified for this aspect and does it include type X? - * - */ - static class WeavingXmlConfig { - - final static int MODE_COMPILE = 1; - final static int MODE_LTW = 2; - - private int mode; - - private boolean initialized = false; // Lazily done - private List definitions = new ArrayList(); - - private List resolvedIncludedAspects = new ArrayList(); - private Map scopes = new HashMap(); - - // these are not set for LTW mode (exclusion of these fast match patterns is handled before the weaver/world are used) - private List includedFastMatchPatterns = Collections.emptyList(); - private List includedPatterns = Collections.emptyList(); - private List excludedFastMatchPatterns = Collections.emptyList(); - private List excludedPatterns = Collections.emptyList(); - - private BcelWorld world; - - public WeavingXmlConfig(BcelWorld bcelWorld, int mode) { - this.world = bcelWorld; - this.mode = mode; - } - - public void add(Definition d) { - definitions.add(d); - } - - public void addScopedAspect(String aspectName, String scope) { - ensureInitialized(); - resolvedIncludedAspects.add(aspectName); - try { - TypePattern scopePattern = new PatternParser(scope).parseTypePattern(); - scopePattern.resolve(world); - scopes.put(aspectName, scopePattern); - if (!world.getMessageHandler().isIgnoring(IMessage.INFO)) { - world.getMessageHandler().handleMessage( - MessageUtil.info("Aspect '" + aspectName + "' is scoped to apply against types matching pattern '" - + scopePattern.toString() + "'")); - } - } catch (Exception e) { - world.getMessageHandler().handleMessage( - MessageUtil.error("Unable to parse scope as type pattern. Scope was '" + scope + "': " + e.getMessage())); - } - } - - public void ensureInitialized() { - if (!initialized) { - try { - resolvedIncludedAspects = new ArrayList(); - // Process the definitions into something more optimal - for (Definition definition : definitions) { - List aspectNames = definition.getAspectClassNames(); - for (String name : aspectNames) { - resolvedIncludedAspects.add(name); - // TODO check for existence? - // ResolvedType resolvedAspect = resolve(UnresolvedType.forName(name)); - // if (resolvedAspect.isMissing()) { - // // ERROR - // } else { - // resolvedIncludedAspects.add(resolvedAspect); - // } - String scope = definition.getScopeForAspect(name); - if (scope != null) { - // Resolve the type pattern - try { - TypePattern scopePattern = new PatternParser(scope).parseTypePattern(); - scopePattern.resolve(world); - scopes.put(name, scopePattern); - if (!world.getMessageHandler().isIgnoring(IMessage.INFO)) { - world.getMessageHandler().handleMessage( - MessageUtil.info("Aspect '" + name - + "' is scoped to apply against types matching pattern '" - + scopePattern.toString() + "'")); - } - } catch (Exception e) { - // TODO definitions should remember which file they came from, for inclusion in this message - world.getMessageHandler().handleMessage( - MessageUtil.error("Unable to parse scope as type pattern. Scope was '" + scope + "': " - + e.getMessage())); - } - } - } - try { - List includePatterns = definition.getIncludePatterns(); - if (includePatterns.size() > 0) { - includedPatterns = new ArrayList(); - includedFastMatchPatterns = new ArrayList(); - } - for (String includePattern : includePatterns) { - if (includePattern.endsWith("..*")) { - // from 'blah.blah.blah..*' leave the 'blah.blah.blah.' - includedFastMatchPatterns.add(includePattern.substring(0, includePattern.length() - 2)); - } else { - TypePattern includedPattern = new PatternParser(includePattern).parseTypePattern(); - includedPatterns.add(includedPattern); - } - } - List excludePatterns = definition.getExcludePatterns(); - if (excludePatterns.size() > 0) { - excludedPatterns = new ArrayList(); - excludedFastMatchPatterns = new ArrayList(); - } - for (String excludePattern : excludePatterns) { - if (excludePattern.endsWith("..*")) { - // from 'blah.blah.blah..*' leave the 'blah.blah.blah.' - excludedFastMatchPatterns.add(excludePattern.substring(0, excludePattern.length() - 2)); - } else { - TypePattern excludedPattern = new PatternParser(excludePattern).parseTypePattern(); - excludedPatterns.add(excludedPattern); - } - } - } catch (ParserException pe) { - // TODO definitions should remember which file they came from, for inclusion in this message - world.getMessageHandler().handleMessage( - MessageUtil.error("Unable to parse type pattern: " + pe.getMessage())); - - } - } - } finally { - initialized = true; - } - } - } - - public boolean specifiesInclusionOfAspect(String name) { - ensureInitialized(); - return resolvedIncludedAspects.contains(name); - } - - public TypePattern getScopeFor(String name) { - return scopes.get(name); - } - - // Can't quite follow the same rules for exclusion as used for loadtime weaving: - // "The set of types to be woven are those types matched by at least one weaver include element and not matched by any - // weaver - // exclude element. If there are no weaver include statements then all non-excluded types are included." - // Since if the weaver is seeing it during this kind of build, the type is implicitly included. So all we should check - // for is exclusion - public boolean excludesType(ResolvedType type) { - if (mode == MODE_LTW) { - return false; - } - String typename = type.getName(); - boolean excluded = false; - for (String excludedPattern : excludedFastMatchPatterns) { - if (typename.startsWith(excludedPattern)) { - excluded = true; - break; - } - } - if (!excluded) { - for (TypePattern excludedPattern : excludedPatterns) { - if (excludedPattern.matchesStatically(type)) { - excluded = true; - break; - } - } - } - return excluded; - } - - } - - @Override - public TypeMap getTypeMap() { - return typeMap; - } - - @Override - public boolean isLoadtimeWeaving() { - return false; - } - - public void addTypeDelegateResolver(TypeDelegateResolver typeDelegateResolver) { - if (typeDelegateResolvers == null) { - typeDelegateResolvers = new ArrayList(); - } - typeDelegateResolvers.add(typeDelegateResolver); - } - - @Override - public void classWriteEvent(char[][] compoundName) { - typeMap.classWriteEvent(new String(CharOperation.concatWith(compoundName, '.'))); - } - - /** - * Force demote a type. - */ - public void demote(ResolvedType type) { - typeMap.demote(type); - } - -} diff --git a/weaver/src/org/aspectj/weaver/bcel/ClassPathManager.java b/weaver/src/org/aspectj/weaver/bcel/ClassPathManager.java deleted file mode 100644 index f8a36ba98..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/ClassPathManager.java +++ /dev/null @@ -1,579 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002, 2017 Contributors - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Palo Alto Research Center, Incorporated (PARC). - * ******************************************************************/ -package org.aspectj.weaver.bcel; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; -import java.nio.file.FileVisitResult; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.attribute.BasicFileAttributes; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -import org.aspectj.bridge.IMessageHandler; -import org.aspectj.bridge.MessageUtil; -import org.aspectj.util.LangUtil; -import org.aspectj.util.SoftHashMap; -import org.aspectj.weaver.BCException; -import org.aspectj.weaver.UnresolvedType; -import org.aspectj.weaver.WeaverMessages; -import org.aspectj.weaver.tools.Trace; -import org.aspectj.weaver.tools.TraceFactory; - -/** - * @author Andy Clement - * @author Mario Ivankovits - */ -public class ClassPathManager { - - private static Trace trace = TraceFactory.getTraceFactory().getTrace(ClassPathManager.class); - - private static int maxOpenArchives = -1; - - private static URI JRT_URI = URI.create("jrt:/"); //$NON-NLS-1$ - - private static final int MAXOPEN_DEFAULT = 1000; - - private List entries; - - // In order to control how many open files we have, we maintain a list. - // The max number is configured through the property: - // org.aspectj.weaver.openarchives - // and it defaults to 1000 - private List openArchives = new ArrayList(); - - static { - String openzipsString = getSystemPropertyWithoutSecurityException("org.aspectj.weaver.openarchives", - Integer.toString(MAXOPEN_DEFAULT)); - maxOpenArchives = Integer.parseInt(openzipsString); - if (maxOpenArchives < 20) { - maxOpenArchives = 1000; - } - } - - public ClassPathManager(List classpath, IMessageHandler handler) { - if (trace.isTraceEnabled()) { - trace.enter("", this, new Object[] { classpath==null?"null":classpath.toString(), handler }); - } - entries = new ArrayList(); - for (String classpathEntry: classpath) { - addPath(classpathEntry,handler); - } - if (trace.isTraceEnabled()) { - trace.exit(""); - } - } - - protected ClassPathManager() { - } - - public void addPath(String name, IMessageHandler handler) { - File f = new File(name); - String lc = name.toLowerCase(); - if (!f.isDirectory()) { - if (!f.isFile()) { - if (!lc.endsWith(".jar") || lc.endsWith(".zip")) { - // heuristic-only: ending with .jar or .zip means probably a - // zip file - MessageUtil.info(handler, WeaverMessages.format(WeaverMessages.ZIPFILE_ENTRY_MISSING, name)); - } else { - MessageUtil.info(handler, WeaverMessages.format(WeaverMessages.DIRECTORY_ENTRY_MISSING, name)); - } - return; - } - try { - if (lc.endsWith(LangUtil.JRT_FS)) { // Java9 - entries.add(new JImageEntry()); - } else { - entries.add(new ZipFileEntry(f)); - } - } catch (IOException ioe) { - MessageUtil.warn(handler, - WeaverMessages.format(WeaverMessages.ZIPFILE_ENTRY_INVALID, name, ioe.getMessage())); - return; - } - } else { - entries.add(new DirEntry(f)); - } - } - - public ClassFile find(UnresolvedType type) { - if (trace.isTraceEnabled()) { - trace.enter("find", this, type); - } - String name = type.getName(); - for (Iterator i = entries.iterator(); i.hasNext();) { - Entry entry = i.next(); - try { - ClassFile ret = entry.find(name); - if (trace.isTraceEnabled()) { - trace.event("searching for "+type+" in "+entry.toString()); - } - if (ret != null) { - if (trace.isTraceEnabled()) { - trace.exit("find", ret); - } - return ret; - } - } catch (IOException ioe) { - // this is NOT an error: it's valid to have missing classpath entries - if (trace.isTraceEnabled()) { - trace.error("Removing classpath entry for "+entry,ioe); - } - i.remove(); - } - } - if (trace.isTraceEnabled()) { - trace.exit("find", null); - } - return null; - } - - @Override - public String toString() { - StringBuffer buf = new StringBuffer(); - boolean start = true; - for (Iterator i = entries.iterator(); i.hasNext();) { - if (start) { - start = false; - } else { - buf.append(File.pathSeparator); - } - buf.append(i.next()); - } - return buf.toString(); - } - - public abstract static class ClassFile { - public abstract InputStream getInputStream() throws IOException; - public abstract String getPath(); - public abstract void close(); - } - - abstract static class Entry { - public abstract ClassFile find(String name) throws IOException; - } - - static class ByteBasedClassFile extends ClassFile { - - private byte[] bytes; - private ByteArrayInputStream bais; - private String path; - - public ByteBasedClassFile(byte[] bytes, String path) { - this.bytes = bytes; - this.path = path; - } - - @Override - public InputStream getInputStream() throws IOException { - this.bais = new ByteArrayInputStream(bytes); - return this.bais; - } - - @Override - public String getPath() { - return this.path; - } - - @Override - public void close() { - if (this.bais!=null) { - try { - this.bais.close(); - } catch (IOException e) { - } - this.bais = null; - } - } - - } - - static class FileClassFile extends ClassFile { - private File file; - private FileInputStream fis; - - public FileClassFile(File file) { - this.file = file; - } - - @Override - public InputStream getInputStream() throws IOException { - fis = new FileInputStream(file); - return fis; - } - - @Override - public void close() { - try { - if (fis != null) - fis.close(); - } catch (IOException ioe) { - throw new BCException("Can't close class file : " + file.getName(), ioe); - } finally { - fis = null; - } - } - - @Override - public String getPath() { - return file.getPath(); - } - } - - class DirEntry extends Entry { - private String dirPath; - - public DirEntry(File dir) { - this.dirPath = dir.getPath(); - } - - public DirEntry(String dirPath) { - this.dirPath = dirPath; - } - - @Override - public ClassFile find(String name) { - File f = new File(dirPath + File.separator + name.replace('.', File.separatorChar) + ".class"); - if (f.isFile()) - return new FileClassFile(f); - else - return null; - } - - @Override - public String toString() { - return dirPath; - } - } - - static class ZipEntryClassFile extends ClassFile { - private ZipEntry entry; - private ZipFileEntry zipFile; - private InputStream is; - - public ZipEntryClassFile(ZipFileEntry zipFile, ZipEntry entry) { - this.zipFile = zipFile; - this.entry = entry; - } - - @Override - public InputStream getInputStream() throws IOException { - is = zipFile.getZipFile().getInputStream(entry); - return is; - } - - @Override - public void close() { - try { - if (is != null) - is.close(); - } catch (IOException e) { - e.printStackTrace(); - } finally { - is = null; - } - } - - @Override - public String getPath() { - return entry.getName(); - } - - } - - /** - * Maintains a shared package cache for java runtime image. This maps packages (for example: - * java/lang) to a starting root position in the filesystem (for example: /modules/java.base/java/lang). - * When searching for a type we work out the package name, use it to find where in the filesystem - * to start looking then run from there. Once found we do cache what we learn to make subsequent - * lookups of that type even faster. Maintaining just a package cache rather than complete type cache - * helps reduce memory usage but still gives reasonably fast lookup performance. - */ - static class JImageEntry extends Entry { - - private static FileSystem fs = null; - - private final static Map fileCache = new SoftHashMap(); - - private final static Map packageCache = new HashMap(); - - private static boolean packageCacheInitialized = false; - - public JImageEntry() { - if (fs == null) { - try { - fs = FileSystems.getFileSystem(JRT_URI); - } catch (Throwable t) { - throw new IllegalStateException("Unexpectedly unable to initialize a JRT filesystem", t); - } - } - buildPackageMap(); - } - - class PackageCacheBuilderVisitor extends SimpleFileVisitor { - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - if (file.getNameCount() > 3 && file.toString().endsWith(".class")) { - int fnc = file.getNameCount(); - if (fnc > 3) { // There is a package name - e.g. /modules/java.base/java/lang/Object.class - Path packagePath = file.subpath(2, fnc-1); // e.g. java/lang - String packagePathString = packagePath.toString(); - packageCache.put(packagePathString, file.subpath(0, fnc-1)); // java/lang -> /modules/java.base/java/lang - } - } - return FileVisitResult.CONTINUE; - } - } - - /** - * Create a map from package names to the specific directory of the package members in the filesystem. - */ - private synchronized void buildPackageMap() { - if (!packageCacheInitialized) { - packageCacheInitialized = true; - Iterable roots = fs.getRootDirectories(); - PackageCacheBuilderVisitor visitor = new PackageCacheBuilderVisitor(); - try { - for (java.nio.file.Path path : roots) { - Files.walkFileTree(path, visitor); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } - } - - class TypeIdentifier extends SimpleFileVisitor { - - // What are we looking for? - private String name; - - // If set, where did we find it? - public Path found; - - // Basic metric count of how many files we checked before finding it - public int filesSearchedCount; - - public TypeIdentifier(String name) { - this.name = name; - } - - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - filesSearchedCount++; - if (file.getNameCount() > 2 && file.toString().endsWith(".class")) { - int fnc = file.getNameCount(); - Path filePath = file.subpath(2, fnc); - String filePathString = filePath.toString(); - if (filePathString.equals(name)) { - fileCache.put(filePathString, file); - found = file; - return FileVisitResult.TERMINATE; - } - } - return FileVisitResult.CONTINUE; - } - } - - private Path searchForFileAndCache(final Path startPath, final String name) { - TypeIdentifier locator = new TypeIdentifier(name); - try { - Files.walkFileTree(startPath, locator); - } catch (IOException e) { - throw new RuntimeException(e); - } - return locator.found; - } - - @Override - public ClassFile find(String name) throws IOException { - String fileName = name.replace('.', '/') + ".class"; - Path file = fileCache.get(fileName); - if (file == null) { - // Check the packages map to see if we know about this package - int idx = fileName.lastIndexOf('/'); - if (idx == -1) { - // Package not here - return null; - } - Path packageStart = null; - String packageName = null; - if (idx !=-1 ) { - packageName = fileName.substring(0, idx); - packageStart = packageCache.get(packageName); - if (packageStart != null) { - file = searchForFileAndCache(packageStart, fileName); - } - } - } - if (file == null) { - return null; - } - byte[] bs = Files.readAllBytes(file); - ClassFile cf = new ByteBasedClassFile(bs, fileName); - return cf; - } - - static Map getPackageCache() { - return packageCache; - } - - static Map getFileCache() { - return fileCache; - } - - } - - class ZipFileEntry extends Entry { - private File file; - private ZipFile zipFile; - - public ZipFileEntry(File file) throws IOException { - this.file = file; - } - - public ZipFileEntry(ZipFile zipFile) { - this.zipFile = zipFile; - } - - public ZipFile getZipFile() { - return zipFile; - } - - @Override - public ClassFile find(String name) throws IOException { - ensureOpen(); - String key = name.replace('.', '/') + ".class"; - ZipEntry entry = zipFile.getEntry(key); - if (entry != null) - return new ZipEntryClassFile(this, entry); - else - return null; // This zip will be closed when necessary... - } - - public List getAllClassFiles() throws IOException { - ensureOpen(); - List ret = new ArrayList(); - for (Enumeration e = zipFile.entries(); e.hasMoreElements();) { - ZipEntry entry = e.nextElement(); - String name = entry.getName(); - if (hasClassExtension(name)) - ret.add(new ZipEntryClassFile(this, entry)); - } - // if (ret.isEmpty()) close(); - return ret; - } - - private void ensureOpen() throws IOException { - if (zipFile != null && openArchives.contains(zipFile)) { - if (isReallyOpen()) - return; - } - if (openArchives.size() >= maxOpenArchives) { - closeSomeArchives(openArchives.size() / 10); // Close 10% of - // those open - } - zipFile = new ZipFile(file); - if (!isReallyOpen()) { - throw new FileNotFoundException("Can't open archive: " + file.getName() + " (size() check failed)"); - } - openArchives.add(zipFile); - } - - private boolean isReallyOpen() { - try { - zipFile.size(); // this will fail if the file has been closed - // for - // some reason; - return true; - } catch (IllegalStateException ex) { - // this means the zip file is closed... - return false; - } - - } - - public void closeSomeArchives(int n) { - for (int i = n - 1; i >= 0; i--) { - ZipFile zf = openArchives.get(i); - try { - zf.close(); - } catch (IOException e) { - e.printStackTrace(); - } - openArchives.remove(i); - } - } - - public void close() { - if (zipFile == null) - return; - try { - openArchives.remove(zipFile); - zipFile.close(); - } catch (IOException ioe) { - throw new BCException("Can't close archive: " + file.getName(), ioe); - } finally { - zipFile = null; - } - } - - @Override - public String toString() { - return file.getName(); - } - } - - /* private */static boolean hasClassExtension(String name) { - return name.toLowerCase().endsWith((".class")); - } - - public void closeArchives() { - for (Entry entry : entries) { - if (entry instanceof ZipFileEntry) { - ((ZipFileEntry) entry).close(); - } - openArchives.clear(); - } - } - - // Copes with the security manager - private static String getSystemPropertyWithoutSecurityException(String aPropertyName, String aDefaultValue) { - try { - return System.getProperty(aPropertyName, aDefaultValue); - } catch (SecurityException ex) { - return aDefaultValue; - } - } - - // Mainly exposed for testing - public List getEntries() { - return entries; - } - -} diff --git a/weaver/src/org/aspectj/weaver/bcel/ExceptionRange.java b/weaver/src/org/aspectj/weaver/bcel/ExceptionRange.java deleted file mode 100644 index 5e74627c3..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/ExceptionRange.java +++ /dev/null @@ -1,151 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - -package org.aspectj.weaver.bcel; - -import org.aspectj.apache.bcel.generic.InstructionHandle; -import org.aspectj.apache.bcel.generic.InstructionList; -import org.aspectj.weaver.UnresolvedType; - -/** - * exceptionRanges are set initially to be low priority. The various setPriority methods should be used accordingly. The priority is - * used when we pack the exception table into a method... the exception table should be sorted from high to low priority. Exceptions - * we generate for advice is either high priority (higher than anything coming from the original method... most kinds of - * non-execution advice) or low priority (lower than anything coming from the original method, for execution advice). - * - *

- * ??? This does not account for handler, or any other "statement-level" advice. When such statement level advice happens, we may - * want to go to a float level, so we can set the priority of advice to be lower than anything it encloses, and higher than anything - * enclosing it. - */ - -/* - * we're actually using the fact that we're an instruction targeter, for the handler - */ -public final class ExceptionRange extends Range { - - private InstructionHandle handler; - private final UnresolvedType exceptionType; - private final int priority; - - // ---- initialization - - /** - * After this constructor is called, this range is not well situated unless {@link #associateWithTargets} is called - * - * XXX priority should be fixed - */ - public ExceptionRange(InstructionList body, UnresolvedType exceptionType, int priority) { - super(body); - this.exceptionType = exceptionType; - this.priority = priority; - } - - /** - * @param insideExisting - */ - public ExceptionRange(InstructionList body, UnresolvedType exceptionType, boolean insideExisting) { - this(body, exceptionType, insideExisting ? Integer.MAX_VALUE : -1); - } - - public void associateWithTargets(InstructionHandle start, InstructionHandle end, InstructionHandle handler) { - // assert body.contains(start) && body.contains(end) && body.contains(handler) - this.start = start; - this.end = end; - this.handler = handler; - start.addTargeter(this); - end.addTargeter(this); - handler.addTargeter(this); - } - - // ---- - - public InstructionHandle getHandler() { - return handler; - } - - public UnresolvedType getCatchType() { - return exceptionType; - } - - public int getPriority() { - return priority; - } - - // ---- from object - - public String toString() { - String str; - if (exceptionType == null) { - str = "finally"; - } else { - str = "catch " + exceptionType; - } - // if (priority >= 0 && priority < Integer.MAX_VALUE) { - // str += " (priority " + priority + ")"; - // } - return str; - } - - public boolean equals(Object other) { - if (!(other instanceof ExceptionRange)) - return false; - ExceptionRange o = (ExceptionRange) other; - return o.getStart() == getStart() && o.getEnd() == getEnd() && o.handler == handler - && ((o.exceptionType == null) ? (exceptionType == null) : o.exceptionType.equals(exceptionType)) - && o.priority == priority; - } - - private volatile int hashCode = 0; - - public int hashCode() { - if (hashCode == 0) { - int ret = 17; - ret = 37 * ret + getStart().hashCode(); - ret = 37 * ret + getEnd().hashCode(); - ret = 37 * ret + handler.hashCode(); - ret = 37 * ret + ((exceptionType == null) ? 0 : exceptionType.hashCode()); - ret = 37 * ret + priority; - hashCode = ret; - } - return hashCode; - } - - public void updateTarget(InstructionHandle oldIh, InstructionHandle newIh, InstructionList newBody) { - super.updateTarget(oldIh, newIh, newBody); - // we're guaranteed that start, end, and handler are distinct instruction handles. - if (oldIh == handler) { - handler = newIh; - } - } - - public static boolean isExceptionStart(InstructionHandle ih) { - if (!isRangeHandle(ih)) - return false; - Range r = getRange(ih); - if (!(r instanceof ExceptionRange)) - return false; - ExceptionRange er = (ExceptionRange) r; - return er.getStart() == ih; - } - - public static boolean isExceptionEnd(InstructionHandle ih) { - if (!isRangeHandle(ih)) - return false; - Range r = getRange(ih); - if (!(r instanceof ExceptionRange)) - return false; - ExceptionRange er = (ExceptionRange) r; - return er.getEnd() == ih; - } - -} diff --git a/weaver/src/org/aspectj/weaver/bcel/ExtensibleURLClassLoader.java b/weaver/src/org/aspectj/weaver/bcel/ExtensibleURLClassLoader.java deleted file mode 100644 index 3480b8172..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/ExtensibleURLClassLoader.java +++ /dev/null @@ -1,113 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2004 IBM Corporation - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Matthew Webster, Adrian Colyer, - * Martin Lippert initial implementation - * Andy Clement - * Roy Varghese - Bug 473555 - * ******************************************************************/ - -package org.aspectj.weaver.bcel; - -import java.io.IOException; -import java.net.URL; -import java.net.URLClassLoader; -import java.security.CodeSource; - -import org.aspectj.util.FileUtil; -import org.aspectj.weaver.BCException; -import org.aspectj.weaver.UnresolvedType; - -public abstract class ExtensibleURLClassLoader extends URLClassLoader { - - private ClassPathManager classPath; - - public ExtensibleURLClassLoader(URL[] urls, ClassLoader parent) { - super(urls, parent); - - // System.err.println("? ExtensibleURLClassLoader.() path=" + WeavingAdaptor.makeClasspath(urls)); - try { - classPath = new ClassPathManager(FileUtil.makeClasspath(urls), null); - } catch (ExceptionInInitializerError ex) { - ex.printStackTrace(System.out); - throw ex; - } - } - - protected void addURL(URL url) { - super.addURL(url); // amc - this call was missing and is needed in - // WeavingURLClassLoader chains - classPath.addPath(url.getPath(), null); - } - - protected Class findClass(String name) throws ClassNotFoundException { - // System.err.println("? ExtensibleURLClassLoader.findClass(" + name + ")"); - try { - byte[] bytes = getBytes(name); - if (bytes != null) { - return defineClass(name, bytes); - } else { - throw new ClassNotFoundException(name); - } - } catch (IOException ex) { - throw new ClassNotFoundException(name); - } - } - - protected Class defineClass(String name, byte[] b, CodeSource cs) throws IOException { - // System.err.println("? ExtensibleURLClassLoader.defineClass(" + name + ",[" + b.length + "])"); - return defineClass(name, b, 0, b.length, cs); - } - - protected byte[] getBytes(String name) throws IOException { - byte[] b = null; - UnresolvedType unresolvedType = null; - try { - unresolvedType = UnresolvedType.forName(name); - } catch (BCException bce) { - if (bce.getMessage().indexOf("nameToSignature") == -1) { - bce.printStackTrace(System.err); - } - return null; - } - ClassPathManager.ClassFile classFile = classPath.find(unresolvedType); - if (classFile != null) { - try { - b = FileUtil.readAsByteArray(classFile.getInputStream()); - } finally { - classFile.close(); - } - } - return b; - } - - private Class defineClass(String name, byte[] bytes /* ClassPathManager.ClassFile classFile */) throws IOException { - String packageName = getPackageName(name); - if (packageName != null) { - Package pakkage = getPackage(packageName); - if (pakkage == null) { - definePackage(packageName, null, null, null, null, null, null, null); - } - } - - return defineClass(name, bytes, null); - } - - private String getPackageName(String className) { - int offset = className.lastIndexOf('.'); - return (offset == -1) ? null : className.substring(0, offset); - } - - @Override - public void close() throws IOException { - super.close(); - classPath.closeArchives(); - } - -} diff --git a/weaver/src/org/aspectj/weaver/bcel/FakeAnnotation.java b/weaver/src/org/aspectj/weaver/bcel/FakeAnnotation.java deleted file mode 100644 index 4194e66e9..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/FakeAnnotation.java +++ /dev/null @@ -1,80 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2005 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * initial implementation Andy Clement - *******************************************************************************/ -package org.aspectj.weaver.bcel; - -import java.io.DataOutputStream; -import java.io.IOException; -import java.util.List; - -import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; -import org.aspectj.apache.bcel.classfile.annotation.NameValuePair; - -/** - * For implementing declare @type interacting with declare @parents during compilation - we need to be able to add an annotation to - * 'binary type binding' (this is how types are seen during incremental compilation). Unlike a SourceTypeBinding - a - * BinaryTypeBinding does not allow easy interaction with its annotations - so what we do is take the eclipse annotation, suck out - * the name/signature and visibility and put that information in a 'FakeAnnotation'. The FakeAnnotation is attached to the BCEL - * delegate for the binary type binding - this will allow type resolution to succeed correctly. The FakeAnnotation never makes it to - * disk, since the weaver does the job properly, attaching a real annotation. - */ -public class FakeAnnotation extends AnnotationGen { - - private String name; - private String sig; - private boolean isRuntimeVisible; - - public FakeAnnotation(String name, String sig, boolean isRuntimeVisible) { - super(null, null, true, null); - this.name = name; - this.sig = sig; - this.isRuntimeVisible = isRuntimeVisible; - } - - public String getTypeName() { - return name; - } - - public String getTypeSignature() { - return sig; - } - - public void addElementNameValuePair(NameValuePair evp) { - // doesnt need to know about name/value pairs - } - - public void dump(DataOutputStream dos) throws IOException { - // should be serialized - } - - public int getTypeIndex() { - return 0; - } - - public List getValues() { - return null; - } - - public boolean isRuntimeVisible() { - return isRuntimeVisible; - } - - protected void setIsRuntimeVisible(boolean b) { - } - - public String toShortString() { - return "@" + this.name; - } - - public String toString() { - return this.name; - } -} diff --git a/weaver/src/org/aspectj/weaver/bcel/IfFinder.java b/weaver/src/org/aspectj/weaver/bcel/IfFinder.java deleted file mode 100644 index c66aa8939..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/IfFinder.java +++ /dev/null @@ -1,56 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2006 Contributors - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Andy Clement initial implementation - * ******************************************************************/ -package org.aspectj.weaver.bcel; - -import org.aspectj.weaver.patterns.AbstractPatternNodeVisitor; -import org.aspectj.weaver.patterns.AndPointcut; -import org.aspectj.weaver.patterns.IfPointcut; -import org.aspectj.weaver.patterns.NotPointcut; -import org.aspectj.weaver.patterns.OrPointcut; - -/** - * Look for an if() pointcut - */ -class IfFinder extends AbstractPatternNodeVisitor { - boolean hasIf = false; - - public Object visit(IfPointcut node, Object data) { - if (node.alwaysFalse() || node.alwaysTrue()) { - // IfFalse / IfTrue - } else { - hasIf = true; - } - return node; - } - - public Object visit(AndPointcut node, Object data) { - if (!hasIf) - node.getLeft().accept(this, data); - if (!hasIf) - node.getRight().accept(this, data); - return node; - } - - public Object visit(NotPointcut node, Object data) { - if (!hasIf) - node.getNegatedPointcut().accept(this, data); - return node; - } - - public Object visit(OrPointcut node, Object data) { - if (!hasIf) - node.getLeft().accept(this, data); - if (!hasIf) - node.getRight().accept(this, data); - return node; - } -} diff --git a/weaver/src/org/aspectj/weaver/bcel/LazyClassGen.java b/weaver/src/org/aspectj/weaver/bcel/LazyClassGen.java deleted file mode 100644 index 83189e1d4..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/LazyClassGen.java +++ /dev/null @@ -1,1940 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002-2010 Contributors - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * Andy Clement 6Jul05 generics - signature attribute - * Abraham Nevado - * ******************************************************************/ - -package org.aspectj.weaver.bcel; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; -import java.io.PrintStream; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.SortedMap; -import java.util.TreeMap; -import java.util.Vector; - -import org.aspectj.apache.bcel.Constants; -import org.aspectj.apache.bcel.classfile.Attribute; -import org.aspectj.apache.bcel.classfile.ConstantPool; -import org.aspectj.apache.bcel.classfile.Field; -import org.aspectj.apache.bcel.classfile.JavaClass; -import org.aspectj.apache.bcel.classfile.Method; -import org.aspectj.apache.bcel.classfile.Signature; -import org.aspectj.apache.bcel.classfile.Synthetic; -import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; -import org.aspectj.apache.bcel.generic.BasicType; -import org.aspectj.apache.bcel.generic.ClassGen; -import org.aspectj.apache.bcel.generic.FieldGen; -import org.aspectj.apache.bcel.generic.InstructionConstants; -import org.aspectj.apache.bcel.generic.InstructionFactory; -import org.aspectj.apache.bcel.generic.InstructionHandle; -import org.aspectj.apache.bcel.generic.InstructionList; -import org.aspectj.apache.bcel.generic.ObjectType; -import org.aspectj.apache.bcel.generic.Type; -import org.aspectj.bridge.IMessage; -import org.aspectj.bridge.ISourceLocation; -import org.aspectj.bridge.SourceLocation; -import org.aspectj.weaver.AjAttribute; -import org.aspectj.weaver.AjAttribute.WeaverState; -import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; -import org.aspectj.weaver.BCException; -import org.aspectj.weaver.Member; -import org.aspectj.weaver.MemberKind; -import org.aspectj.weaver.NameMangler; -import org.aspectj.weaver.ResolvedMember; -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.RuntimeVersion; -import org.aspectj.weaver.Shadow; -import org.aspectj.weaver.SignatureUtils; -import org.aspectj.weaver.TypeVariable; -import org.aspectj.weaver.UnresolvedType; -import org.aspectj.weaver.UnresolvedType.TypeKind; -import org.aspectj.weaver.WeaverMessages; -import org.aspectj.weaver.WeaverStateInfo; -import org.aspectj.weaver.World; -import org.aspectj.weaver.bcel.asm.AsmDetector; -import org.aspectj.weaver.bcel.asm.StackMapAdder; - -/** - * Lazy lazy lazy. We don't unpack the underlying class unless necessary. Things like new methods and annotations accumulate in here - * until they must be written out, don't add them to the underlying MethodGen! Things are slightly different if this represents an - * Aspect. - */ -public final class LazyClassGen { - - private static final Type[] ARRAY_7STRING_INT = new Type[] { Type.STRING, Type.STRING, Type.STRING, Type.STRING, Type.STRING, - Type.STRING, Type.STRING, Type.INT }; - - private static final Type[] ARRAY_8STRING_INT = new Type[] { Type.STRING, Type.STRING, Type.STRING, Type.STRING, Type.STRING, - Type.STRING, Type.STRING, Type.STRING, Type.INT }; - - private static final Type[] PARAMSIGNATURE_MAKESJP_METHOD = new Type[] { - Type.STRING, Type.INT, Type.STRING, Type.CLASS, Type.CLASS_ARRAY, Type.STRING_ARRAY, Type.CLASS_ARRAY, Type.CLASS, Type.INT - }; - - private static final Type[] PARAMSIGNATURE_MAKESJP_CONSTRUCTOR = new Type[] { - Type.STRING, Type.INT, Type.CLASS, Type.CLASS_ARRAY, Type.STRING_ARRAY, Type.CLASS_ARRAY, Type.INT - }; - - private static final Type[] PARAMSIGNATURE_MAKESJP_CATCHCLAUSE = new Type[] { - Type.STRING, Type.CLASS, Type.CLASS, Type.STRING, Type.INT - }; - - private static final Type[] PARAMSIGNATURE_MAKESJP_FIELD = new Type[] { - Type.STRING, Type.INT, Type.STRING, Type.CLASS, Type.CLASS, Type.INT - }; - - private static final Type[] PARAMSIGNATURE_MAKESJP_INITIALIZER = new Type[] { - Type.STRING, Type.INT, Type.CLASS, Type.INT - }; - - private static final Type[] PARAMSIGNATURE_MAKESJP_MONITOR = new Type[] { - Type.STRING, Type.CLASS, Type.INT - }; - - private static final Type[] PARAMSIGNATURE_MAKESJP_ADVICE = new Type[] { - Type.STRING, Type.INT, Type.STRING, Type.CLASS, Type.CLASS_ARRAY, Type.STRING_ARRAY, - Type.CLASS_ARRAY, Type.CLASS, Type.INT - }; - - - - - - private static final int ACC_SYNTHETIC = 0x1000; - - private static final String[] NO_STRINGS = new String[0]; - - int highestLineNumber = 0; // ---- JSR 45 info - - private final SortedMap inlinedFiles = new TreeMap(); - - private boolean regenerateGenericSignatureAttribute = false; - - private BcelObjectType myType; // XXX is not set for types we create - private ClassGen myGen; - private final ConstantPool cp; - private final World world; - private final String packageName = null; - - private final List fields = new ArrayList(); - private final List methodGens = new ArrayList(); - private final List classGens = new ArrayList(); - private final List annotations = new ArrayList(); - private int childCounter = 0; - - private final InstructionFactory fact; - - private boolean isSerializable = false; - private boolean hasSerialVersionUIDField = false; - private boolean serialVersionUIDRequiresInitialization = false; - private long calculatedSerialVersionUID; - private boolean hasClinit = false; - - private ResolvedType[] extraSuperInterfaces = null; - private ResolvedType superclass = null; - - // --- - - static class InlinedSourceFileInfo { - int highestLineNumber; - int offset; // calculated - - InlinedSourceFileInfo(int highestLineNumber) { - this.highestLineNumber = highestLineNumber; - } - } - - void addInlinedSourceFileInfo(String fullpath, int highestLineNumber) { - Object o = inlinedFiles.get(fullpath); - if (o != null) { - InlinedSourceFileInfo info = (InlinedSourceFileInfo) o; - if (info.highestLineNumber < highestLineNumber) { - info.highestLineNumber = highestLineNumber; - } - } else { - inlinedFiles.put(fullpath, new InlinedSourceFileInfo(highestLineNumber)); - } - } - - void calculateSourceDebugExtensionOffsets() { - int i = roundUpToHundreds(highestLineNumber); - for (InlinedSourceFileInfo element : inlinedFiles.values()) { - element.offset = i; - i = roundUpToHundreds(i + element.highestLineNumber); - } - } - - private static int roundUpToHundreds(int i) { - return ((i / 100) + 1) * 100; - } - - int getSourceDebugExtensionOffset(String fullpath) { - return inlinedFiles.get(fullpath).offset; - } - - // private Unknown getSourceDebugExtensionAttribute() { - // int nameIndex = cp.addUtf8("SourceDebugExtension"); - // String data = getSourceDebugExtensionString(); - // //System.err.println(data); - // byte[] bytes = Utility.stringToUTF(data); - // int length = bytes.length; - // - // return new Unknown(nameIndex, length, bytes, cp); - // } - - // private LazyClassGen() {} - // public static void main(String[] args) { - // LazyClassGen m = new LazyClassGen(); - // m.highestLineNumber = 37; - // m.inlinedFiles.put("boo/baz/foo.java", new InlinedSourceFileInfo( 83)); - // m.inlinedFiles.put("boo/barz/foo.java", new InlinedSourceFileInfo(292)); - // m.inlinedFiles.put("boo/baz/moo.java", new InlinedSourceFileInfo(128)); - // m.calculateSourceDebugExtensionOffsets(); - // System.err.println(m.getSourceDebugExtensionString()); - // } - - // For the entire pathname, we're using package names. This is probably - // wrong. - // private String getSourceDebugExtensionString() { - // StringBuffer out = new StringBuffer(); - // String myFileName = getFileName(); - // // header section - // out.append("SMAP\n"); - // out.append(myFileName); - // out.append("\nAspectJ\n"); - // // stratum section - // out.append("*S AspectJ\n"); - // // file section - // out.append("*F\n"); - // out.append("1 "); - // out.append(myFileName); - // out.append("\n"); - // int i = 2; - // for (Iterator iter = inlinedFiles.keySet().iterator(); iter.hasNext();) { - // String element = (String) iter.next(); - // int ii = element.lastIndexOf('/'); - // if (ii == -1) { - // out.append(i++); out.append(' '); - // out.append(element); out.append('\n'); - // } else { - // out.append("+ "); out.append(i++); out.append(' '); - // out.append(element.substring(ii+1)); out.append('\n'); - // out.append(element); out.append('\n'); - // } - // } - // // emit line section - // out.append("*L\n"); - // out.append("1#1,"); - // out.append(highestLineNumber); - // out.append(":1,1\n"); - // i = 2; - // for (Iterator iter = inlinedFiles.values().iterator(); iter.hasNext();) { - // InlinedSourceFileInfo element = (InlinedSourceFileInfo) iter.next(); - // out.append("1#"); - // out.append(i++); out.append(','); - // out.append(element.highestLineNumber); out.append(":"); - // out.append(element.offset + 1); out.append(",1\n"); - // } - // // end section - // out.append("*E\n"); - // // and finish up... - // return out.toString(); - // } - - // ---- end JSR45-related stuff - - /** Emit disassembled class and newline to out */ - public static void disassemble(String path, String name, PrintStream out) throws IOException { - if (null == out) { - return; - } - // out.println("classPath: " + classPath); - - BcelWorld world = new BcelWorld(path); - - UnresolvedType ut = UnresolvedType.forName(name); - ut.setNeedsModifiableDelegate(true); - LazyClassGen clazz = new LazyClassGen(BcelWorld.getBcelObjectType(world.resolve(ut))); - clazz.print(out); - out.println(); - } - - public String getNewGeneratedNameTag() { - return new Integer(childCounter++).toString(); - } - - // ---- - - public LazyClassGen(String class_name, String super_class_name, String file_name, int access_flags, String[] interfaces, - World world) { - myGen = new ClassGen(class_name, super_class_name, file_name, access_flags, interfaces); - cp = myGen.getConstantPool(); - fact = new InstructionFactory(myGen, cp); - regenerateGenericSignatureAttribute = true; - this.world = world; - } - - public void setMajorMinor(int major, int minor) { - myGen.setMajor(major); - myGen.setMinor(minor); - } - - public int getMajor() { - return myGen.getMajor(); - } - - public int getMinor() { - return myGen.getMinor(); - } - - // Non child type, so it comes from a real type in the world. - public LazyClassGen(BcelObjectType myType) { - myGen = new ClassGen(myType.getJavaClass()); - cp = myGen.getConstantPool(); - fact = new InstructionFactory(myGen, cp); - this.myType = myType; - world = myType.getResolvedTypeX().getWorld(); - - /* Does this class support serialization */ - if (implementsSerializable(getType())) { - isSerializable = true; - - // ResolvedMember[] fields = getType().getDeclaredFields(); - // for (int i = 0; i < fields.length; i++) { - // ResolvedMember field = fields[i]; - // if (field.getName().equals("serialVersionUID") - // && field.isStatic() && field.getType().equals(UnresolvedType.LONG)) - // { - // hasSerialVersionUIDField = true; - // } - // } - hasSerialVersionUIDField = hasSerialVersionUIDField(getType()); - - ResolvedMember[] methods = getType().getDeclaredMethods(); - for (int i = 0; i < methods.length; i++) { - ResolvedMember method = methods[i]; - if (method.getName().equals("")) { - if (method.getKind() != Member.STATIC_INITIALIZATION) { - throw new RuntimeException("qui?"); - } - hasClinit = true; - } - } - - // Do we need to calculate an SUID and add it? - if (!getType().isInterface() && !hasSerialVersionUIDField && world.isAddSerialVerUID()) { - calculatedSerialVersionUID = myGen.getSUID(); - FieldGen fg = new FieldGen(Constants.ACC_PRIVATE | Constants.ACC_FINAL | Constants.ACC_STATIC, BasicType.LONG, - "serialVersionUID", getConstantPool()); - addField(fg); - hasSerialVersionUIDField = true; - serialVersionUIDRequiresInitialization = true; - // warn about what we've done? - if (world.getLint().calculatingSerialVersionUID.isEnabled()) { - world.getLint().calculatingSerialVersionUID.signal( - new String[] { getClassName(), Long.toString(calculatedSerialVersionUID) + "L" }, null, null); - } - } - } - - ResolvedMember[] methods = myType.getDeclaredMethods(); - for (int i = 0; i < methods.length; i++) { - addMethodGen(new LazyMethodGen((BcelMethod) methods[i], this)); - } - - // Method[] methods = myGen.getMethods(); - // for (int i = 0; i < methods.length; i++) { - // addMethodGen(new LazyMethodGen(methods[i], this)); - // } - - ResolvedMember[] fields = myType.getDeclaredFields(); - for (int i = 0; i < fields.length; i++) { - this.fields.add((BcelField) fields[i]); - } - } - - public static boolean hasSerialVersionUIDField(ResolvedType type) { - - ResolvedMember[] fields = type.getDeclaredFields(); - for (int i = 0; i < fields.length; i++) { - ResolvedMember field = fields[i]; - if (field.getName().equals("serialVersionUID") && Modifier.isStatic(field.getModifiers()) - && field.getType().equals(UnresolvedType.LONG)) { - return true; - } - } - - return false; - } - - // public void addAttribute(Attribute i) { - // myGen.addAttribute(i); - // } - - // ---- - - public String getInternalClassName() { - return getConstantPool().getConstantString_CONSTANTClass(myGen.getClassNameIndex()); - // getConstantPool().getConstantString( - // myGen.getClassNameIndex(), - // Constants.CONSTANT_Class); - - } - - public String getInternalFileName() { - String str = getInternalClassName(); - int index = str.lastIndexOf('/'); - if (index == -1) { - return getFileName(); - } else { - return str.substring(0, index + 1) + getFileName(); - } - } - - /** - * Returns the packagename - if its the default package we return an empty string - */ - public String getPackageName() { - if (packageName != null) { - return packageName; - } - String str = getInternalClassName(); - int index = str.indexOf("<"); - if (index != -1) { - str = str.substring(0, index); // strip off the generics guff - } - index = str.lastIndexOf("/"); - if (index == -1) { - return ""; - } - return str.substring(0, index).replace('/', '.'); - } - - public void addMethodGen(LazyMethodGen gen) { - // assert gen.getClassName() == super.getClassName(); - methodGens.add(gen); - if (highestLineNumber < gen.highestLineNumber) { - highestLineNumber = gen.highestLineNumber; - } - } - - public boolean removeMethodGen(LazyMethodGen gen) { - return methodGens.remove(gen); - } - - public void addMethodGen(LazyMethodGen gen, ISourceLocation sourceLocation) { - addMethodGen(gen); - if (!gen.getMethod().isPrivate()) { - warnOnAddedMethod(gen.getMethod(), sourceLocation); - } - } - - public void errorOnAddedField(FieldGen field, ISourceLocation sourceLocation) { - if (isSerializable && !hasSerialVersionUIDField) { - getWorld().getLint().serialVersionUIDBroken.signal( - new String[] { myType.getResolvedTypeX().getName(), field.getName() }, sourceLocation, null); - } - } - - public void warnOnAddedInterface(String name, ISourceLocation sourceLocation) { - warnOnModifiedSerialVersionUID(sourceLocation, "added interface " + name); - } - - public void warnOnAddedMethod(Method method, ISourceLocation sourceLocation) { - warnOnModifiedSerialVersionUID(sourceLocation, "added non-private method " + method.getName()); - } - - public void warnOnAddedStaticInitializer(Shadow shadow, ISourceLocation sourceLocation) { - if (!hasClinit) { - warnOnModifiedSerialVersionUID(sourceLocation, "added static initializer"); - } - } - - public void warnOnModifiedSerialVersionUID(ISourceLocation sourceLocation, String reason) { - if (isSerializable && !hasSerialVersionUIDField) { - getWorld().getLint().needsSerialVersionUIDField.signal(new String[] { myType.getResolvedTypeX().getName().toString(), - reason }, sourceLocation, null); - } - } - - public World getWorld() { - return world; - } - - public List getMethodGens() { - return methodGens; // ???Collections.unmodifiableList(methodGens); - } - - public List getFieldGens() { - return fields; - } - - public boolean fieldExists(String name) { -// Field[] allFields = myGen.getFields(); -// if (allFields!=null) { -// for (int i=0;i Short.MAX_VALUE) { - reportClassTooBigProblem(); - return; - } - - if (annotations.size() > 0) { - for (AnnotationGen element : annotations) { - myGen.addAnnotation(element); - } - // Attribute[] annAttributes = - // org.aspectj.apache.bcel.classfile.Utility.getAnnotationAttributes( - // getConstantPool(),annotations); - // for (int i = 0; i < annAttributes.length; i++) { - // Attribute attribute = annAttributes[i]; - // System.err.println("Adding attribute for "+attribute); - // myGen.addAttribute(attribute); - // } - } - - // Add a weaver version attribute to the file being produced (if - // necessary...) - if (!myGen.hasAttribute("org.aspectj.weaver.WeaverVersion")) { - myGen.addAttribute(Utility.bcelAttribute(new AjAttribute.WeaverVersionInfo(), getConstantPool())); - } - - // see 389678: TODO more finessing possible here? - if (world.isOverWeaving()) { - if (myGen.hasAttribute(WeaverState.AttributeName) && myType!=null && myType.getWeaverState() != null) { - myGen.removeAttribute(myGen.getAttribute(WeaverState.AttributeName)); - myGen.addAttribute(Utility.bcelAttribute(new AjAttribute.WeaverState(myType.getWeaverState()), getConstantPool())); - } - } else { - if (!myGen.hasAttribute(WeaverState.AttributeName) && myType != null && myType.getWeaverState() != null) { - myGen.addAttribute(Utility.bcelAttribute(new AjAttribute.WeaverState(myType.getWeaverState()), getConstantPool())); - } - } - - // FIXME ATAJ needed only for slow Aspects.aspectOf() - keep or remove - // make a lot of test fail since the test compare weaved class file - // based on some test data as text files... - // if (!myGen.isInterface()) { - // addAjClassField(); - // } - - addAjcInitializers(); - - // 17Feb05 - ASC - Skip this for now - it crashes IBM 1.4.2 jvms - // (pr80430). Will be revisited when contents - // of attribute are confirmed to be correct. - boolean sourceDebugExtensionSupportSwitchedOn = false; - - if (sourceDebugExtensionSupportSwitchedOn) { - calculateSourceDebugExtensionOffsets(); - } - - int len = methodGens.size(); - myGen.setMethods(Method.NoMethods); - - for (LazyMethodGen gen : methodGens) { - // we skip empty clinits - if (isEmptyClinit(gen)) { - continue; - } - myGen.addMethod(gen.getMethod()); - } - - len = fields.size(); - myGen.setFields(Field.NoFields); - for (int i = 0; i < len; i++) { - BcelField gen = fields.get(i); - myGen.addField(gen.getField(cp)); - } - - if (sourceDebugExtensionSupportSwitchedOn) { - if (inlinedFiles.size() != 0) { - if (hasSourceDebugExtensionAttribute(myGen)) { - world.showMessage(IMessage.WARNING, WeaverMessages.format(WeaverMessages.OVERWRITE_JSR45, getFileName()), null, - null); - } - // myGen.addAttribute(getSourceDebugExtensionAttribute()); - } - } - - fixupGenericSignatureAttribute(); - } - - /** - * When working with Java generics, a signature attribute is attached to the type which indicates how it was declared. This - * routine ensures the signature attribute for the class we are about to write out is correct. Basically its responsibilities - * are: - *

    - *
  1. - * Checking whether the attribute needs changing (ie. did weaving change the type hierarchy) - if it did, remove the old - * attribute - *
  2. - * Check if we need an attribute at all, are we generic? are our supertypes parameterized/generic? - *
  3. - * Build the new attribute which includes all typevariable, supertype and superinterface information - *
- */ - private void fixupGenericSignatureAttribute() { - - if (getWorld() != null && !getWorld().isInJava5Mode()) { - return; - } - - // TODO asc generics Temporarily assume that types we generate dont need - // a signature attribute (closure/etc).. will need - // revisiting no doubt... - // if (myType == null) { - // return; - // } - - // 1. Has anything changed that would require us to modify this - // attribute? - if (!regenerateGenericSignatureAttribute) { - return; - } - - // 2. Find the old attribute - Signature sigAttr = null; - if (myType != null) { // if null, this is a type built from scratch, it - // won't already have a sig attribute - sigAttr = (Signature) myGen.getAttribute("Signature"); - } - - // 3. Do we need an attribute? - boolean needAttribute = false; - // If we had one before, we definetly still need one as types can't be - // 'removed' from the hierarchy - if (sigAttr != null) { - needAttribute = true; - } - - // check the interfaces - if (!needAttribute) { - if (myType != null) { - ResolvedType[] interfaceRTXs = myType.getDeclaredInterfaces(); - for (int i = 0; i < interfaceRTXs.length; i++) { - ResolvedType typeX = interfaceRTXs[i]; - if (typeX.isGenericType() || typeX.isParameterizedType()) { - needAttribute = true; - } - } - if (extraSuperInterfaces != null) { - for (int i = 0; i < extraSuperInterfaces.length; i++) { - ResolvedType interfaceType = extraSuperInterfaces[i]; - if (interfaceType.isGenericType() || interfaceType.isParameterizedType()) { - needAttribute = true; - } - } - } - } - - if (myType == null) { - ResolvedType superclassRTX = superclass; - if (superclassRTX != null) { - if (superclassRTX.isGenericType() || superclassRTX.isParameterizedType()) { - needAttribute = true; - } - } - } else { - // check the supertype - ResolvedType superclassRTX = getSuperClass(); - if (superclassRTX.isGenericType() || superclassRTX.isParameterizedType()) { - needAttribute = true; - } - } - } - - if (needAttribute) { - StringBuffer signature = new StringBuffer(); - // first, the type variables... - if (myType != null) { - TypeVariable[] tVars = myType.getTypeVariables(); - if (tVars.length > 0) { - signature.append("<"); - for (int i = 0; i < tVars.length; i++) { - TypeVariable variable = tVars[i]; - signature.append(variable.getSignatureForAttribute()); - } - signature.append(">"); - } - } - // now the supertype - String supersig = getSuperClass().getSignatureForAttribute(); - signature.append(supersig); - if (myType != null) { - ResolvedType[] interfaceRTXs = myType.getDeclaredInterfaces(); - for (int i = 0; i < interfaceRTXs.length; i++) { - String s = interfaceRTXs[i].getSignatureForAttribute(); - signature.append(s); - } - if (extraSuperInterfaces != null) { - for (int i = 0; i < extraSuperInterfaces.length; i++) { - String s = extraSuperInterfaces[i].getSignatureForAttribute(); - signature.append(s); - } - } - } - if (sigAttr != null) { - myGen.removeAttribute(sigAttr); - } - myGen.addAttribute(createSignatureAttribute(signature.toString())); - } - } - - /** - * Helper method to create a signature attribute based on a string signature: e.g. "Ljava/lang/Object;LI;" - */ - private Signature createSignatureAttribute(String signature) { - int nameIndex = cp.addUtf8("Signature"); - int sigIndex = cp.addUtf8(signature); - return new Signature(nameIndex, 2, sigIndex, cp); - } - - /** - * - */ - private void reportClassTooBigProblem() { - // PR 59208 - // we've generated a class that is just toooooooooo big (you've been - // generating programs - // again haven't you? come on, admit it, no-one writes classes this big - // by hand). - // create an empty myGen so that we can give back a return value that - // doesn't upset the - // rest of the process. - myGen = new ClassGen(myGen.getClassName(), myGen.getSuperclassName(), myGen.getFileName(), myGen.getModifiers(), - myGen.getInterfaceNames()); - // raise an error against this compilation unit. - getWorld().showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.CLASS_TOO_BIG, this.getClassName()), - new SourceLocation(new File(myGen.getFileName()), 0), null); - } - - private static boolean hasSourceDebugExtensionAttribute(ClassGen gen) { - return gen.hasAttribute("SourceDebugExtension"); - } - - public JavaClass getJavaClass(BcelWorld world) { - writeBack(world); - return myGen.getJavaClass(); - } - - public byte[] getJavaClassBytesIncludingReweavable(BcelWorld world) { - writeBack(world); - byte[] wovenClassFileData = myGen.getJavaClass().getBytes(); - // At 1.6 stackmaps are optional, whilst at 1.7 and later they - // are required (unless turning off the verifier) - if ((myGen.getMajor() == Constants.MAJOR_1_6 && world.shouldGenerateStackMaps()) || myGen.getMajor() > Constants.MAJOR_1_6) { - if (!AsmDetector.isAsmAround) { - throw new BCException("Unable to find Asm for stackmap generation (Looking for 'aj.org.objectweb.asm.ClassReader'). Stackmap generation for woven code is required to avoid verify errors on a Java 1.7 or higher runtime"); - }; - wovenClassFileData = StackMapAdder.addStackMaps(world, wovenClassFileData); - } - - WeaverStateInfo wsi = myType.getWeaverState();// getOrCreateWeaverStateInfo(); - if (wsi != null && wsi.isReweavable() && !world.isOverWeaving()) { // && !reweavableDataInserted - // reweavableDataInserted = true; - return wsi.replaceKeyWithDiff(wovenClassFileData); - } else { - return wovenClassFileData; - } - } - - public void addGeneratedInner(LazyClassGen newClass) { - classGens.add(newClass); - } - - public void addInterface(ResolvedType newInterface, ISourceLocation sourceLocation) { - regenerateGenericSignatureAttribute = true; - - if (extraSuperInterfaces == null) { - extraSuperInterfaces = new ResolvedType[1]; - extraSuperInterfaces[0] = newInterface; - } else { - ResolvedType[] x = new ResolvedType[extraSuperInterfaces.length + 1]; - System.arraycopy(extraSuperInterfaces, 0, x, 1, extraSuperInterfaces.length); - x[0] = newInterface; - extraSuperInterfaces = x; - } - myGen.addInterface(newInterface.getRawName()); - if (!newInterface.equals(UnresolvedType.SERIALIZABLE)) { - warnOnAddedInterface(newInterface.getName(), sourceLocation); - } - } - - public void setSuperClass(ResolvedType newSuperclass) { - regenerateGenericSignatureAttribute = true; - superclass = newSuperclass; - // myType.addParent(typeX); // used for the attribute - if (newSuperclass.getGenericType() != null) { - newSuperclass = newSuperclass.getGenericType(); - } - myGen.setSuperclassName(newSuperclass.getName()); // used in the real - // class data - } - - // public String getSuperClassname() { - // return myGen.getSuperclassName(); - // } - - public ResolvedType getSuperClass() { - if (superclass != null) { - return superclass; - } - return myType.getSuperclass(); - } - - public String[] getInterfaceNames() { - return myGen.getInterfaceNames(); - } - - // non-recursive, may be a bug, ha ha. - private List getClassGens() { - List ret = new ArrayList(); - ret.add(this); - ret.addAll(classGens); - return ret; - } - - public List getChildClasses(BcelWorld world) { - if (classGens.isEmpty()) { - return Collections.emptyList(); - } - List ret = new ArrayList(); - for (LazyClassGen clazz : classGens) { - byte[] bytes = clazz.getJavaClass(world).getBytes(); - String name = clazz.getName(); - int index = name.lastIndexOf('$'); - // XXX this could be bad, check use of dollar signs. - name = name.substring(index + 1); - ret.add(new UnwovenClassFile.ChildClass(name, bytes)); - } - return ret; - } - - @Override - public String toString() { - return toShortString(); - } - - public String toShortString() { - String s = org.aspectj.apache.bcel.classfile.Utility.accessToString(myGen.getModifiers(), true); - if (!s.equals("")) { - s += " "; - } - s += org.aspectj.apache.bcel.classfile.Utility.classOrInterface(myGen.getModifiers()); - s += " "; - s += myGen.getClassName(); - return s; - } - - public String toLongString() { - ByteArrayOutputStream s = new ByteArrayOutputStream(); - print(new PrintStream(s)); - return new String(s.toByteArray()); - } - - public void print() { - print(System.out); - } - - public void print(PrintStream out) { - List classGens = getClassGens(); - for (Iterator iter = classGens.iterator(); iter.hasNext();) { - LazyClassGen element = iter.next(); - element.printOne(out); - if (iter.hasNext()) { - out.println(); - } - } - } - - private void printOne(PrintStream out) { - out.print(toShortString()); - out.print(" extends "); - out.print(org.aspectj.apache.bcel.classfile.Utility.compactClassName(myGen.getSuperclassName(), false)); - - int size = myGen.getInterfaces().length; - - if (size > 0) { - out.print(" implements "); - for (int i = 0; i < size; i++) { - out.print(myGen.getInterfaceNames()[i]); - if (i < size - 1) { - out.print(", "); - } - } - } - out.print(":"); - out.println(); - // XXX make sure to pass types correctly around, so this doesn't happen. - if (myType != null) { - myType.printWackyStuff(out); - } - Field[] fields = myGen.getFields(); - for (int i = 0, len = fields.length; i < len; i++) { - out.print(" "); - out.println(fields[i]); - } - List methodGens = getMethodGens(); - for (Iterator iter = methodGens.iterator(); iter.hasNext();) { - LazyMethodGen gen = iter.next(); - // we skip empty clinits - if (isEmptyClinit(gen)) { - continue; - } - gen.print(out, (myType != null ? myType.getWeaverVersionAttribute() : WeaverVersionInfo.UNKNOWN)); - if (iter.hasNext()) { - out.println(); - } - } - // out.println(" ATTRIBS: " + Arrays.asList(myGen.getAttributes())); - - out.println("end " + toShortString()); - } - - private boolean isEmptyClinit(LazyMethodGen gen) { - - if (!gen.getName().equals("")) { - return false; - } - // System.err.println("checking clinig: " + gen); - InstructionHandle start = gen.getBody().getStart(); - while (start != null) { - if (Range.isRangeHandle(start) || (start.getInstruction().opcode == Constants.RETURN)) { - start = start.getNext(); - } else { - return false; - } - } - - return true; - } - - public ConstantPool getConstantPool() { - return cp; - } - - public String getName() { - return myGen.getClassName(); - } - - public boolean isWoven() { - return myType.getWeaverState() != null; - } - - public boolean isReweavable() { - if (myType.getWeaverState() == null) { - return true; - } - return myType.getWeaverState().isReweavable(); - } - - public Set getAspectsAffectingType() { - if (myType.getWeaverState() == null) { - return null; - } - return myType.getWeaverState().getAspectsAffectingType(); - } - - public WeaverStateInfo getOrCreateWeaverStateInfo(boolean inReweavableMode) { - WeaverStateInfo ret = myType.getWeaverState(); - if (ret != null) { - return ret; - } - ret = new WeaverStateInfo(inReweavableMode); - myType.setWeaverState(ret); - return ret; - } - - public InstructionFactory getFactory() { - return fact; - } - - public LazyMethodGen getStaticInitializer() { - for (LazyMethodGen gen : methodGens) { - // OPTIMIZE persist kind of member into the gen object? for clinit - if (gen.getName().equals("")) { - return gen; - } - } - LazyMethodGen clinit = new LazyMethodGen(Modifier.STATIC, Type.VOID, "", new Type[0], NO_STRINGS, this); - clinit.getBody().insert(InstructionConstants.RETURN); - methodGens.add(clinit); - return clinit; - } - - /** - * Retrieve the ajc$preClinit method - this method captures any initialization AspectJ wants to ensure happens in a class. It is - * called from the static initializer. Maintaining this separation enables overweaving to ignore join points added due to - * earlier weaves. If the ajc$preClinit method cannot be found, it is created and a call to it is placed in the real static - * initializer (the call is placed at the start of the static initializer). - * - * @return the LazyMethodGen representing the ajc$ clinit - */ - public LazyMethodGen getAjcPreClinit() { - if (this.isInterface()) { - throw new IllegalStateException(); - } - for (LazyMethodGen methodGen : methodGens) { - if (methodGen.getName().equals(NameMangler.AJC_PRE_CLINIT_NAME)) { - return methodGen; - } - } - LazyMethodGen ajcPreClinit = new LazyMethodGen(Modifier.PRIVATE | Modifier.STATIC, Type.VOID, - NameMangler.AJC_PRE_CLINIT_NAME, Type.NO_ARGS, NO_STRINGS, this); - ajcPreClinit.getBody().insert(InstructionConstants.RETURN); - methodGens.add(ajcPreClinit); - getStaticInitializer().getBody().insert(Utility.createInvoke(fact, ajcPreClinit)); - return ajcPreClinit; - } - - /** - * factory method for building multiple extended clinit methods. Constructs a new clinit method that invokes the previous one - * and then returns it. The index is used as a name suffix. - * - * @param previousPreClinit - * @param i - */ - public LazyMethodGen createExtendedAjcPreClinit(LazyMethodGen previousPreClinit, int i) { - LazyMethodGen ajcPreClinit = new LazyMethodGen(Modifier.PRIVATE | Modifier.STATIC, Type.VOID, - NameMangler.AJC_PRE_CLINIT_NAME + i, Type.NO_ARGS, NO_STRINGS, this); - ajcPreClinit.getBody().insert(InstructionConstants.RETURN); - methodGens.add(ajcPreClinit); - previousPreClinit.getBody().insert(Utility.createInvoke(fact, ajcPreClinit)); - return ajcPreClinit; - } - - // - - // reflective thisJoinPoint support - private Map tjpFields = new HashMap(); - Map annotationCachingFieldCache = new HashMap(); - private int tjpFieldsCounter = -1; // -1 means not yet initialized - private int annoFieldsCounter = 0; - public static final ObjectType proceedingTjpType = new ObjectType("org.aspectj.lang.ProceedingJoinPoint"); - public static final ObjectType tjpType = new ObjectType("org.aspectj.lang.JoinPoint"); - public static final ObjectType staticTjpType = new ObjectType("org.aspectj.lang.JoinPoint$StaticPart"); - public static final ObjectType typeForAnnotation = new ObjectType("java.lang.annotation.Annotation"); - public static final ObjectType enclosingStaticTjpType = new ObjectType("org.aspectj.lang.JoinPoint$EnclosingStaticPart"); - private static final ObjectType sigType = new ObjectType("org.aspectj.lang.Signature"); - // private static final ObjectType slType = - // new ObjectType("org.aspectj.lang.reflect.SourceLocation"); - private static final ObjectType factoryType = new ObjectType("org.aspectj.runtime.reflect.Factory"); - private static final ObjectType classType = new ObjectType("java.lang.Class"); - - public Field getTjpField(BcelShadow shadow, final boolean isEnclosingJp) { - Field tjpField = tjpFields.get(shadow); - if (tjpField != null) { - return tjpField; - } - - int modifiers = Modifier.STATIC; - - // J9: Can't always be final on Java 9 because it is set outside of clinit - // But must be final in interface - if (shadow.getEnclosingClass().isInterface()) { - modifiers |= Modifier.FINAL; - } - - // XXX - Do we ever inline before or after advice? If we do, then we - // better include them in the check below. (or just change it to - // shadow.getEnclosingMethod().getCanInline()) - - // If the enclosing method is around advice, we could inline the join - // point that has led to this shadow. If we do that then the TJP we are - // creating here must be PUBLIC so it is visible to the type in which the - // advice is inlined. (PR71377) - LazyMethodGen encMethod = shadow.getEnclosingMethod(); - boolean shadowIsInAroundAdvice = false; - if (encMethod != null && encMethod.getName().startsWith(NameMangler.PREFIX + "around")) { - shadowIsInAroundAdvice = true; - } - - if (getType().isInterface() || shadowIsInAroundAdvice) { - modifiers |= Modifier.PUBLIC; - } else { - modifiers |= Modifier.PRIVATE; - } - ObjectType jpType = null; - // Did not have different static joinpoint types in 1.2 - if (world.isTargettingAspectJRuntime12()) { - jpType = staticTjpType; - } else { - jpType = isEnclosingJp ? enclosingStaticTjpType : staticTjpType; - } - if (tjpFieldsCounter == -1) { - // not yet initialized, do it now - if (!world.isOverWeaving()) { - tjpFieldsCounter = 0; - } else { - List existingFields = getFieldGens(); - if (existingFields == null) { - tjpFieldsCounter = 0; - } else { - BcelField lastField = null; - // OPTIMIZE: go from last to first? - for (BcelField field : existingFields) { - if (field.getName().startsWith("ajc$tjp_")) { - lastField = field; - } - } - if (lastField == null) { - tjpFieldsCounter = 0; - } else { - tjpFieldsCounter = Integer.parseInt(lastField.getName().substring(8)) + 1; - // System.out.println("tjp counter starting at " + tjpFieldsCounter); - } - } - } - } - if (!isInterface() && world.isTransientTjpFields()) { - modifiers|=Modifier.TRANSIENT; - } - FieldGen fGen = new FieldGen(modifiers, jpType, "ajc$tjp_" + tjpFieldsCounter++, getConstantPool()); - addField(fGen); - tjpField = fGen.getField(); - tjpFields.put(shadow, tjpField); - return tjpField; - } - - /** - * Create a field in the type containing the shadow where the annotation retrieved during binding can be stored - for later fast - * access. - * - * @param shadow the shadow at which the @annotation result is being cached - * @return a field - */ - public Field getAnnotationCachingField(BcelShadow shadow, ResolvedType toType, boolean isWithin) { - // Multiple annotation types at a shadow. A different field would be required for each - CacheKey cacheKey = new CacheKey(shadow, toType, isWithin); - Field field = annotationCachingFieldCache.get(cacheKey); - // System.out.println(field + " for shadow " + shadow); - if (field == null) { - // private static Annotation ajc$anno$ - StringBuilder sb = new StringBuilder(); - sb.append(NameMangler.ANNOTATION_CACHE_FIELD_NAME); - sb.append(annoFieldsCounter++); - FieldGen annotationCacheField = new FieldGen(Modifier.PRIVATE | Modifier.STATIC, typeForAnnotation, sb.toString(), cp); - addField(annotationCacheField); - field = annotationCacheField.getField(); - annotationCachingFieldCache.put(cacheKey, field); - } - return field; - } - - static class CacheKey { - private Object key; - private ResolvedType annotationType; - - // If the annotation is being accessed via @annotation on a shadow then we can use the shadows toString() (so two shadows - // the same share a variable), but if it is @withincode() or @within() we can't share them (as the shadows may look the same - // but be occurring 'within' different things). In the within cases we continue to use the shadow itself as the key. - CacheKey(BcelShadow shadow, ResolvedType annotationType, boolean isWithin) { - this.key = isWithin ? shadow : shadow.toString(); - this.annotationType = annotationType; - } - - @Override - public int hashCode() { - return key.hashCode() * 37 + annotationType.hashCode(); - } - - @Override - public boolean equals(Object other) { - if (!(other instanceof CacheKey)) { - return false; - } - CacheKey oCacheKey = (CacheKey) other; - return key.equals(oCacheKey.key) && annotationType.equals(oCacheKey.annotationType); - } - } - - // FIXME ATAJ needed only for slow Aspects.aspectOf - keep or remove - // private void addAjClassField() { - // // Andy: Why build it again?? - // Field ajClassField = new FieldGen( - // Modifier.PRIVATE | Modifier.FINAL | Modifier.STATIC, - // classType, - // "aj$class", - // getConstantPool()).getField(); - // addField(ajClassField); - // - // InstructionList il = new InstructionList(); - // il.append(new PUSH(getConstantPool(), getClassName())); - // il.append(fact.createInvoke("java.lang.Class", "forName", classType, - // new Type[] {Type.STRING}, Constants.INVOKESTATIC)); - // il.append(fact.createFieldAccess(getClassName(), ajClassField.getName(), - // classType, Constants.PUTSTATIC)); - // - // getStaticInitializer().getBody().insert(il); - // } - - private void addAjcInitializers() { - if (tjpFields.size() == 0 && !serialVersionUIDRequiresInitialization) { - return; - } - InstructionList[] il = null; - - if (tjpFields.size() > 0) { - il = initializeAllTjps(); - } - - if (serialVersionUIDRequiresInitialization) { - InstructionList[] ilSVUID = new InstructionList[1]; - ilSVUID[0] = new InstructionList(); - ilSVUID[0].append(InstructionFactory.PUSH(getConstantPool(), calculatedSerialVersionUID)); - ilSVUID[0].append(getFactory().createFieldAccess(getClassName(), "serialVersionUID", BasicType.LONG, - Constants.PUTSTATIC)); - if (il == null) { - il = ilSVUID; - } else { - InstructionList[] newIl = new InstructionList[il.length + ilSVUID.length]; - System.arraycopy(il, 0, newIl, 0, il.length); - System.arraycopy(ilSVUID, 0, newIl, il.length, ilSVUID.length); - il = newIl; - } - } - - LazyMethodGen prevMethod; - LazyMethodGen nextMethod = null; - if (this.isInterface()) { // Cannot sneak stuff into another static method in an interface - prevMethod = getStaticInitializer(); - } else { - prevMethod = getAjcPreClinit(); - } - for (int counter = 1; counter <= il.length; counter++) { - if (il.length > counter) { - nextMethod = createExtendedAjcPreClinit(prevMethod, counter); - } - prevMethod.getBody().insert(il[counter - 1]); - prevMethod = nextMethod; - } - } - - private InstructionList initInstructionList() { - InstructionList list = new InstructionList(); - InstructionFactory fact = getFactory(); - - // make a new factory - list.append(fact.createNew(factoryType)); - list.append(InstructionFactory.createDup(1)); - - list.append(InstructionFactory.PUSH(getConstantPool(), getFileName())); - - // load the current Class object - // XXX check that this works correctly for inners/anonymous - list.append(fact.PUSHCLASS(cp, myGen.getClassName())); - // XXX do we need to worry about the fact the theorectically this could - // throw - // a ClassNotFoundException - - list.append(fact.createInvoke(factoryType.getClassName(), "", Type.VOID, new Type[] { Type.STRING, classType }, - Constants.INVOKESPECIAL)); - - list.append(InstructionFactory.createStore(factoryType, 0)); - return list; - } - - private InstructionList[] initializeAllTjps() { - Vector lists = new Vector(); - - InstructionList list = initInstructionList(); - lists.add(list); - - List> entries = new ArrayList>(tjpFields.entrySet()); - Collections.sort(entries, new Comparator>() { - @Override - public int compare(Map.Entry a, Map.Entry b) { - return (a.getValue()).getName().compareTo((b.getValue()).getName()); - } - }); - - long estimatedSize = 0; - for (Iterator> i = entries.iterator(); i.hasNext();) { - Map.Entry entry = i.next(); - if (estimatedSize > Constants.MAX_CODE_SIZE) { - estimatedSize = 0; - list = initInstructionList(); - lists.add(list); - } - estimatedSize += entry.getValue().getSignature().getBytes().length; - initializeTjp(fact, list, entry.getValue(), entry.getKey()); - } - InstructionList listArrayModel[] = new InstructionList[1]; - return lists.toArray(listArrayModel); - } - - private void initializeTjp(InstructionFactory fact, InstructionList list, Field field, BcelShadow shadow) { - if (world.getTargetAspectjRuntimeLevel() == RuntimeVersion.V1_9) { - initializeTjpOptimal(fact, list, field, shadow); - return; - } - boolean fastSJP = false; - // avoid fast SJP if it is for an enclosing joinpoint - boolean isFastSJPAvailable = shadow.getWorld().isTargettingRuntime1_6_10() - && !enclosingStaticTjpType.equals(field.getType()); - - Member sig = shadow.getSignature(); - - // load the factory - list.append(InstructionFactory.createLoad(factoryType, 0)); - - // load the kind - list.append(InstructionFactory.PUSH(getConstantPool(), shadow.getKind().getName())); - - // create the signature - if (world.isTargettingAspectJRuntime12() || !isFastSJPAvailable || !sig.getKind().equals(Member.METHOD)) { - list.append(InstructionFactory.createLoad(factoryType, 0)); - } - - String signatureMakerName = SignatureUtils.getSignatureMakerName(sig); - ObjectType signatureType = new ObjectType(SignatureUtils.getSignatureType(sig)); - UnresolvedType[] exceptionTypes = null; - if (world.isTargettingAspectJRuntime12()) { // TAG:SUPPORTING12: We didn't have optimized factory methods in 1.2 - list.append(InstructionFactory.PUSH(cp, SignatureUtils.getSignatureString(sig, shadow.getWorld()))); - list.append(fact.createInvoke(factoryType.getClassName(), signatureMakerName, signatureType, Type.STRINGARRAY1, - Constants.INVOKEVIRTUAL)); - } else if (sig.getKind().equals(Member.METHOD)) { - BcelWorld w = shadow.getWorld(); - - // For methods, push the parts of the signature on. - list.append(InstructionFactory.PUSH(cp, makeString(sig.getModifiers(w)))); - list.append(InstructionFactory.PUSH(cp, sig.getName())); - list.append(InstructionFactory.PUSH(cp, makeString(sig.getDeclaringType()))); - list.append(InstructionFactory.PUSH(cp, makeString(sig.getParameterTypes()))); - list.append(InstructionFactory.PUSH(cp, makeString(sig.getParameterNames(w)))); - exceptionTypes = sig.getExceptions(w); - if (isFastSJPAvailable && exceptionTypes.length == 0) { - fastSJP = true; - } else { - list.append(InstructionFactory.PUSH(cp, makeString(exceptionTypes))); - } - list.append(InstructionFactory.PUSH(cp, makeString(sig.getReturnType()))); - // And generate a call to the variant of makeMethodSig() that takes the strings - if (isFastSJPAvailable) { - fastSJP = true; - } else { - list.append(fact.createInvoke(factoryType.getClassName(), signatureMakerName, signatureType, Type.STRINGARRAY7, - Constants.INVOKEVIRTUAL)); - } - - } else if (sig.getKind().equals(Member.MONITORENTER)) { - list.append(InstructionFactory.PUSH(cp, makeString(sig.getDeclaringType()))); - list.append(fact.createInvoke(factoryType.getClassName(), signatureMakerName, signatureType, Type.STRINGARRAY1, - Constants.INVOKEVIRTUAL)); - } else if (sig.getKind().equals(Member.MONITOREXIT)) { - list.append(InstructionFactory.PUSH(cp, makeString(sig.getDeclaringType()))); - list.append(fact.createInvoke(factoryType.getClassName(), signatureMakerName, signatureType, Type.STRINGARRAY1, - Constants.INVOKEVIRTUAL)); - } else if (sig.getKind().equals(Member.HANDLER)) { - BcelWorld w = shadow.getWorld(); - list.append(InstructionFactory.PUSH(cp, makeString(sig.getDeclaringType()))); - list.append(InstructionFactory.PUSH(cp, makeString(sig.getParameterTypes()))); - list.append(InstructionFactory.PUSH(cp, makeString(sig.getParameterNames(w)))); - list.append(fact.createInvoke(factoryType.getClassName(), signatureMakerName, signatureType, Type.STRINGARRAY3, - Constants.INVOKEVIRTUAL)); - } else if (sig.getKind().equals(Member.CONSTRUCTOR)) { - BcelWorld w = shadow.getWorld(); - if (w.isJoinpointArrayConstructionEnabled() && sig.getDeclaringType().isArray()) { - // its the magical new jp - list.append(InstructionFactory.PUSH(cp, makeString(Modifier.PUBLIC))); - list.append(InstructionFactory.PUSH(cp, makeString(sig.getDeclaringType()))); - list.append(InstructionFactory.PUSH(cp, makeString(sig.getParameterTypes()))); - list.append(InstructionFactory.PUSH(cp, "")); // sig.getParameterNames? - list.append(InstructionFactory.PUSH(cp, ""));// sig.getExceptions? - list.append(fact.createInvoke(factoryType.getClassName(), signatureMakerName, signatureType, Type.STRINGARRAY5, - Constants.INVOKEVIRTUAL)); - } else { - list.append(InstructionFactory.PUSH(cp, makeString(sig.getModifiers(w)))); - list.append(InstructionFactory.PUSH(cp, makeString(sig.getDeclaringType()))); - list.append(InstructionFactory.PUSH(cp, makeString(sig.getParameterTypes()))); - list.append(InstructionFactory.PUSH(cp, makeString(sig.getParameterNames(w)))); - list.append(InstructionFactory.PUSH(cp, makeString(sig.getExceptions(w)))); - list.append(fact.createInvoke(factoryType.getClassName(), signatureMakerName, signatureType, Type.STRINGARRAY5, - Constants.INVOKEVIRTUAL)); - } - } else if (sig.getKind().equals(Member.FIELD)) { - BcelWorld w = shadow.getWorld(); - list.append(InstructionFactory.PUSH(cp, makeString(sig.getModifiers(w)))); - list.append(InstructionFactory.PUSH(cp, sig.getName())); - // see pr227401 - UnresolvedType dType = sig.getDeclaringType(); - if (dType.getTypekind() == TypeKind.PARAMETERIZED || dType.getTypekind() == TypeKind.GENERIC) { - dType = sig.getDeclaringType().resolve(world).getGenericType(); - } - list.append(InstructionFactory.PUSH(cp, makeString(dType))); - list.append(InstructionFactory.PUSH(cp, makeString(sig.getReturnType()))); - list.append(fact.createInvoke(factoryType.getClassName(), signatureMakerName, signatureType, Type.STRINGARRAY4, - Constants.INVOKEVIRTUAL)); - } else if (sig.getKind().equals(Member.ADVICE)) { - BcelWorld w = shadow.getWorld(); - list.append(InstructionFactory.PUSH(cp, makeString(sig.getModifiers(w)))); - list.append(InstructionFactory.PUSH(cp, sig.getName())); - list.append(InstructionFactory.PUSH(cp, makeString(sig.getDeclaringType()))); - list.append(InstructionFactory.PUSH(cp, makeString(sig.getParameterTypes()))); - list.append(InstructionFactory.PUSH(cp, makeString(sig.getParameterNames(w)))); - list.append(InstructionFactory.PUSH(cp, makeString(sig.getExceptions(w)))); - list.append(InstructionFactory.PUSH(cp, makeString((sig.getReturnType())))); - list.append(fact.createInvoke(factoryType.getClassName(), signatureMakerName, signatureType, new Type[] { Type.STRING, - Type.STRING, Type.STRING, Type.STRING, Type.STRING, Type.STRING, Type.STRING }, Constants.INVOKEVIRTUAL)); - } else if (sig.getKind().equals(Member.STATIC_INITIALIZATION)) { - BcelWorld w = shadow.getWorld(); - list.append(InstructionFactory.PUSH(cp, makeString(sig.getModifiers(w)))); - list.append(InstructionFactory.PUSH(cp, makeString(sig.getDeclaringType()))); - list.append(fact.createInvoke(factoryType.getClassName(), signatureMakerName, signatureType, Type.STRINGARRAY2, - Constants.INVOKEVIRTUAL)); - } else { - // TODO looks like this block is unused code - list.append(InstructionFactory.PUSH(cp, SignatureUtils.getSignatureString(sig, shadow.getWorld()))); - list.append(fact.createInvoke(factoryType.getClassName(), signatureMakerName, signatureType, Type.STRINGARRAY1, - Constants.INVOKEVIRTUAL)); - } - - // XXX should load source location from shadow - list.append(Utility.createConstant(fact, shadow.getSourceLine())); - - final String factoryMethod; - - // TAG:SUPPORTING12: We didn't have makeESJP() in 1.2 - if (world.isTargettingAspectJRuntime12()) { - list.append(fact.createInvoke(factoryType.getClassName(), "makeSJP", staticTjpType, new Type[] { Type.STRING, sigType, - Type.INT }, Constants.INVOKEVIRTUAL)); - - // put it in the field - list.append(fact.createFieldAccess(getClassName(), field.getName(), staticTjpType, Constants.PUTSTATIC)); - - } else { - if (staticTjpType.equals(field.getType())) { - factoryMethod = "makeSJP"; - } else if (enclosingStaticTjpType.equals(field.getType())) { - factoryMethod = "makeESJP"; - } else { - throw new Error("should not happen"); - } - - if (fastSJP) { - if (exceptionTypes != null && exceptionTypes.length != 0) { - list.append(fact.createInvoke(factoryType.getClassName(), factoryMethod, field.getType(), ARRAY_8STRING_INT, - Constants.INVOKEVIRTUAL)); - } else { - list.append(fact.createInvoke(factoryType.getClassName(), factoryMethod, field.getType(), ARRAY_7STRING_INT, - Constants.INVOKEVIRTUAL)); - } - } else { - list.append(fact.createInvoke(factoryType.getClassName(), factoryMethod, field.getType(), new Type[] { Type.STRING, - sigType, Type.INT }, Constants.INVOKEVIRTUAL)); - } - - // put it in the field - list.append(fact.createFieldAccess(getClassName(), field.getName(), field.getType(), Constants.PUTSTATIC)); - } - } - - public String getFactoryMethod(Field field, BcelShadow shadow) { - StringBuilder b = new StringBuilder(); - b.append("make"); - MemberKind kind = shadow.getSignature().getKind(); - if (kind.equals(Member.METHOD)) { - b.append("Method"); - } else if (kind.equals(Member.CONSTRUCTOR)) { - b.append("Constructor"); - } else if (kind.equals(Member.HANDLER)) { - b.append("CatchClause"); - } else if (kind.equals(Member.FIELD)) { - b.append("Field"); - } else if (kind.equals(Member.STATIC_INITIALIZATION)) { - b.append("Initializer"); - } else if (kind.equals(Member.MONITORENTER)) { - b.append("Lock"); - } else if (kind.equals(Member.MONITOREXIT)) { - b.append("Unlock"); - } else if (kind.equals(Member.ADVICE)) { - b.append("Advice"); - } else { - throw new IllegalStateException(kind.toString()); - } - if (staticTjpType.equals(field.getType())) { - b.append("SJP"); - } else if (enclosingStaticTjpType.equals(field.getType())) { - b.append("ESJP"); - } - return b.toString(); - } - - /** - * Generate optimal joinpoint initialization code. - * - * As of version 1.9.1 the runtime includes new factory methods for joinpoints that take classes, not strings - * and using them requires different code generation. Using these instead of the old ones means we can avoid - * deferred classloading for these types. By using the LDC instruction that loads classes, it also means - * anything modifying woven code and changing type names will also pick up on these references. - */ - private void initializeTjpOptimal(InstructionFactory fact, InstructionList list, Field field, BcelShadow shadow) { - list.append(InstructionFactory.createLoad(factoryType, 0)); - pushString(list, shadow.getKind().getName()); - String factoryMethod = getFactoryMethod(field, shadow); - Member sig = shadow.getSignature(); - BcelWorld w = shadow.getWorld(); - - if (sig.getKind().equals(Member.METHOD)) { - pushInt(list, sig.getModifiers(w)); - pushString(list, sig.getName()); - pushClass(list, sig.getDeclaringType()); - pushClasses(list, sig.getParameterTypes()); - pushStrings(list, sig.getParameterNames(w)); - pushClasses(list, sig.getExceptions(w)); - pushClass(list, sig.getReturnType()); - pushInt(list, shadow.getSourceLine()); - list.append(fact.createInvoke(factoryType.getClassName(), factoryMethod, field.getType(), - PARAMSIGNATURE_MAKESJP_METHOD, Constants.INVOKEVIRTUAL)); - } else if (sig.getKind().equals(Member.CONSTRUCTOR)) { - if (w.isJoinpointArrayConstructionEnabled() && sig.getDeclaringType().isArray()) { - pushInt(list, Modifier.PUBLIC); - pushClass(list, sig.getDeclaringType()); - pushClasses(list, sig.getParameterTypes()); - pushStrings(list, null); - pushClasses(list, null); - } else { - pushInt(list, sig.getModifiers(w)); - pushClass(list, sig.getDeclaringType()); - pushClasses(list, sig.getParameterTypes()); - pushStrings(list, sig.getParameterNames(w)); - pushClasses(list, sig.getExceptions(w)); - } - pushInt(list, shadow.getSourceLine()); - list.append(fact.createInvoke(factoryType.getClassName(), factoryMethod, field.getType(), - PARAMSIGNATURE_MAKESJP_CONSTRUCTOR, Constants.INVOKEVIRTUAL)); - } else if (sig.getKind().equals(Member.HANDLER)) { - pushClass(list, sig.getDeclaringType()); - pushClass(list, sig.getParameterTypes()[0]); - String pname = null; - String[] pnames = sig.getParameterNames(w); - if (pnames != null && pnames.length>0) { - pname = pnames[0]; - } - pushString(list, pname); - pushInt(list, shadow.getSourceLine()); - list.append(fact.createInvoke(factoryType.getClassName(), factoryMethod, field.getType(), - PARAMSIGNATURE_MAKESJP_CATCHCLAUSE, Constants.INVOKEVIRTUAL)); - } else if (sig.getKind().equals(Member.FIELD)) { - pushInt(list, sig.getModifiers(w)); - pushString(list, sig.getName()); - // see pr227401 - UnresolvedType dType = sig.getDeclaringType(); - if (dType.getTypekind() == TypeKind.PARAMETERIZED || dType.getTypekind() == TypeKind.GENERIC) { - dType = sig.getDeclaringType().resolve(world).getGenericType(); - } - pushClass(list, dType); - pushClass(list, sig.getReturnType()); - pushInt(list,shadow.getSourceLine()); - list.append(fact.createInvoke(factoryType.getClassName(), factoryMethod, field.getType(), - PARAMSIGNATURE_MAKESJP_FIELD, Constants.INVOKEVIRTUAL)); - } else if (sig.getKind().equals(Member.STATIC_INITIALIZATION)) { - pushInt(list, sig.getModifiers(w)); - pushClass(list, sig.getDeclaringType()); - pushInt(list, shadow.getSourceLine()); - list.append(fact.createInvoke(factoryType.getClassName(), factoryMethod, field.getType(), - PARAMSIGNATURE_MAKESJP_INITIALIZER, Constants.INVOKEVIRTUAL)); - } else if (sig.getKind().equals(Member.MONITORENTER)) { - pushClass(list, sig.getDeclaringType()); - pushInt(list, shadow.getSourceLine()); - list.append(fact.createInvoke(factoryType.getClassName(), factoryMethod, field.getType(), - PARAMSIGNATURE_MAKESJP_MONITOR, Constants.INVOKEVIRTUAL)); - } else if (sig.getKind().equals(Member.MONITOREXIT)) { - pushClass(list, sig.getDeclaringType()); - pushInt(list, shadow.getSourceLine()); - list.append(fact.createInvoke(factoryType.getClassName(), factoryMethod, field.getType(), - PARAMSIGNATURE_MAKESJP_MONITOR, Constants.INVOKEVIRTUAL)); - } else if (sig.getKind().equals(Member.ADVICE)) { - pushInt(list, sig.getModifiers(w)); - pushString(list, sig.getName()); - pushClass(list, sig.getDeclaringType()); - pushClasses(list, sig.getParameterTypes()); - pushStrings(list, sig.getParameterNames(w)); - pushClasses(list, sig.getExceptions(w)); - pushClass(list, sig.getReturnType()); - pushInt(list, shadow.getSourceLine()); - list.append(fact.createInvoke(factoryType.getClassName(), factoryMethod, field.getType(), - PARAMSIGNATURE_MAKESJP_ADVICE, Constants.INVOKEVIRTUAL)); - } else { - throw new IllegalStateException("not sure what to do: "+shadow); - } - list.append(fact.createFieldAccess(getClassName(), field.getName(), field.getType(), Constants.PUTSTATIC)); - } - - private void pushStrings(InstructionList list, String[] strings) { - // Build an array loaded with the strings - if (strings == null || strings.length == 0) { - list.append(InstructionFactory.ACONST_NULL); - } else { - list.append(InstructionFactory.PUSH(cp, strings.length)); - list.append(fact.createNewArray(Type.STRING, (short)1)); - for (int s=0;s 0) { - buf.append(':'); - } - buf.append(makeString(types[i])); - } - return buf.toString(); - } - - protected String makeString(String[] names) { - if (names == null) { - return ""; - } - StringBuilder buf = new StringBuilder(); - for (int i = 0, len = names.length; i < len; i++) { - if (i > 0) { - buf.append(':'); - } - buf.append(names[i]); - } - return buf.toString(); - } - - public ResolvedType getType() { - if (myType == null) { - return null; - } - return myType.getResolvedTypeX(); - } - - public BcelObjectType getBcelObjectType() { - return myType; - } - - public String getFileName() { - return myGen.getFileName(); - } - - // for *new* fields - private void addField(FieldGen field) { - makeSyntheticAndTransientIfNeeded(field); - BcelField bcelField = null; - if (getBcelObjectType() != null) { - bcelField = new BcelField(getBcelObjectType(), field.getField()); - } else { - bcelField = new BcelField(getName(), field.getField(), world); - } - fields.add(bcelField); - // myGen.addField(field.getField()); - } - - private void makeSyntheticAndTransientIfNeeded(FieldGen field) { - if (field.getName().startsWith(NameMangler.PREFIX) && !field.getName().startsWith("ajc$interField$") - && !field.getName().startsWith("ajc$instance$")) { - // it's an aj added field - // first do transient - if (!field.isStatic()) { - field.setModifiers(field.getModifiers() | Constants.ACC_TRANSIENT); - } - // then do synthetic - if (getWorld().isInJava5Mode()) { - // add the synthetic modifier flag - field.setModifiers(field.getModifiers() | ACC_SYNTHETIC); - } - if (!hasSyntheticAttribute(field.getAttributes())) { - // belt and braces, do the attribute even on Java 5 in addition - // to the modifier flag - // Attribute[] oldAttrs = field.getAttributes(); - // Attribute[] newAttrs = new Attribute[oldAttrs.length + 1]; - // System.arraycopy(oldAttrs, 0, newAttrs, 0, oldAttrs.length); - ConstantPool cpg = myGen.getConstantPool(); - int index = cpg.addUtf8("Synthetic"); - Attribute synthetic = new Synthetic(index, 0, new byte[0], cpg); - field.addAttribute(synthetic); - // newAttrs[newAttrs.length - 1] = synthetic; - // field.setAttributes(newAttrs); - } - } - } - - private boolean hasSyntheticAttribute(List attributes) { - for (int i = 0; i < attributes.size(); i++) { - if ((attributes.get(i)).getName().equals("Synthetic")) { - return true; - } - } - return false; - } - - public void addField(FieldGen field, ISourceLocation sourceLocation) { - addField(field); - if (!(field.isPrivate() && (field.isStatic() || field.isTransient()))) { - errorOnAddedField(field, sourceLocation); - } - } - - public String getClassName() { - return myGen.getClassName(); - } - - public boolean isInterface() { - return myGen.isInterface(); - } - - public boolean isAbstract() { - return myGen.isAbstract(); - } - - public LazyMethodGen getLazyMethodGen(Member m) { - return getLazyMethodGen(m.getName(), m.getSignature(), false); - } - - public LazyMethodGen getLazyMethodGen(String name, String signature) { - return getLazyMethodGen(name, signature, false); - } - - public LazyMethodGen getLazyMethodGen(String name, String signature, boolean allowMissing) { - for (LazyMethodGen gen : methodGens) { - if (gen.getName().equals(name) && gen.getSignature().equals(signature)) { - return gen; - } - } - - if (!allowMissing) { - throw new BCException("Class " + this.getName() + " does not have a method " + name + " with signature " + signature); - } - - return null; - } - - public void forcePublic() { - myGen.setModifiers(Utility.makePublic(myGen.getModifiers())); - } - - public boolean hasAnnotation(UnresolvedType t) { - - // annotations on the real thing - AnnotationGen agens[] = myGen.getAnnotations(); - if (agens == null) { - return false; - } - for (int i = 0; i < agens.length; i++) { - AnnotationGen gen = agens[i]; - if (t.equals(UnresolvedType.forSignature(gen.getTypeSignature()))) { - return true; - } - } - - // annotations added during this weave - - return false; - } - - public void addAnnotation(AnnotationGen a) { - if (!hasAnnotation(UnresolvedType.forSignature(a.getTypeSignature()))) { - annotations.add(new AnnotationGen(a, getConstantPool(), true)); - } - } - - public void addAttribute(AjAttribute attribute) { - myGen.addAttribute(Utility.bcelAttribute(attribute, getConstantPool())); - } - - public void addAttribute(Attribute attribute) { - myGen.addAttribute(attribute); - } - - public Collection getAttributes() { - return myGen.getAttributes(); - } - - // this test is like asking: - // if - // (UnresolvedType.SERIALIZABLE.resolve(getType().getWorld()).isAssignableFrom - // (getType())) { - // only we don't do that because this forces us to find all the supertypes - // of the type, - // and if one of them is missing we fail, and it's not worth failing just to - // put out - // a warning message! - private boolean implementsSerializable(ResolvedType aType) { - if (aType.getSignature().equals(UnresolvedType.SERIALIZABLE.getSignature())) { - return true; - } - - ResolvedType[] interfaces = aType.getDeclaredInterfaces(); - for (int i = 0; i < interfaces.length; i++) { - if (interfaces[i].isMissing()) { - continue; - } - if (implementsSerializable(interfaces[i])) { - return true; - } - } - ResolvedType superType = aType.getSuperclass(); - if (superType != null && !superType.isMissing()) { - return implementsSerializable(superType); - } - return false; - } - - public boolean isAtLeastJava5() { - return (myGen.getMajor() >= Constants.MAJOR_1_5); - } - - /** - * Return the next available field name with the specified 'prefix', e.g. for prefix 'class$' where class$0, class$1 exist then - * return class$2 - */ - public String allocateField(String prefix) { - int highestAllocated = -1; - List fs = getFieldGens(); - for (BcelField field : fs) { - if (field.getName().startsWith(prefix)) { - try { - int num = Integer.parseInt(field.getName().substring(prefix.length())); - if (num > highestAllocated) { - highestAllocated = num; - } - } catch (NumberFormatException nfe) { - // something wrong with the number on the end of that - // field... - } - } - } - return prefix + Integer.toString(highestAllocated + 1); - } - -} diff --git a/weaver/src/org/aspectj/weaver/bcel/LazyMethodGen.java b/weaver/src/org/aspectj/weaver/bcel/LazyMethodGen.java deleted file mode 100644 index 2655a3456..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/LazyMethodGen.java +++ /dev/null @@ -1,1870 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - -package org.aspectj.weaver.bcel; - -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; -import java.util.Set; - -import org.aspectj.apache.bcel.Constants; -import org.aspectj.apache.bcel.classfile.Attribute; -import org.aspectj.apache.bcel.classfile.ConstantPool; -import org.aspectj.apache.bcel.classfile.Method; -import org.aspectj.apache.bcel.classfile.Synthetic; -import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; -import org.aspectj.apache.bcel.generic.BranchHandle; -import org.aspectj.apache.bcel.generic.ClassGenException; -import org.aspectj.apache.bcel.generic.CodeExceptionGen; -import org.aspectj.apache.bcel.generic.Instruction; -import org.aspectj.apache.bcel.generic.InstructionBranch; -import org.aspectj.apache.bcel.generic.InstructionHandle; -import org.aspectj.apache.bcel.generic.InstructionList; -import org.aspectj.apache.bcel.generic.InstructionSelect; -import org.aspectj.apache.bcel.generic.InstructionTargeter; -import org.aspectj.apache.bcel.generic.LineNumberTag; -import org.aspectj.apache.bcel.generic.LocalVariableTag; -import org.aspectj.apache.bcel.generic.MethodGen; -import org.aspectj.apache.bcel.generic.ObjectType; -import org.aspectj.apache.bcel.generic.Tag; -import org.aspectj.apache.bcel.generic.TargetLostException; -import org.aspectj.apache.bcel.generic.Type; -import org.aspectj.bridge.IMessage; -import org.aspectj.bridge.ISourceLocation; -import org.aspectj.weaver.AjAttribute; -import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; -import org.aspectj.weaver.AnnotationAJ; -import org.aspectj.weaver.BCException; -import org.aspectj.weaver.ISourceContext; -import org.aspectj.weaver.MemberImpl; -import org.aspectj.weaver.NameMangler; -import org.aspectj.weaver.ResolvedMember; -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.Shadow; -import org.aspectj.weaver.UnresolvedType; -import org.aspectj.weaver.WeaverMessages; -import org.aspectj.weaver.World; -import org.aspectj.weaver.tools.Traceable; - -/** - * A LazyMethodGen should be treated as a MethodGen. It's our way of abstracting over the low-level Method objects. It converts - * through {@link MethodGen} to create and to serialize, but that's it. - * - *

- * At any rate, there are two ways to create LazyMethodGens. One is from a method, which does work through MethodGen to do the - * correct thing. The other is the creation of a completely empty LazyMethodGen, and it is used when we're constructing code from - * scratch. - * - *

- * We stay away from targeters for rangey things like Shadows and Exceptions. - */ -public final class LazyMethodGen implements Traceable { - - private static final AnnotationAJ[] NO_ANNOTATIONAJ = new AnnotationAJ[] {}; - - private int modifiers; - private Type returnType; - private final String name; - private Type[] argumentTypes; - // private final String[] argumentNames; - private String[] declaredExceptions; - private InstructionList body; - private List attributes; - private List newAnnotations; - private List annotationsForRemoval; - private AnnotationAJ[][] newParameterAnnotations; - private final LazyClassGen enclosingClass; - private BcelMethod memberView; - private AjAttribute.EffectiveSignatureAttribute effectiveSignature; - int highestLineNumber = 0; - boolean wasPackedOptimally = false; - private Method savedMethod = null; - - // Some tools that may post process the output bytecode do not long local variable tables - // to be generated as one reason the tables may be missing in the first place is because - // the bytecode is odd. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=470658 - private final boolean originalMethodHasLocalVariableTable; - - /* - * We use LineNumberTags and not Gens. - * - * This option specifies whether we let the BCEL classes create LineNumberGens and LocalVariableGens or if we make it create - * LineNumberTags and LocalVariableTags. Up until 1.5.1 we always created Gens - then on return from the MethodGen ctor we took - * them apart, reprocessed them all and created Tags. (see unpackLocals/unpackLineNumbers). As we have our own copy of Bcel, why - * not create the right thing straightaway? So setting this to true will call the MethodGen ctor() in such a way that it creates - * Tags - removing the need for unpackLocals/unpackLineNumbers - HOWEVER see the ensureAllLineNumberSetup() method for some - * other relevant info. - * - * Whats the difference between a Tag and a Gen? A Tag is more lightweight, it doesn't know which instructions it targets, it - * relies on the instructions targettingit - this reduces the amount of targeter manipulation we have to do. - */ - - /** - * This is nonnull if this method is the result of an "inlining". We currently copy methods into other classes for around - * advice. We add this field so we can get JSR45 information correct. If/when we do _actual_ inlining, we'll need to subtype - * LineNumberTag to have external line numbers. - */ - String fromFilename = null; - private int maxLocals; - private boolean canInline = true; - private boolean isSynthetic = false; - List matchedShadows; - // Used for interface introduction - this is the type of the interface the method is technically on - public ResolvedType definingType = null; - - static class LightweightBcelMethod extends BcelMethod { - - LightweightBcelMethod(BcelObjectType declaringType, Method method) { - super(declaringType, method); - // TODO Auto-generated constructor stub - } - - } - - public LazyMethodGen(int modifiers, Type returnType, String name, Type[] paramTypes, String[] declaredExceptions, - LazyClassGen enclosingClass) { - // enclosingClass.getName() + ", " + returnType); - this.memberView = null; // should be okay, since constructed ones aren't woven into - this.modifiers = modifiers; - this.returnType = returnType; - this.name = name; - this.argumentTypes = paramTypes; - // this.argumentNames = Utility.makeArgNames(paramTypes.length); - this.declaredExceptions = declaredExceptions; - if (!Modifier.isAbstract(modifiers)) { - body = new InstructionList(); - setMaxLocals(calculateMaxLocals()); - } else { - body = null; - } - this.attributes = new ArrayList(); - this.enclosingClass = enclosingClass; - assertGoodBody(); - this.originalMethodHasLocalVariableTable = true; // it is a new method, we want an lvar table - - // @AJ advice are not inlined by default since requires further analysis and weaving ordering control - // TODO AV - improve - note: no room for improvement as long as aspects are reweavable - // since the inlined version with wrappers and an to be done annotation to keep - // inline state will be garbaged due to reweavable impl - if (memberView != null && isAdviceMethod()) { - if (enclosingClass.getType().isAnnotationStyleAspect()) { - // TODO we could check for @Around advice as well - this.canInline = false; - } - } - } - - private int calculateMaxLocals() { - int ret = Modifier.isStatic(modifiers) ? 0 : 1; // will there be a 'this'? - for (Type type : argumentTypes) { - ret += type.getSize(); - } - return ret; - } - - // build from an existing method, lazy build saves most work for - // initialization - public LazyMethodGen(Method m, LazyClassGen enclosingClass) { - savedMethod = m; - - this.enclosingClass = enclosingClass; - if (!(m.isAbstract() || m.isNative()) && m.getCode() == null) { - throw new RuntimeException("bad non-abstract method with no code: " + m + " on " + enclosingClass); - } - if ((m.isAbstract() || m.isNative()) && m.getCode() != null) { - throw new RuntimeException("bad abstract method with code: " + m + " on " + enclosingClass); - } - this.memberView = new BcelMethod(enclosingClass.getBcelObjectType(), m); - this.originalMethodHasLocalVariableTable = savedMethod.getLocalVariableTable()!=null; - this.modifiers = m.getModifiers(); - this.name = m.getName(); - - // @AJ advice are not inlined by default since requires further analysis - // and weaving ordering control - // TODO AV - improve - note: no room for improvement as long as aspects - // are reweavable - // since the inlined version with wrappers and an to be done annotation - // to keep - // inline state will be garbaged due to reweavable impl - if (memberView != null && isAdviceMethod()) { - if (enclosingClass.getType().isAnnotationStyleAspect()) { - // TODO we could check for @Around advice as well - this.canInline = false; - } - } - } - - private boolean isAbstractOrNative(int modifiers) { - return Modifier.isAbstract(modifiers) || Modifier.isNative(modifiers); - } - - public LazyMethodGen(BcelMethod m, LazyClassGen enclosingClass) { - savedMethod = m.getMethod(); - this.enclosingClass = enclosingClass; - if (!isAbstractOrNative(m.getModifiers()) && savedMethod.getCode() == null) { - throw new RuntimeException("bad non-abstract method with no code: " + m + " on " + enclosingClass); - } - if (isAbstractOrNative(m.getModifiers()) && savedMethod.getCode() != null) { - throw new RuntimeException("bad abstract method with code: " + m + " on " + enclosingClass); - } - // this.memberView = new BcelMethod(enclosingClass.getBcelObjectType(), - // m); - this.memberView = m; - this.modifiers = savedMethod.getModifiers(); - this.name = m.getName(); - this.originalMethodHasLocalVariableTable = savedMethod.getLocalVariableTable() != null; - // @AJ advice are not inlined by default since requires further analysis - // and weaving ordering control - // TODO AV - improve - note: no room for improvement as long as aspects - // are reweavable - // since the inlined version with wrappers and an to be done annotation - // to keep - // inline state will be garbaged due to reweavable impl - if (memberView != null && isAdviceMethod()) { - if (enclosingClass.getType().isAnnotationStyleAspect()) { - // TODO we could check for @Around advice as well - this.canInline = false; - } - } - - } - - public boolean hasDeclaredLineNumberInfo() { - return (memberView != null && memberView.hasDeclarationLineNumberInfo()); - } - - public int getDeclarationLineNumber() { - if (hasDeclaredLineNumberInfo()) { - return memberView.getDeclarationLineNumber(); - } else { - return -1; - } - } - - public int getDeclarationOffset() { - if (hasDeclaredLineNumberInfo()) { - return memberView.getDeclarationOffset(); - } else { - return 0; - } - } - - public void addAnnotation(AnnotationAJ ax) { - initialize(); - if (memberView == null) { - // If member view is null, we manage them in newAnnotations - if (newAnnotations == null) { - newAnnotations = new ArrayList(); - } - newAnnotations.add(ax); - } else { - memberView.addAnnotation(ax); - } - } - - public void removeAnnotation(ResolvedType annotationType) { - initialize(); - if (memberView == null) { - // If member view is null, we manage them in newAnnotations - if (annotationsForRemoval == null) { - annotationsForRemoval = new ArrayList(); - } - annotationsForRemoval.add(annotationType); - } else { - memberView.removeAnnotation(annotationType); - } - } - - public void addParameterAnnotation(int parameterNumber, AnnotationAJ anno) { - initialize(); - if (memberView == null) { - if (newParameterAnnotations == null) { - // time to create it - int pcount = getArgumentTypes().length; - newParameterAnnotations = new AnnotationAJ[pcount][]; - for (int i = 0; i < pcount; i++) { - if (i == parameterNumber) { - newParameterAnnotations[i] = new AnnotationAJ[1]; - newParameterAnnotations[i][0] = anno; - } else { - newParameterAnnotations[i] = NO_ANNOTATIONAJ; - } - } - } else { - AnnotationAJ[] currentAnnoArray = newParameterAnnotations[parameterNumber]; - AnnotationAJ[] newAnnoArray = new AnnotationAJ[currentAnnoArray.length + 1]; - System.arraycopy(currentAnnoArray, 0, newAnnoArray, 0, currentAnnoArray.length); - newAnnoArray[currentAnnoArray.length] = anno; - newParameterAnnotations[parameterNumber] = newAnnoArray; - } - } else { - memberView.addParameterAnnotation(parameterNumber, anno); - } - } - - public ResolvedType[] getAnnotationTypes() { - initialize(); - if (memberView == null && newAnnotations!=null && newAnnotations.size()!=0) { - // TODO Ignoring removed annotations for now - ResolvedType[] annotationTypes = new ResolvedType[newAnnotations.size()]; - for (int a=0,len=newAnnotations.size();a 0) hasExceptionHandlers = true; - int priority = len - 1; - for (int i = 0; i < len; i++, priority--) { - CodeExceptionGen exn = exns[i]; - - InstructionHandle start = Range.genStart(body, getOutermostExceptionStart(exn.getStartPC())); - InstructionHandle end = Range.genEnd(body, getOutermostExceptionEnd(exn.getEndPC())); - // this doesn't necessarily handle overlapping correctly!!! - ExceptionRange er = new ExceptionRange(body, exn.getCatchType() == null ? null : BcelWorld.fromBcel(exn - .getCatchType()), priority); - er.associateWithTargets(start, end, exn.getHandlerPC()); - exn.setStartPC(null); // also removes from target - exn.setEndPC(null); // also removes from target - exn.setHandlerPC(null); // also removes from target - } - gen.removeExceptionHandlers(); - } - } - - private InstructionHandle getOutermostExceptionStart(InstructionHandle ih) { - while (true) { - if (ExceptionRange.isExceptionStart(ih.getPrev())) { - ih = ih.getPrev(); - } else { - return ih; - } - } - } - - private InstructionHandle getOutermostExceptionEnd(InstructionHandle ih) { - while (true) { - if (ExceptionRange.isExceptionEnd(ih.getNext())) { - ih = ih.getNext(); - } else { - return ih; - } - } - } - - /** - * On entry to this method we have a method whose instruction stream contains a few instructions that have line numbers assigned - * to them (LineNumberTags). The aim is to ensure every instruction has the right line number. This is necessary because some of - * them may be extracted out into other methods - and it'd be useful for them to maintain the source line number for debugging. - */ - public void ensureAllLineNumberSetup() { - LineNumberTag lastKnownLineNumberTag = null; - boolean skip = false; - for (InstructionHandle ih = body.getStart(); ih != null; ih = ih.getNext()) { - skip = false; - for (InstructionTargeter targeter : ih.getTargeters()) { - if (targeter instanceof LineNumberTag) { - lastKnownLineNumberTag = (LineNumberTag) targeter; - skip = true; - } - } - if (lastKnownLineNumberTag != null && !skip) { - ih.addTargeter(lastKnownLineNumberTag); - } - } - } - - // =============== - - public int allocateLocal(Type type) { - return allocateLocal(type.getSize()); - } - - public int allocateLocal(int slots) { - int max = getMaxLocals(); - setMaxLocals(max + slots); - return max; - } - - public Method getMethod() { - if (savedMethod != null) { - return savedMethod; // ??? this relies on gentle treatment of - // constant pool - } - - try { - MethodGen gen = pack(); - savedMethod = gen.getMethod(); - return savedMethod; - } catch (ClassGenException e) { - enclosingClass - .getBcelObjectType() - .getResolvedTypeX() - .getWorld() - .showMessage( - IMessage.ERROR, - WeaverMessages.format(WeaverMessages.PROBLEM_GENERATING_METHOD, this.getClassName(), this.getName(), - e.getMessage()), - this.getMemberView() == null ? null : this.getMemberView().getSourceLocation(), null); - // throw e; PR 70201.... let the normal problem reporting - // infrastructure deal with this rather than crashing. - body = null; - MethodGen gen = pack(); - return gen.getMethod(); - } catch (RuntimeException re) { - if (re.getCause() instanceof ClassGenException) { - enclosingClass - .getBcelObjectType() - .getResolvedTypeX() - .getWorld() - .showMessage( - IMessage.ERROR, - WeaverMessages.format(WeaverMessages.PROBLEM_GENERATING_METHOD, this.getClassName(), - this.getName(), re.getCause().getMessage()), - this.getMemberView() == null ? null : this.getMemberView().getSourceLocation(), null); - // throw e; PR 70201.... let the normal problem reporting - // infrastructure deal with this rather than crashing. - body = null; - MethodGen gen = pack(); - return gen.getMethod(); - } - throw re; - } - } - - public void markAsChanged() { - if (wasPackedOptimally) { - throw new RuntimeException("Already packed method is being re-modified: " + getClassName() + " " + toShortString()); - } - initialize(); - savedMethod = null; - } - - // ============================= - - @Override - public String toString() { - BcelObjectType bot = enclosingClass.getBcelObjectType(); - WeaverVersionInfo weaverVersion = (bot == null ? WeaverVersionInfo.CURRENT : bot.getWeaverVersionAttribute()); - return toLongString(weaverVersion); - } - - public String toShortString() { - String access = org.aspectj.apache.bcel.classfile.Utility.accessToString(getAccessFlags()); - - StringBuffer buf = new StringBuffer(); - - if (!access.equals("")) { - buf.append(access); - buf.append(" "); - } - buf.append(org.aspectj.apache.bcel.classfile.Utility.signatureToString(getReturnType().getSignature(), true)); - buf.append(" "); - buf.append(getName()); - buf.append("("); - { - int len = argumentTypes.length; - if (len > 0) { - buf.append(org.aspectj.apache.bcel.classfile.Utility.signatureToString(argumentTypes[0].getSignature(), true)); - for (int i = 1; i < argumentTypes.length; i++) { - buf.append(", "); - buf.append(org.aspectj.apache.bcel.classfile.Utility.signatureToString(argumentTypes[i].getSignature(), true)); - } - } - } - buf.append(")"); - - { - int len = declaredExceptions != null ? declaredExceptions.length : 0; - if (len > 0) { - buf.append(" throws "); - buf.append(declaredExceptions[0]); - for (int i = 1; i < declaredExceptions.length; i++) { - buf.append(", "); - buf.append(declaredExceptions[i]); - } - } - } - return buf.toString(); - } - - public String toLongString(WeaverVersionInfo weaverVersion) { - ByteArrayOutputStream s = new ByteArrayOutputStream(); - print(new PrintStream(s), weaverVersion); - return new String(s.toByteArray()); - } - - public void print(WeaverVersionInfo weaverVersion) { - print(System.out, weaverVersion); - } - - public void print(PrintStream out, WeaverVersionInfo weaverVersion) { - out.print(" " + toShortString()); - printAspectAttributes(out, weaverVersion); - - InstructionList body = getBody(); - if (body == null) { - out.println(";"); - return; - } - out.println(":"); - new BodyPrinter(out).run(); - out.println(" end " + toShortString()); - } - - private void printAspectAttributes(PrintStream out, WeaverVersionInfo weaverVersion) { - ISourceContext context = null; - if (enclosingClass != null && enclosingClass.getType() != null) { - context = enclosingClass.getType().getSourceContext(); - } - List as = Utility.readAjAttributes(getClassName(), attributes.toArray(new Attribute[] {}), context, null, weaverVersion, - new BcelConstantPoolReader(this.enclosingClass.getConstantPool())); - if (!as.isEmpty()) { - out.println(" " + as.get(0)); // XXX assuming exactly one - // attribute, munger... - } - } - - private class BodyPrinter { - Map labelMap = new HashMap(); - - InstructionList body; - PrintStream out; - ConstantPool pool; - - BodyPrinter(PrintStream out) { - this.pool = enclosingClass.getConstantPool(); - this.body = getBodyForPrint(); - this.out = out; - } - - BodyPrinter(PrintStream out, InstructionList il) { - this.pool = enclosingClass.getConstantPool(); - this.body = il; - this.out = out; - } - - void run() { - // killNops(); - assignLabels(); - print(); - } - - // label assignment - void assignLabels() { - LinkedList exnTable = new LinkedList(); - String pendingLabel = null; - // boolean hasPendingTargeters = false; - int lcounter = 0; - for (InstructionHandle ih = body.getStart(); ih != null; ih = ih.getNext()) { - Iterator tIter = ih.getTargeters().iterator(); - while (tIter.hasNext()) { - InstructionTargeter t = tIter.next();// targeters - // [ - // i - // ] - // ; - if (t instanceof ExceptionRange) { - // assert isRangeHandle(h); - ExceptionRange r = (ExceptionRange) t; - if (r.getStart() == ih) { - insertHandler(r, exnTable); - } - } else if (t instanceof InstructionBranch) { - if (pendingLabel == null) { - pendingLabel = "L" + lcounter++; - } - } else { - // assert isRangeHandle(h) - } - } - if (pendingLabel != null) { - labelMap.put(ih, pendingLabel); - if (!Range.isRangeHandle(ih)) { - pendingLabel = null; - } - } - } - int ecounter = 0; - for (ExceptionRange er: exnTable) { - String exceptionLabel = "E" + ecounter++; - labelMap.put(Range.getRealStart(er.getHandler()), exceptionLabel); - labelMap.put(er.getHandler(), exceptionLabel); - } - } - - // printing - - void print() { - int depth = 0; - int currLine = -1; - bodyPrint: for (InstructionHandle ih = body.getStart(); ih != null; ih = ih.getNext()) { - if (Range.isRangeHandle(ih)) { - Range r = Range.getRange(ih); - // don't print empty ranges, that is, ranges who contain no - // actual instructions - for (InstructionHandle xx = r.getStart(); Range.isRangeHandle(xx); xx = xx.getNext()) { - if (xx == r.getEnd()) { - continue bodyPrint; - } - } - - // doesn't handle nested: if (r.getStart().getNext() == - // r.getEnd()) continue; - if (r.getStart() == ih) { - printRangeString(r, depth++); - } else { - if (r.getEnd() != ih) { - throw new RuntimeException("bad"); - } - printRangeString(r, --depth); - } - } else { - printInstruction(ih, depth); - int line = getLineNumber(ih, currLine); - if (line != currLine) { - currLine = line; - out.println(" (line " + line + ")"); - } else { - out.println(); - } - } - } - } - - void printRangeString(Range r, int depth) { - printDepth(depth); - out.println(getRangeString(r, labelMap)); - } - - String getRangeString(Range r, Map labelMap) { - if (r instanceof ExceptionRange) { - ExceptionRange er = (ExceptionRange) r; - return er.toString() + " -> " + labelMap.get(er.getHandler()); - // - // + " PRI " + er.getPriority(); - } else { - return r.toString(); - } - } - - void printDepth(int depth) { - pad(BODY_INDENT); - while (depth > 0) { - out.print("| "); - depth--; - } - } - - void printLabel(String s, int depth) { - int space = Math.max(CODE_INDENT - depth * 2, 0); - if (s == null) { - pad(space); - } else { - space = Math.max(space - (s.length() + 2), 0); - pad(space); - out.print(s); - out.print(": "); - } - } - - void printInstruction(InstructionHandle h, int depth) { - printDepth(depth); - printLabel(labelMap.get(h), depth); - - Instruction inst = h.getInstruction(); - if (inst.isConstantPoolInstruction()) { - out.print(Constants.OPCODE_NAMES[inst.opcode].toUpperCase()); - out.print(" "); - out.print(pool.constantToString(pool.getConstant(inst.getIndex()))); - } else if (inst instanceof InstructionSelect) { - InstructionSelect sinst = (InstructionSelect) inst; - out.println(Constants.OPCODE_NAMES[sinst.opcode].toUpperCase()); - int[] matches = sinst.getMatchs(); - InstructionHandle[] targets = sinst.getTargets(); - InstructionHandle defaultTarget = sinst.getTarget(); - for (int i = 0, len = matches.length; i < len; i++) { - printDepth(depth); - printLabel(null, depth); - out.print(" "); - out.print(matches[i]); - out.print(": \t"); - out.println(labelMap.get(targets[i])); - } - printDepth(depth); - printLabel(null, depth); - out.print(" "); - out.print("default: \t"); - out.print(labelMap.get(defaultTarget)); - } else if (inst instanceof InstructionBranch) { - InstructionBranch brinst = (InstructionBranch) inst; - out.print(Constants.OPCODE_NAMES[brinst.getOpcode()].toUpperCase()); - out.print(" "); - out.print(labelMap.get(brinst.getTarget())); - } else if (inst.isLocalVariableInstruction()) { - // LocalVariableInstruction lvinst = (LocalVariableInstruction) - // inst; - out.print(inst.toString(false).toUpperCase()); - int index = inst.getIndex(); - LocalVariableTag tag = getLocalVariableTag(h, index); - if (tag != null) { - out.print(" // "); - out.print(tag.getType()); - out.print(" "); - out.print(tag.getName()); - } - } else { - out.print(inst.toString(false).toUpperCase()); - } - } - - static final int BODY_INDENT = 4; - static final int CODE_INDENT = 16; - - void pad(int size) { - for (int i = 0; i < size; i++) { - out.print(" "); - } - } - } - - static LocalVariableTag getLocalVariableTag(InstructionHandle ih, int index) { - for (InstructionTargeter t : ih.getTargeters()) { - if (t instanceof LocalVariableTag) { - LocalVariableTag lvt = (LocalVariableTag) t; - if (lvt.getSlot() == index) { - return lvt; - } - } - } - return null; - } - - static int getLineNumber(InstructionHandle ih, int prevLine) { - for (InstructionTargeter t : ih.getTargeters()) { - if (t instanceof LineNumberTag) { - return ((LineNumberTag) t).getLineNumber(); - } - } - return prevLine; - } - - public boolean isStatic() { - return Modifier.isStatic(getAccessFlags()); - } - - public boolean isAbstract() { - return Modifier.isAbstract(getAccessFlags()); - } - - public boolean isBridgeMethod() { - return (getAccessFlags() & Constants.ACC_BRIDGE) != 0; - } - - public void addExceptionHandler(InstructionHandle start, InstructionHandle end, InstructionHandle handlerStart, - ObjectType catchType, boolean highPriority) { - - InstructionHandle start1 = Range.genStart(body, start); - InstructionHandle end1 = Range.genEnd(body, end); - - ExceptionRange er = new ExceptionRange(body, (catchType == null ? null : BcelWorld.fromBcel(catchType)), highPriority); - er.associateWithTargets(start1, end1, handlerStart); - } - - public int getAccessFlags() { - return modifiers; - } - - public int getAccessFlagsWithoutSynchronized() { - if (isSynchronized()) { - return modifiers - Modifier.SYNCHRONIZED; - } - return modifiers; - } - - public boolean isSynchronized() { - return (modifiers & Modifier.SYNCHRONIZED) != 0; - } - - public void setAccessFlags(int newFlags) { - this.modifiers = newFlags; - } - - public Type[] getArgumentTypes() { - initialize(); - return argumentTypes; - } - - public LazyClassGen getEnclosingClass() { - return enclosingClass; - } - - public int getMaxLocals() { - return maxLocals; - } - - public String getName() { - return name; - } - - public String getGenericReturnTypeSignature() { - if (memberView == null) { - return getReturnType().getSignature(); - } else { - return memberView.getGenericReturnType().getSignature(); - } - } - - public Type getReturnType() { - initialize(); - return returnType; - } - - public void setMaxLocals(int maxLocals) { - this.maxLocals = maxLocals; - } - - public InstructionList getBody() { - markAsChanged(); - return body; - } - - public InstructionList getBodyForPrint() { - return body; - } - - public boolean hasBody() { - if (savedMethod != null) { - return savedMethod.getCode() != null; - } - return body != null; - } - - public List getAttributes() { - return attributes; - } - - public String[] getDeclaredExceptions() { - return declaredExceptions; - } - - public String getClassName() { - return enclosingClass.getName(); - } - - // ---- packing! - - public MethodGen pack() { - forceSyntheticForAjcMagicMembers(); - - // killNops(); - int flags = getAccessFlags(); - if (enclosingClass.getWorld().isJoinpointSynchronizationEnabled() - && enclosingClass.getWorld().areSynchronizationPointcutsInUse()) { - flags = getAccessFlagsWithoutSynchronized(); - } - MethodGen gen = new MethodGen(flags, getReturnType(), getArgumentTypes(), null, // getArgumentNames(), - getName(), getEnclosingClass().getName(), new InstructionList(), getEnclosingClass().getConstantPool()); - for (int i = 0, len = declaredExceptions.length; i < len; i++) { - gen.addException(declaredExceptions[i]); - } - - for (Attribute attr : attributes) { - gen.addAttribute(attr); - } - - if (newAnnotations != null) { - for (AnnotationAJ element : newAnnotations) { - gen.addAnnotation(new AnnotationGen(((BcelAnnotation) element).getBcelAnnotation(), gen.getConstantPool(), true)); - } - } - - if (newParameterAnnotations != null) { - for (int i = 0; i < newParameterAnnotations.length; i++) { - AnnotationAJ[] annos = newParameterAnnotations[i]; - for (int j = 0; j < annos.length; j++) { - gen.addParameterAnnotation(i, - new AnnotationGen(((BcelAnnotation) annos[j]).getBcelAnnotation(), gen.getConstantPool(), true)); - } - } - } - - if (memberView != null && memberView.getAnnotations() != null && memberView.getAnnotations().length != 0) { - AnnotationAJ[] ans = memberView.getAnnotations(); - for (int i = 0, len = ans.length; i < len; i++) { - AnnotationGen a = ((BcelAnnotation) ans[i]).getBcelAnnotation(); - gen.addAnnotation(new AnnotationGen(a, gen.getConstantPool(), true)); - } - } - - if (isSynthetic) { - if (enclosingClass.getWorld().isInJava5Mode()) { - gen.setModifiers(gen.getModifiers() | Constants.ACC_SYNTHETIC); - } - if (!hasAttribute("Synthetic")) { - // belt and braces, do the attribute even on Java 5 in addition to the modifier flag - ConstantPool cpg = gen.getConstantPool(); - int index = cpg.addUtf8("Synthetic"); - gen.addAttribute(new Synthetic(index, 0, new byte[0], cpg)); - } - } - - if (hasBody()) { - if (this.enclosingClass.getWorld().shouldFastPackMethods()) { - if (isAdviceMethod() || getName().equals("")) { - packBody(gen); - } else { - optimizedPackBody(gen); - } - } else { - packBody(gen); - } - - gen.setMaxLocals(true); - gen.setMaxStack(); - } else { - gen.setInstructionList(null); - } - return gen; - } - - private boolean hasAttribute(String attributeName) { - for (Attribute attr: attributes) { - if (attr.getName().equals(attributeName)) { - return true; - } - } - return false; - } - - private void forceSyntheticForAjcMagicMembers() { - if (NameMangler.isSyntheticMethod(getName(), inAspect())) { - makeSynthetic(); - } - } - - private boolean inAspect() { - BcelObjectType objectType = enclosingClass.getBcelObjectType(); - return (objectType == null ? false : objectType.isAspect()); - } - - public void makeSynthetic() { - isSynthetic = true; - } - - private static class LVPosition { - InstructionHandle start = null; - InstructionHandle end = null; - } - - /** - * fill the newly created method gen with our body, inspired by InstructionList.copy() - */ - public void packBody(MethodGen gen) { - InstructionList fresh = gen.getInstructionList(); - Map map = copyAllInstructionsExceptRangeInstructionsInto(fresh); - - // at this point, no rangeHandles are in fresh. Let's use that... - - /* - * Update branch targets and insert various attributes. Insert our exceptionHandlers into a sorted list, so they can be - * added in order later. - */ - InstructionHandle oldInstructionHandle = getBody().getStart(); - InstructionHandle newInstructionHandle = fresh.getStart(); - LinkedList exceptionList = new LinkedList(); - - Map localVariables = new HashMap(); - - int currLine = -1; - int lineNumberOffset = (fromFilename == null) ? 0 : getEnclosingClass().getSourceDebugExtensionOffset(fromFilename); - - while (oldInstructionHandle != null) { - if (map.get(oldInstructionHandle) == null) { - // must be a range instruction since they're the only things we - // didn't copy across - handleRangeInstruction(oldInstructionHandle, exceptionList); - // just increment ih. - oldInstructionHandle = oldInstructionHandle.getNext(); - } else { - // assert map.get(ih) == jh - Instruction oldInstruction = oldInstructionHandle.getInstruction(); - Instruction newInstruction = newInstructionHandle.getInstruction(); - - if (oldInstruction instanceof InstructionBranch) { - handleBranchInstruction(map, oldInstruction, newInstruction); - } - - // now deal with line numbers - // and store up info for local variables - for (InstructionTargeter targeter : oldInstructionHandle.getTargeters()) { - if (targeter instanceof LineNumberTag) { - int line = ((LineNumberTag) targeter).getLineNumber(); - if (line != currLine) { - gen.addLineNumber(newInstructionHandle, line + lineNumberOffset); - currLine = line; - } - } else if (targeter instanceof LocalVariableTag) { - LocalVariableTag lvt = (LocalVariableTag) targeter; - LVPosition p = localVariables.get(lvt); - // If we don't know about it, create a new position and - // store - // If we do know about it - update its end position - if (p == null) { - LVPosition newp = new LVPosition(); - newp.start = newp.end = newInstructionHandle; - localVariables.put(lvt, newp); - } else { - p.end = newInstructionHandle; - } - } - } - - // now continue - oldInstructionHandle = oldInstructionHandle.getNext(); - newInstructionHandle = newInstructionHandle.getNext(); - } - } - - addExceptionHandlers(gen, map, exceptionList); - if (originalMethodHasLocalVariableTable || enclosingClass - .getBcelObjectType() - .getResolvedTypeX() - .getWorld().generateNewLvts) { - if (localVariables.size() == 0) { - // Might be a case of 173978 where around advice on an execution join point - // has caused everything to be extracted from the method and thus we - // are left with no local variables, not even the ones for 'this' and - // parameters passed to the method - createNewLocalVariables(gen); - } else { - addLocalVariables(gen, localVariables); - } - } - - // JAVAC adds line number tables (with just one entry) to generated - // accessor methods - this - // keeps some tools that rely on finding at least some form of - // linenumbertable happy. - // Let's check if we have one - if we don't then let's add one. - // TODO Could be made conditional on whether line debug info is being - // produced - if (gen.getLineNumbers().length == 0) { - gen.addLineNumber(gen.getInstructionList().getStart(), 1); - } - } - - private void createNewLocalVariables(MethodGen gen) { - gen.removeLocalVariables(); - // ignore or for now - if (!getName().startsWith("<")) { - int slot = 0; - InstructionHandle start = gen.getInstructionList().getStart(); - InstructionHandle end = gen.getInstructionList().getEnd(); - // Add a 'this' if non-static - if (!isStatic()) { - String cname = this.enclosingClass.getClassName(); - if (cname == null) { - return; // give up for now - } - Type enclosingType = BcelWorld.makeBcelType(UnresolvedType.forName(cname)); - gen.addLocalVariable("this", enclosingType, slot++, start, end); - } - // Add entries for the method arguments - String[] paramNames = (memberView == null ? null : memberView.getParameterNames()); - if (paramNames != null) { - for (int i = 0; i < argumentTypes.length; i++) { - String pname = paramNames[i]; - if (pname == null) { - pname = "arg" + i; - } - gen.addLocalVariable(pname, argumentTypes[i], slot, start, end); - slot += argumentTypes[i].getSize(); - } - } - } - } - - private World getWorld() { - return enclosingClass.getBcelObjectType().getResolvedTypeX().getWorld(); - } - /* - * Optimized packing that does a 'local packing' of the code rather than building a brand new method and packing into it. Only - * usable when the packing is going to be done just once. - */ - public void optimizedPackBody(MethodGen gen) { - InstructionList theBody = getBody(); - InstructionHandle iHandle = theBody.getStart(); - - int currLine = -1; - int lineNumberOffset = (fromFilename == null) ? 0 : getEnclosingClass().getSourceDebugExtensionOffset(fromFilename); - Map localVariables = new HashMap(); - LinkedList exceptionList = new LinkedList(); - Set forDeletion = new HashSet(); - Set branchInstructions = new HashSet(); - // OPTIMIZE sort out in here: getRange()/insertHandler() and type of - // exceptionList - while (iHandle != null) { - Instruction inst = iHandle.getInstruction(); - // InstructionHandle nextInst = iHandle.getNext(); - // OPTIMIZE remove this instructionhandle as it now points to - // nowhere? - if (inst == Range.RANGEINSTRUCTION) { - Range r = Range.getRange(iHandle); - if (r instanceof ExceptionRange) { - ExceptionRange er = (ExceptionRange) r; - if (er.getStart() == iHandle) { - if (!er.isEmpty()) { - // order is important, insert handlers in order of start - insertHandler(er, exceptionList); - } - } - } - forDeletion.add(iHandle); - } else { - if (inst instanceof InstructionBranch) { - branchInstructions.add((BranchHandle) iHandle); - } - - for (InstructionTargeter targeter : iHandle.getTargetersCopy()) { - if (targeter instanceof LineNumberTag) { - int line = ((LineNumberTag) targeter).getLineNumber(); - if (line != currLine) { - gen.addLineNumber(iHandle, line + lineNumberOffset); - currLine = line; - } - } else if (targeter instanceof LocalVariableTag) { - LocalVariableTag lvt = (LocalVariableTag) targeter; - LVPosition p = localVariables.get(lvt); - // If we don't know about it, create a new position - // and store - // If we do know about it - update its end position - if (p == null) { - LVPosition newp = new LVPosition(); - newp.start = newp.end = iHandle; - localVariables.put(lvt, newp); - } else { - p.end = iHandle; - } - } - } - } - iHandle = iHandle.getNext(); - } - for (BranchHandle branchHandle : branchInstructions) { - handleBranchInstruction(branchHandle, forDeletion); - } - // now add exception handlers - for (ExceptionRange r : exceptionList) { - if (r.isEmpty()) { - continue; - } - gen.addExceptionHandler(jumpForward(r.getRealStart(), forDeletion), jumpForward(r.getRealEnd(), forDeletion), - jumpForward(r.getHandler(), forDeletion), - (r.getCatchType() == null) ? null : (ObjectType) BcelWorld.makeBcelType(r.getCatchType())); - } - - for (InstructionHandle handle : forDeletion) { - try { - theBody.delete(handle); - } catch (TargetLostException e) { - e.printStackTrace(); - } - } - gen.setInstructionList(theBody); - if (originalMethodHasLocalVariableTable || getWorld().generateNewLvts) { - if (localVariables.size() == 0) { - // Might be a case of 173978 where around advice on an execution join point - // has caused everything to be extracted from the method and thus we - // are left with no local variables, not even the ones for 'this' and - // parameters passed to the method - createNewLocalVariables(gen); - } else { - addLocalVariables(gen, localVariables); - } - } - // JAVAC adds line number tables (with just one entry) to generated - // accessor methods - this - // keeps some tools that rely on finding at least some form of - // linenumbertable happy. - // Let's check if we have one - if we don't then let's add one. - // TODO Could be made conditional on whether line debug info is being - // produced - if (gen.getLineNumbers().length == 0) { - gen.addLineNumber(gen.getInstructionList().getStart(), 1); - } - wasPackedOptimally = true; - } - - private void addLocalVariables(MethodGen gen, Map localVariables) { - // now add local variables - gen.removeLocalVariables(); - - // this next iteration _might_ be overkill, but we had problems with - // bcel before with duplicate local variables. Now that we're patching - // bcel we should be able to do without it if we're paranoid enough - // through the rest of the compiler. - InstructionHandle methodStart = gen.getInstructionList().getStart(); - InstructionHandle methodEnd = gen.getInstructionList().getEnd(); - - // Determine how many 'slots' are used by parameters to the method. - // Then below we can determine if a local variable is a parameter variable, if it is - // we force its range to from the method start (as it may have been shuffled down - // due to insertion of advice like cflow entry) - int paramSlots = gen.isStatic() ? 0 : 1; - Type[] argTypes = gen.getArgumentTypes(); - if (argTypes != null) { - for (int i = 0; i < argTypes.length; i++) { - if (argTypes[i].getSize() == 2) { - paramSlots += 2; - } else { - paramSlots += 1; - } - } - } - if (!this.enclosingClass.getWorld().generateNewLvts) { - // Here the generateNewLvts option is used to control "Do not damage unusually positioned local - // variables that represent method parameters". Strictly speaking local variables that represent - // method parameters effectively have a bytecode range from 0..end_of_method - however some - // tools generate bytecode that specifies a compressed range. The code below would normally - // extend the parameter local variables to cover the full method but by setting paramSlots to -1 - // here we cause the code below to avoid modifying any local vars that represent method - // parameters. - paramSlots = -1; - } - - Map> duplicatedLocalMap = new HashMap>(); - for (LocalVariableTag tag : localVariables.keySet()) { - // have we already added one with the same slot number and start - // location? - // if so, just continue. - LVPosition lvpos = localVariables.get(tag); - InstructionHandle start = (tag.getSlot() < paramSlots ? methodStart : lvpos.start); - InstructionHandle end = (tag.getSlot() < paramSlots ? methodEnd : lvpos.end); - Set slots = duplicatedLocalMap.get(start); - if (slots == null) { - slots = new HashSet(); - duplicatedLocalMap.put(start, slots); - } else if (slots.contains(new Integer(tag.getSlot()))) { - // we already have a var starting at this tag with this slot - continue; - } - slots.add(Integer.valueOf(tag.getSlot())); - Type t = tag.getRealType(); - if (t == null) { - t = BcelWorld.makeBcelType(UnresolvedType.forSignature(tag.getType())); - } - gen.addLocalVariable(tag.getName(), t, tag.getSlot(), start, end); - } - } - - private void addExceptionHandlers(MethodGen gen, Map map, - LinkedList exnList) { - // now add exception handlers - for (ExceptionRange r : exnList) { - if (r.isEmpty()) { - continue; - } - InstructionHandle rMappedStart = remap(r.getRealStart(), map); - InstructionHandle rMappedEnd = remap(r.getRealEnd(), map); - InstructionHandle rMappedHandler = remap(r.getHandler(), map); - gen.addExceptionHandler(rMappedStart, rMappedEnd, rMappedHandler, (r.getCatchType() == null) ? null - : (ObjectType) BcelWorld.makeBcelType(r.getCatchType())); - } - } - - private void handleBranchInstruction(Map map, Instruction oldInstruction, - Instruction newInstruction) { - InstructionBranch oldBranchInstruction = (InstructionBranch) oldInstruction; - InstructionBranch newBranchInstruction = (InstructionBranch) newInstruction; - InstructionHandle oldTarget = oldBranchInstruction.getTarget(); // old - // target - - // New target is in hash map - newBranchInstruction.setTarget(remap(oldTarget, map)); - - if (oldBranchInstruction instanceof InstructionSelect) { - // Either LOOKUPSWITCH or TABLESWITCH - InstructionHandle[] oldTargets = ((InstructionSelect) oldBranchInstruction).getTargets(); - InstructionHandle[] newTargets = ((InstructionSelect) newBranchInstruction).getTargets(); - - for (int k = oldTargets.length - 1; k >= 0; k--) { - // Update all targets - newTargets[k] = remap(oldTargets[k], map); - newTargets[k].addTargeter(newBranchInstruction); - } - } - } - - private InstructionHandle jumpForward(InstructionHandle t, Set handlesForDeletion) { - InstructionHandle target = t; - if (handlesForDeletion.contains(target)) { - do { - target = target.getNext(); - } while (handlesForDeletion.contains(target)); - } - return target; - } - - /** - * Process a branch instruction with respect to instructions that are about to be deleted. If the target for the branch is a - * candidate for deletion, move it to the next valid instruction after the deleted target. - */ - private void handleBranchInstruction(BranchHandle branchHandle, Set handlesForDeletion) { - InstructionBranch branchInstruction = (InstructionBranch) branchHandle.getInstruction(); - InstructionHandle target = branchInstruction.getTarget(); // old target - - if (handlesForDeletion.contains(target)) { - do { - target = target.getNext(); - } while (handlesForDeletion.contains(target)); - branchInstruction.setTarget(target); - } - - if (branchInstruction instanceof InstructionSelect) { - // Either LOOKUPSWITCH or TABLESWITCH - InstructionSelect iSelect = (InstructionSelect) branchInstruction; - InstructionHandle[] targets = iSelect.getTargets(); - for (int k = targets.length - 1; k >= 0; k--) { - InstructionHandle oneTarget = targets[k]; - if (handlesForDeletion.contains(oneTarget)) { - do { - oneTarget = oneTarget.getNext(); - } while (handlesForDeletion.contains(oneTarget)); - iSelect.setTarget(k, oneTarget); - oneTarget.addTargeter(branchInstruction); - } - } - } - } - - private void handleRangeInstruction(InstructionHandle ih, LinkedList exnList) { - // we're a range instruction - Range r = Range.getRange(ih); - if (r instanceof ExceptionRange) { - ExceptionRange er = (ExceptionRange) r; - if (er.getStart() == ih) { - // System.err.println("er " + er); - if (!er.isEmpty()) { - // order is important, insert handlers in order of start - insertHandler(er, exnList); - } - } - } else { - // we must be a shadow range or something equally useless, - // so forget about doing anything - } - } - - /* - * Make copies of all instructions, append them to the new list and associate old instruction references with the new ones, - * i.e., a 1:1 mapping. - */ - private Map copyAllInstructionsExceptRangeInstructionsInto(InstructionList intoList) { - Map map = new HashMap(); - for (InstructionHandle ih = getBody().getStart(); ih != null; ih = ih.getNext()) { - if (Range.isRangeHandle(ih)) { - continue; - } - Instruction inst = ih.getInstruction(); - Instruction copy = Utility.copyInstruction(inst); - - if (copy instanceof InstructionBranch) { - map.put(ih, intoList.append((InstructionBranch) copy)); - } else { - map.put(ih, intoList.append(copy)); - } - } - return map; - } - - /** - * This procedure should not currently be used. - */ - // public void killNops() { - // InstructionHandle curr = body.getStart(); - // while (true) { - // if (curr == null) break; - // InstructionHandle next = curr.getNext(); - // if (curr.getInstruction() instanceof NOP) { - // InstructionTargeter[] targeters = curr.getTargeters(); - // if (targeters != null) { - // for (int i = 0, len = targeters.length; i < len; i++) { - // InstructionTargeter targeter = targeters[i]; - // targeter.updateTarget(curr, next); - // } - // } - // try { - // body.delete(curr); - // } catch (TargetLostException e) { - // } - // } - // curr = next; - // } - // } - // private static InstructionHandle fNext(InstructionHandle ih) { - // while (true) { - // if (ih.getInstruction()==Range.RANGEINSTRUCTION) ih = ih.getNext(); - // else return ih; - // } - // } - private static InstructionHandle remap(InstructionHandle handle, Map map) { - while (true) { - InstructionHandle ret = map.get(handle); - if (ret == null) { - handle = handle.getNext(); - } else { - return ret; - } - } - } - - // Update to all these comments, ASC 11-01-2005 - // The right thing to do may be to do more with priorities as - // we create new exception handlers, but that is a relatively - // complex task. In the meantime, just taking account of the - // priority here enables a couple of bugs to be fixed to do - // with using return or break in code that contains a finally - // block (pr78021,pr79554). - - // exception ordering. - // What we should be doing is dealing with priority inversions way earlier - // than we are - // and counting on the tree structure. In which case, the below code is in - // fact right. - - // XXX THIS COMMENT BELOW IS CURRENTLY WRONG. - // An exception A preceeds an exception B in the exception table iff: - - // * A and B were in the original method, and A preceeded B in the original - // exception table - // * If A has a higher priority than B, than it preceeds B. - // * If A and B have the same priority, then the one whose START happens - // EARLIEST has LEAST priority. - // in short, the outermost exception has least priority. - // we implement this with a LinkedList. We could possibly implement this - // with a java.util.SortedSet, - // but I don't trust the only implementation, TreeSet, to do the right - // thing. - - /* private */static void insertHandler(ExceptionRange fresh, LinkedList l) { - // Old implementation, simply: l.add(0,fresh); - for (ListIterator iter = l.listIterator(); iter.hasNext();) { - ExceptionRange r = iter.next(); - // int freal = fresh.getRealStart().getPosition(); - // int rreal = r.getRealStart().getPosition(); - if (fresh.getPriority() >= r.getPriority()) { - iter.previous(); - iter.add(fresh); - return; - } - } - - // we have reached the end - l.add(fresh); - } - - public boolean isPrivate() { - return Modifier.isPrivate(getAccessFlags()); - } - - public boolean isProtected() { - return Modifier.isProtected(getAccessFlags()); - } - - public boolean isDefault() { - return !(isProtected() || isPrivate() || isPublic()); - } - - public boolean isPublic() { - return Modifier.isPublic(getAccessFlags()); - } - - // ---- - - /** - * A good body is a body with the following properties: - * - *

    - *
  • For each branch instruction S in body, target T of S is in body. - *
  • For each branch instruction S in body, target T of S has S as a targeter. - *
  • For each instruction T in body, for each branch instruction S that is a targeter of T, S is in body. - *
  • For each non-range-handle instruction T in body, for each instruction S that is a targeter of T, S is either a branch - * instruction, an exception range or a tag - *
  • For each range-handle instruction T in body, there is exactly one targeter S that is a range. - *
  • For each range-handle instruction T in body, the range R targeting T is in body. - *
  • For each instruction T in body, for each exception range R targeting T, R is in body. - *
  • For each exception range R in body, let T := R.handler. T is in body, and R is one of T's targeters - *
  • All ranges are properly nested: For all ranges Q and R, if Q.start preceeds R.start, then R.end preceeds Q.end. - *
- * - * Where the shorthand "R is in body" means "R.start is in body, R.end is in body, and any InstructionHandle stored in a field - * of R (such as an exception handle) is in body". - */ - - public void assertGoodBody() { - if (true) { - return; // only enable for debugging - } - assertGoodBody(getBody(), toString()); - } - - public static void assertGoodBody(InstructionList il, String from) { - if (true) { - return; // only to be enabled for debugging - } -// if (il == null) { -// return; -// } -// Set body = new HashSet(); -// Stack ranges = new Stack(); -// for (InstructionHandle ih = il.getStart(); ih != null; ih = ih.getNext()) { -// body.add(ih); -// if (ih.getInstruction() instanceof InstructionBranch) { -// body.add(ih.getInstruction()); -// } -// } -// -// for (InstructionHandle ih = il.getStart(); ih != null; ih = ih.getNext()) { -// assertGoodHandle(ih, body, ranges, from); -// Iterator tIter = ih.getTargeters().iterator(); -// while (tIter.hasNext()) { -// assertGoodTargeter(tIter.next(), ih, body, from); -// } -// } - } - -// private static void assertGoodHandle(InstructionHandle ih, Set body, Stack ranges, String from) { -// Instruction inst = ih.getInstruction(); -// if ((inst instanceof InstructionBranch) ^ (ih instanceof BranchHandle)) { -// throw new BCException("bad instruction/handle pair in " + from); -// } -// if (Range.isRangeHandle(ih)) { -// assertGoodRangeHandle(ih, body, ranges, from); -// } else if (inst instanceof InstructionBranch) { -// assertGoodBranchInstruction((BranchHandle) ih, (InstructionBranch) inst, body, ranges, from); -// } -// } - -// private static void assertGoodBranchInstruction(BranchHandle ih, InstructionBranch inst, Set body, Stack ranges, -// String from) { -// if (ih.getTarget() != inst.getTarget()) { -// throw new BCException("bad branch instruction/handle pair in " + from); -// } -// InstructionHandle target = ih.getTarget(); -// assertInBody(target, body, from); -// assertTargetedBy(target, inst, from); -// if (inst instanceof InstructionSelect) { -// InstructionSelect sel = (InstructionSelect) inst; -// InstructionHandle[] itargets = sel.getTargets(); -// for (int k = itargets.length - 1; k >= 0; k--) { -// assertInBody(itargets[k], body, from); -// assertTargetedBy(itargets[k], inst, from); -// } -// } -// } - - /** ih is an InstructionHandle or a BranchInstruction */ -// private static void assertInBody(Object ih, Set body, String from) { -// if (!body.contains(ih)) { -// throw new BCException("thing not in body in " + from); -// } -// } - -// private static void assertGoodRangeHandle(InstructionHandle ih, Set body, Stack ranges, String from) { -// Range r = getRangeAndAssertExactlyOne(ih, from); -// assertGoodRange(r, body, from); -// if (r.getStart() == ih) { -// ranges.push(r); -// } else if (r.getEnd() == ih) { -// if (ranges.peek() != r) { -// throw new BCException("bad range inclusion in " + from); -// } -// ranges.pop(); -// } -// } - -// private static void assertGoodRange(Range r, Set body, String from) { -// assertInBody(r.getStart(), body, from); -// assertRangeHandle(r.getStart(), from); -// assertTargetedBy(r.getStart(), r, from); -// -// assertInBody(r.getEnd(), body, from); -// assertRangeHandle(r.getEnd(), from); -// assertTargetedBy(r.getEnd(), r, from); -// -// if (r instanceof ExceptionRange) { -// ExceptionRange er = (ExceptionRange) r; -// assertInBody(er.getHandler(), body, from); -// assertTargetedBy(er.getHandler(), r, from); -// } -// } - -// private static void assertRangeHandle(InstructionHandle ih, String from) { -// if (!Range.isRangeHandle(ih)) { -// throw new BCException("bad range handle " + ih + " in " + from); -// } -// } - - private static void assertTargetedBy(InstructionHandle target, InstructionTargeter targeter, String from) { - Iterator tIter = target.getTargeters().iterator(); - while (tIter.hasNext()) { - if (((InstructionTargeter) tIter.next()) == targeter) { - return; - } - } - throw new RuntimeException("bad targeting relationship in " + from); - } - - private static void assertTargets(InstructionTargeter targeter, InstructionHandle target, String from) { - if (targeter instanceof Range) { - Range r = (Range) targeter; - if (r.getStart() == target || r.getEnd() == target) { - return; - } - if (r instanceof ExceptionRange) { - if (((ExceptionRange) r).getHandler() == target) { - return; - } - } - } else if (targeter instanceof InstructionBranch) { - InstructionBranch bi = (InstructionBranch) targeter; - if (bi.getTarget() == target) { - return; - } - if (targeter instanceof InstructionSelect) { - InstructionSelect sel = (InstructionSelect) targeter; - InstructionHandle[] itargets = sel.getTargets(); - for (int k = itargets.length - 1; k >= 0; k--) { - if (itargets[k] == target) { - return; - } - } - } - } else if (targeter instanceof Tag) { - return; - } - throw new BCException(targeter + " doesn't target " + target + " in " + from); - } - - private static Range getRangeAndAssertExactlyOne(InstructionHandle ih, String from) { - Range ret = null; - Iterator tIter = ih.getTargeters().iterator(); - if (!tIter.hasNext()) { - throw new BCException("range handle with no range in " + from); - } - while (tIter.hasNext()) { - InstructionTargeter ts = tIter.next(); - if (ts instanceof Range) { - if (ret != null) { - throw new BCException("range handle with multiple ranges in " + from); - } - ret = (Range) ts; - } - } - if (ret == null) { - throw new BCException("range handle with no range in " + from); - } - return ret; - } - -// private static void assertGoodTargeter(InstructionTargeter t, InstructionHandle ih, Set body, String from) { -// assertTargets(t, ih, from); -// if (t instanceof Range) { -// assertGoodRange((Range) t, body, from); -// } else if (t instanceof InstructionBranch) { -// assertInBody(t, body, from); -// } -// } - - // ---- - - boolean isAdviceMethod() { - if (memberView == null) { - return false; - } - return memberView.getAssociatedShadowMunger() != null; - } - - boolean isAjSynthetic() { - if (memberView == null) { - return true; - } - return memberView.isAjSynthetic(); - } - - boolean isSynthetic() { - if (memberView == null) { - return false; - } - return memberView.isSynthetic(); - } - - public ISourceLocation getSourceLocation() { - if (memberView != null) { - return memberView.getSourceLocation(); - } - return null; - } - - public AjAttribute.EffectiveSignatureAttribute getEffectiveSignature() { - // if (memberView == null) return null; - if (effectiveSignature != null) { - return effectiveSignature; - } - return memberView.getEffectiveSignature(); - } - - public void setEffectiveSignature(ResolvedMember member, Shadow.Kind kind, boolean shouldWeave) { - this.effectiveSignature = new AjAttribute.EffectiveSignatureAttribute(member, kind, shouldWeave); - } - - public String getSignature() { - if (memberView != null) { - return memberView.getSignature(); - } - return MemberImpl.typesToSignature(BcelWorld.fromBcel(getReturnType()), BcelWorld.fromBcel(getArgumentTypes()), false); - } - - public String getParameterSignature() { - if (memberView != null) { - return memberView.getParameterSignature(); - } - return MemberImpl.typesToSignature(BcelWorld.fromBcel(getArgumentTypes())); - } - - public BcelMethod getMemberView() { - return memberView; - } - - public void forcePublic() { - markAsChanged(); - modifiers = Utility.makePublic(modifiers); - } - - public boolean getCanInline() { - return canInline; - } - - public void setCanInline(boolean canInline) { - this.canInline = canInline; - } - - public void addAttribute(Attribute attribute) { - attributes.add(attribute); - } - - public String toTraceString() { - return toShortString(); - } - - public ConstantPool getConstantPool() { - return enclosingClass.getConstantPool(); - } - - public static boolean isConstructor(LazyMethodGen aMethod) { - return aMethod.getName().equals(""); - } - -} diff --git a/weaver/src/org/aspectj/weaver/bcel/Range.java b/weaver/src/org/aspectj/weaver/bcel/Range.java deleted file mode 100644 index fa162c526..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/Range.java +++ /dev/null @@ -1,243 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - -package org.aspectj.weaver.bcel; - -import java.util.Iterator; - -import org.aspectj.apache.bcel.generic.Instruction; -import org.aspectj.apache.bcel.generic.InstructionConstants; -import org.aspectj.apache.bcel.generic.InstructionHandle; -import org.aspectj.apache.bcel.generic.InstructionList; -import org.aspectj.apache.bcel.generic.InstructionTargeter; -import org.aspectj.weaver.BCException; - -abstract class Range implements InstructionTargeter { - - protected InstructionList body; - protected InstructionHandle start; - protected InstructionHandle end; - - // ---- initialization - - protected Range(InstructionList il) { - this.body = il; - } - - // ---- - - final InstructionList getBody() { - return body; - } - - final InstructionHandle getStart() { - return start; - } - - final InstructionHandle getEnd() { - return end; - } - - // ---- - - boolean isEmpty() { - InstructionHandle ih = start; - // System.err.println(" looking for " + end); - while (ih != end) { - // System.err.println(" ih " + ih); - if (!Range.isRangeHandle(ih)) { - return false; - } - ih = ih.getNext(); - } - return true; - } - - static InstructionHandle getRealStart(InstructionHandle ih) { - while (Range.isRangeHandle(ih)) { - ih = ih.getNext(); - } - return ih; - } - - InstructionHandle getRealStart() { - return getRealStart(start); - } - - static InstructionHandle getRealEnd(InstructionHandle ih) { - while (Range.isRangeHandle(ih)) { - ih = ih.getPrev(); - } - return ih; - } - - InstructionHandle getRealEnd() { - return getRealEnd(end); - } - - InstructionHandle getRealNext() { - return getRealStart(end); - } - - // ---- - - InstructionHandle insert(Instruction i, Where where) { - InstructionList il = new InstructionList(); - InstructionHandle ret = il.insert(i); - insert(il, where); - return ret; - } - - void insert(InstructionList freshIl, Where where) { - InstructionHandle h; - if (where == InsideBefore || where == OutsideBefore) { - h = getStart(); - } else { - h = getEnd(); - } - if (where == InsideBefore || where == OutsideAfter) { - body.append(h, freshIl); - } else { - InstructionHandle newStart = body.insert(h, freshIl); - if (where == OutsideBefore) { - // XXX this is slow. There's a better design than this. We should - // never have to retarget branches apart from the creation of ranges. - // basically, we should never weave OutsideBefore. - BcelShadow.retargetAllBranches(h, newStart); - } - } - - } - - InstructionHandle append(Instruction i) { - return insert(i, InsideAfter); - } - - void append(InstructionList i) { - insert(i, InsideAfter); - } - - private static void setLineNumberFromNext(InstructionHandle ih) { - int lineNumber = Utility.getSourceLine(ih.getNext()); - if (lineNumber != -1) { - Utility.setSourceLine(ih, lineNumber); - } - } - - static InstructionHandle genStart(InstructionList body) { - InstructionHandle ih = body.insert(Range.RANGEINSTRUCTION); - setLineNumberFromNext(ih); - return ih; - } - - static InstructionHandle genEnd(InstructionList body) { - return body.append(Range.RANGEINSTRUCTION); - } - - static InstructionHandle genStart(InstructionList body, InstructionHandle ih) { - if (ih == null) { - return genStart(body); - } - InstructionHandle freshIh = body.insert(ih, Range.RANGEINSTRUCTION); - setLineNumberFromNext(freshIh); - return freshIh; - } - - static InstructionHandle genEnd(InstructionList body, InstructionHandle ih) { - if (ih == null) { - return genEnd(body); - } - return body.append(ih, Range.RANGEINSTRUCTION); - } - - // ----- - - public boolean containsTarget(InstructionHandle ih) { - return false; - } - - public final void updateTarget(InstructionHandle old_ih, InstructionHandle new_ih) { - throw new RuntimeException("Ranges must be updated with an enclosing instructionList"); - } - - protected void updateTarget(InstructionHandle old_ih, InstructionHandle new_ih, InstructionList new_il) { - old_ih.removeTargeter(this); - if (new_ih != null) { - new_ih.addTargeter(this); - } - body = new_il; - - if (old_ih == start) { - start = new_ih; - } - if (old_ih == end) { - end = new_ih; - } - } - - public static final boolean isRangeHandle(InstructionHandle ih) { - if (ih == null) { - return false; - } - return ih.getInstruction() == Range.RANGEINSTRUCTION; - } - - protected static final Range getRange(InstructionHandle ih) { - // assert isRangeHandle(ih) - Range ret = null; - Iterator tIter = ih.getTargeters().iterator(); - while (tIter.hasNext()) { - InstructionTargeter targeter = tIter.next(); - if (targeter instanceof Range) { - Range r = (Range) targeter; - if (r.getStart() != ih && r.getEnd() != ih) { - continue; - } - if (ret != null) { - throw new BCException("multiple ranges on same range handle: " + ret + ", " + targeter); - } - ret = r; - } - } - if (ret == null) { - throw new BCException("shouldn't happen"); - } - return ret; - } - - // ---- - - static final Where InsideBefore = new Where("insideBefore"); - static final Where InsideAfter = new Where("insideAfter"); - static final Where OutsideBefore = new Where("outsideBefore"); - static final Where OutsideAfter = new Where("outsideAfter"); - - // ---- constants - - // note that this is STUPIDLY copied by Instruction.copy(), so don't do that. - - public static final Instruction RANGEINSTRUCTION = InstructionConstants.IMPDEP1; - - // ---- - - static class Where { - private String name; - - public Where(String name) { - this.name = name; - } - - public String toString() { - return name; - } - } -} diff --git a/weaver/src/org/aspectj/weaver/bcel/ShadowRange.java b/weaver/src/org/aspectj/weaver/bcel/ShadowRange.java deleted file mode 100644 index 47c8400c5..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/ShadowRange.java +++ /dev/null @@ -1,244 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - -package org.aspectj.weaver.bcel; - -import java.util.Iterator; - -import org.aspectj.apache.bcel.generic.Instruction; -import org.aspectj.apache.bcel.generic.InstructionBranch; -import org.aspectj.apache.bcel.generic.InstructionFactory; -import org.aspectj.apache.bcel.generic.InstructionHandle; -import org.aspectj.apache.bcel.generic.InstructionLV; -import org.aspectj.apache.bcel.generic.InstructionList; -import org.aspectj.apache.bcel.generic.InstructionSelect; -import org.aspectj.apache.bcel.generic.InstructionTargeter; -import org.aspectj.apache.bcel.generic.LocalVariableTag; -import org.aspectj.apache.bcel.generic.RET; -import org.aspectj.apache.bcel.generic.TargetLostException; -import org.aspectj.weaver.BCException; -import org.aspectj.weaver.IntMap; -import org.aspectj.weaver.Shadow; - -final class ShadowRange extends Range { - - private BcelShadow shadow; - - // ---- initialization - - /** - * After this constructor is called, this range is not well situated unless both {@link #associateWithTargets} and - * {@link #associateWithShadow} are called. - */ - public ShadowRange(InstructionList body) { - super(body); - } - - protected void associateWithTargets(InstructionHandle start, InstructionHandle end) { - // assert body.contains(start) && body.contains(end); - this.start = start; - this.end = end; - start.addTargeter(this); - end.addTargeter(this); - } - - public void associateWithShadow(BcelShadow shadow) { - this.shadow = shadow; - shadow.setRange(this); - } - - // ---- - - public Shadow.Kind getKind() { - return shadow.getKind(); - } - - @Override - public String toString() { - return shadow.toString(); - } - - void extractInstructionsInto(LazyMethodGen freshMethod, IntMap remap, boolean addReturn) { - LazyMethodGen.assertGoodBody(getBody(), toString()); - freshMethod.assertGoodBody(); - InstructionList freshBody = freshMethod.getBody(); - - for (InstructionHandle oldIh = start.getNext(); oldIh != end; oldIh = oldIh.getNext()) { - // first we copy the instruction itself. - Instruction oldI = oldIh.getInstruction(); - Instruction freshI = (oldI == RANGEINSTRUCTION) ? oldI : Utility.copyInstruction(oldI); - - // Now we add it to the new instruction list. - InstructionHandle freshIh; - if (freshI instanceof InstructionBranch) { - // If it's a targeting instruction, - // update the target(s) to point to the new copy instead of the old copy. - InstructionBranch oldBranch = (InstructionBranch) oldI; - InstructionBranch freshBranch = (InstructionBranch) freshI; - InstructionHandle oldTarget = oldBranch.getTarget(); - oldTarget.removeTargeter(oldBranch); - oldTarget.addTargeter(freshBranch); - if (freshBranch instanceof InstructionSelect) { - InstructionSelect oldSelect = (InstructionSelect) oldI; - InstructionSelect freshSelect = (InstructionSelect) freshI; - InstructionHandle[] oldTargets = freshSelect.getTargets(); - for (int k = oldTargets.length - 1; k >= 0; k--) { - oldTargets[k].removeTargeter(oldSelect); - oldTargets[k].addTargeter(freshSelect); - } - } - freshIh = freshBody.append(freshBranch); - } else { - freshIh = freshBody.append(freshI); - } - - // if source comes before target: - // source <--> target - // --> [process: target.removeTargeter(source); target.addTargeter(sourcecopy)] - // source ---------\ - // v - // sourcecopy <--> target - // --> [ process: sourcecopy.updateTarget(target, targetcopy) ] - // source ----> target - // sourcecopy <--> targetcopy - - // if target comes before source - - // target <--> source - // --> [process: source.updateTarget(target, targetcopy) ] - // target - // targetcopy <--> source - // --> [process: targetcopy.removeTargeter(source); targetcopy.addTargeter(sourcecopy)] - // target source - // v - // targetcopy <--> sourcecopy - - // now deal with the old instruction's targeters. Update them all to point to us - // instead of the old instruction. We use updateTarget to do this. One goal is - // to make sure we remove all targeters from the old guy, so we can successfully - // delete it. - for (InstructionTargeter source : oldIh.getTargetersCopy()) { - if (source instanceof LocalVariableTag) { - Shadow.Kind kind = getKind(); - if (kind == Shadow.AdviceExecution || kind == Shadow.ConstructorExecution || kind == Shadow.MethodExecution - || kind == Shadow.PreInitialization || kind == Shadow.Initialization - || kind == Shadow.StaticInitialization) { - LocalVariableTag sourceLocalVariableTag = (LocalVariableTag) source; - if (sourceLocalVariableTag.getSlot() == 0) { - // might be 'this' so should be renamed if being dumped in a static method 277616 - if (sourceLocalVariableTag.getName().equals("this")) { - sourceLocalVariableTag.setName("ajc$this"); - } - } - // if we're extracting a whole block we can do this... - source.updateTarget(oldIh, freshIh); - } else { - // XXX destroying local variable info - // but only for a call or get join point, so no big deal - source.updateTarget(oldIh, null); - } - } else if (source instanceof Range) { - // exceptions and shadows are just moved - ((Range) source).updateTarget(oldIh, freshIh, freshBody); - } else { - // line numbers can be shared, - // branches will be copied along with us. - source.updateTarget(oldIh, freshIh); - } - } - // we're now done with the old instruction entirely, and will ignore them through - // the rest of this loop. The only time we'll see them again is a second pass to - // delete them. - - // now deal with local variable instructions. If this points to a remapped - // frame location, update the instruction's index. If this doesn't, - // do compaction/expansion: allocate a new local variable, and modify the remap - // to handle it. XXX We're doing the safe thing and allocating ALL these local variables - // as double-wides, in case the location is found to hold a double-wide later. - if (freshI.isLocalVariableInstruction() || freshI instanceof RET) { - // IndexedInstruction indexedI = (IndexedInstruction) freshI; - int oldIndex = freshI.getIndex(); - int freshIndex; - if (!remap.hasKey(oldIndex)) { - freshIndex = freshMethod.allocateLocal(2); - remap.put(oldIndex, freshIndex); - } else { - freshIndex = remap.get(oldIndex); - } - if (freshI instanceof RET) { - freshI.setIndex(freshIndex); - } else { - freshI = ((InstructionLV) freshI).setIndexAndCopyIfNecessary(freshIndex); - freshIh.setInstruction(freshI); - } - } - // System.err.println("JUST COPIED: " + - // oldIh.getInstruction().toString(freshMethod.getEnclosingClass().getConstantPoolGen().getConstantPool()) - // + " INTO " + - // freshIh.getInstruction().toString(freshMethod.getEnclosingClass().getConstantPoolGen().getConstantPool())); - } - - // now go through again and update variable slots that have been altered as a result - // of remapping... - for (InstructionHandle newIh = freshBody.getStart(); newIh != freshBody.getEnd(); newIh = newIh.getNext()) { - Iterator tIter = newIh.getTargeters().iterator(); - while (tIter.hasNext()) { - InstructionTargeter source = tIter.next(); - if (source instanceof LocalVariableTag) { - LocalVariableTag lvt = (LocalVariableTag) source; - if (!lvt.isRemapped() && remap.hasKey(lvt.getSlot())) { - lvt.updateSlot(remap.get(lvt.getSlot())); - } - } - } - } - - // we've now copied out all the instructions. - // now delete the instructions... we've already taken care of the damn - // targets, but since TargetLostException is checked, we have to do this stuff. - try { - for (InstructionHandle oldIh = start.getNext(); oldIh != end;) { - InstructionHandle next = oldIh.getNext(); - body.delete(oldIh); - oldIh = next; - } - } catch (TargetLostException e) { - throw new BCException("shouldn't have gotten a target lost"); - } - - // now add the return, if one is warranted. - InstructionHandle ret = null; - if (addReturn) { - // we really should pull this out somewhere... - ret = freshBody.append(InstructionFactory.createReturn(freshMethod.getReturnType())); - } - // and remap all the old targeters of the end handle of the range to the return. - for (InstructionTargeter t : end.getTargetersCopy()) { - if (t == this) { - continue; - } - if (!addReturn) { - throw new BCException("range has target, but we aren't adding a return"); - } else { - t.updateTarget(end, ret); - } - } - - LazyMethodGen.assertGoodBody(getBody(), toString()); - freshMethod.assertGoodBody(); - } - - public BcelShadow getShadow() { - return shadow; - } - -} diff --git a/weaver/src/org/aspectj/weaver/bcel/TypeAnnotationAccessVar.java b/weaver/src/org/aspectj/weaver/bcel/TypeAnnotationAccessVar.java deleted file mode 100644 index e23174389..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/TypeAnnotationAccessVar.java +++ /dev/null @@ -1,80 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2005 IBM - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Andy Clement initial implementation - * ******************************************************************/ - -package org.aspectj.weaver.bcel; - -import org.aspectj.apache.bcel.Constants; -import org.aspectj.apache.bcel.generic.Instruction; -import org.aspectj.apache.bcel.generic.InstructionFactory; -import org.aspectj.apache.bcel.generic.InstructionList; -import org.aspectj.apache.bcel.generic.ObjectType; -import org.aspectj.apache.bcel.generic.Type; -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.UnresolvedType; - -/** - * Used for @this() @target() @args() - represents accessing an annotated 'thing'. Main use is to create the instructions that - * retrieve the annotation from the 'thing' - see createLoadInstructions() - */ -public class TypeAnnotationAccessVar extends BcelVar { - - private BcelVar target; - - public TypeAnnotationAccessVar(ResolvedType type, BcelVar theAnnotatedTargetIsStoredHere) { - super(type, 0); - target = theAnnotatedTargetIsStoredHere; - } - - public String toString() { - return "TypeAnnotationAccessVar(" + getType() + ")"; - } - - public Instruction createLoad(InstructionFactory fact) { - throw new RuntimeException("unimplemented"); - } - - public Instruction createStore(InstructionFactory fact) { - throw new RuntimeException("unimplemented"); - } - - public InstructionList createCopyFrom(InstructionFactory fact, int oldSlot) { - throw new RuntimeException("unimplemented"); - } - - public void appendLoad(InstructionList il, InstructionFactory fact) { - il.append(createLoadInstructions(getType(), fact)); - } - - public InstructionList createLoadInstructions(ResolvedType toType, InstructionFactory fact) { - InstructionList il = new InstructionList(); - Type jlClass = BcelWorld.makeBcelType(UnresolvedType.JL_CLASS); - Type jlaAnnotation = BcelWorld.makeBcelType(UnresolvedType.ANNOTATION); - il.append(target.createLoad(fact)); - il.append(fact.createInvoke("java/lang/Object", "getClass", jlClass, new Type[] {}, Constants.INVOKEVIRTUAL)); - il.append(fact.createConstant(new ObjectType(toType.getName()))); - il.append(fact.createInvoke("java/lang/Class", "getAnnotation", jlaAnnotation, new Type[] { jlClass }, - Constants.INVOKEVIRTUAL)); - il.append(Utility.createConversion(fact, jlaAnnotation, BcelWorld.makeBcelType(toType))); - return il; - - } - - public void appendLoadAndConvert(InstructionList il, InstructionFactory fact, ResolvedType toType) { - il.append(createLoadInstructions(toType, fact)); - - } - - public void insertLoad(InstructionList il, InstructionFactory fact) { - il.insert(createLoadInstructions(getType(), fact)); - } - -} diff --git a/weaver/src/org/aspectj/weaver/bcel/TypeDelegateResolver.java b/weaver/src/org/aspectj/weaver/bcel/TypeDelegateResolver.java deleted file mode 100644 index 2a29c927a..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/TypeDelegateResolver.java +++ /dev/null @@ -1,28 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2010 Contributors - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Andy Clement, SpringSource - * ******************************************************************/ -package org.aspectj.weaver.bcel; - -import org.aspectj.weaver.ReferenceType; -import org.aspectj.weaver.ReferenceTypeDelegate; - -/** - * A type delegate resolver is able to create type delegates for a named reference type. A type delegate will implement - * ReferenceTypeDelegate. There are three kind of delegate already in existence: those created for eclipse structures, those - * created for bytecode structures, and those created based on reflection. - * - * @author Andy Clement - */ -public interface TypeDelegateResolver { - - ReferenceTypeDelegate getDelegate(ReferenceType referenceType); - -} diff --git a/weaver/src/org/aspectj/weaver/bcel/UnwovenClassFile.java b/weaver/src/org/aspectj/weaver/bcel/UnwovenClassFile.java deleted file mode 100644 index 7076316f7..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/UnwovenClassFile.java +++ /dev/null @@ -1,200 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - -package org.aspectj.weaver.bcel; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.IOException; -import java.util.Collections; -import java.util.List; - -import org.aspectj.apache.bcel.classfile.JavaClass; -import org.aspectj.util.FileUtil; -import org.aspectj.weaver.IUnwovenClassFile; - -public class UnwovenClassFile implements IUnwovenClassFile { - protected String filename; - protected char[] charfilename; - protected byte[] bytes; - // protected JavaClass javaClass = null; - // protected byte[] writtenBytes = null; - protected List writtenChildClasses = Collections.emptyList(); - protected String className = null; - protected boolean isModule = false; - - public UnwovenClassFile(String filename, byte[] bytes) { - this.filename = filename; - this.isModule = filename.toLowerCase().endsWith("module-info.java"); - this.bytes = bytes; - } - - /** Use if the classname is known, saves a bytecode parse */ - public UnwovenClassFile(String filename, String classname, byte[] bytes) { - this.filename = filename; - this.isModule = filename.toLowerCase().endsWith("module-info.class"); - this.className = classname; - this.bytes = bytes; - } - - public boolean shouldBeWoven() { - // Skip module-info files for now, they aren't really types - return !isModule; - } - - public String getFilename() { - return filename; - } - - public String makeInnerFileName(String innerName) { - String prefix = filename.substring(0, filename.length() - 6); // strip the .class - return prefix + "$" + innerName + ".class"; - } - - public byte[] getBytes() { - // if (bytes == null) bytes = javaClass.getBytes(); - return bytes; - } - - public JavaClass getJavaClass() { - // XXX need to know when to make a new class and when not to - // XXX this is an important optimization - if (getBytes() == null) { - System.out.println("no bytes for: " + getFilename()); - // Thread.currentThread().dumpStack(); - Thread.dumpStack(); - } - return Utility.makeJavaClass(filename, getBytes()); - // if (javaClass == null) javaClass = Utility.makeJavaClass(filename, getBytes()); - // return javaClass; - } - - public void writeUnchangedBytes() throws IOException { - writeWovenBytes(getBytes(), Collections.emptyList()); - } - - public void writeWovenBytes(byte[] bytes, List childClasses) throws IOException { - writeChildClasses(childClasses); - - // System.err.println("should write: " + getClassName()); - - // System.err.println("about to write: " + this + ", " + writtenBytes + ", "); - // + writtenBytes != null + " && " + unchanged(bytes, writtenBytes) ); - - // if (writtenBytes != null && unchanged(bytes, writtenBytes)) return; - - // System.err.println(" actually wrote it"); - - BufferedOutputStream os = FileUtil.makeOutputStream(new File(filename)); - os.write(bytes); - os.close(); - - // writtenBytes = bytes; - } - - private void writeChildClasses(List childClasses) throws IOException { - // ??? we only really need to delete writtenChildClasses whose - // ??? names aren't in childClasses; however, it's unclear - // ??? how much that will affect performance - deleteAllChildClasses(); - - childClasses.removeAll(writtenChildClasses); // XXX is this right - - for (ChildClass childClass : childClasses) { - writeChildClassFile(childClass.name, childClass.bytes); - } - - writtenChildClasses = childClasses; - - } - - private void writeChildClassFile(String innerName, byte[] bytes) throws IOException { - BufferedOutputStream os = FileUtil.makeOutputStream(new File(makeInnerFileName(innerName))); - os.write(bytes); - os.close(); - } - - protected void deleteAllChildClasses() { - for (ChildClass childClass : writtenChildClasses) { - deleteChildClassFile(childClass.name); - } - } - - protected void deleteChildClassFile(String innerName) { - File childClassFile = new File(makeInnerFileName(innerName)); - childClassFile.delete(); - } - - /* private */static boolean unchanged(byte[] b1, byte[] b2) { - int len = b1.length; - if (b2.length != len) - return false; - for (int i = 0; i < len; i++) { - if (b1[i] != b2[i]) - return false; - } - return true; - } - - public char[] getClassNameAsChars() { - if (charfilename == null) { - charfilename = getClassName().replace('.', '/').toCharArray(); - } - return charfilename; - } - - public String getClassName() { - if (className == null) - className = getJavaClass().getClassName(); // OPTIMIZE quicker way to determine name??? surely? - return className; - } - - @Override - public String toString() { - return "UnwovenClassFile(" + filename + ", " + getClassName() + ")"; - } - - // record - // OPTIMIZE why is the 'short name' used here (the bit after the dollar) - seems we mess about a lot trimming it off only to put - // it back on! - public static class ChildClass { - public final String name; - public final byte[] bytes; - - ChildClass(String name, byte[] bytes) { - this.name = name; - this.bytes = bytes; - } - - @Override - public boolean equals(Object other) { - if (!(other instanceof ChildClass)) - return false; - ChildClass o = (ChildClass) other; - return o.name.equals(name) && unchanged(o.bytes, bytes); - } - - @Override - public int hashCode() { - return name.hashCode(); - } - - @Override - public String toString() { - return "(ChildClass " + name + ")"; - } - } - - public void setClassNameAsChars(char[] classNameAsChars) { - this.charfilename = classNameAsChars; - } -} diff --git a/weaver/src/org/aspectj/weaver/bcel/UnwovenClassFileWithThirdPartyManagedBytecode.java b/weaver/src/org/aspectj/weaver/bcel/UnwovenClassFileWithThirdPartyManagedBytecode.java deleted file mode 100644 index 18edc8413..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/UnwovenClassFileWithThirdPartyManagedBytecode.java +++ /dev/null @@ -1,36 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * IBM Corporation - initial API and implementation - *******************************************************************************/ -package org.aspectj.weaver.bcel; - -/** - * @author colyer This subclass of UnwovenClassFile allows a third-party to manage the actual bytes that comprise the class. This - * means the third party can return a reference to an existing array, or create the bytes on demand, or apply any other - * strategy that makes sense. By refering to bytes held elsewhere, the goal is to reduce the overall memory consumption by - * not holding a copy. - */ -public class UnwovenClassFileWithThirdPartyManagedBytecode extends UnwovenClassFile { - - IByteCodeProvider provider; - - public interface IByteCodeProvider { - byte[] getBytes(); - } - - // OPTIMIZE make classname an input char[] - public UnwovenClassFileWithThirdPartyManagedBytecode(String filename, String classname, IByteCodeProvider provider) { - super(filename, classname, null); - this.provider = provider; - } - - public byte[] getBytes() { - return provider.getBytes(); - } -} diff --git a/weaver/src/org/aspectj/weaver/bcel/Utility.java b/weaver/src/org/aspectj/weaver/bcel/Utility.java deleted file mode 100644 index 4acf032fc..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/Utility.java +++ /dev/null @@ -1,719 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - -package org.aspectj.weaver.bcel; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Hashtable; -import java.util.Iterator; -import java.util.List; - -import org.aspectj.apache.bcel.Constants; -import org.aspectj.apache.bcel.classfile.Attribute; -import org.aspectj.apache.bcel.classfile.ClassParser; -import org.aspectj.apache.bcel.classfile.ConstantPool; -import org.aspectj.apache.bcel.classfile.JavaClass; -import org.aspectj.apache.bcel.classfile.Unknown; -import org.aspectj.apache.bcel.classfile.annotation.ArrayElementValue; -import org.aspectj.apache.bcel.classfile.annotation.ElementValue; -import org.aspectj.apache.bcel.classfile.annotation.NameValuePair; -import org.aspectj.apache.bcel.classfile.annotation.SimpleElementValue; -import org.aspectj.apache.bcel.generic.ArrayType; -import org.aspectj.apache.bcel.generic.BasicType; -import org.aspectj.apache.bcel.generic.Instruction; -import org.aspectj.apache.bcel.generic.InstructionByte; -import org.aspectj.apache.bcel.generic.InstructionCP; -import org.aspectj.apache.bcel.generic.InstructionConstants; -import org.aspectj.apache.bcel.generic.InstructionFactory; -import org.aspectj.apache.bcel.generic.InstructionHandle; -import org.aspectj.apache.bcel.generic.InstructionList; -import org.aspectj.apache.bcel.generic.InstructionSelect; -import org.aspectj.apache.bcel.generic.InstructionShort; -import org.aspectj.apache.bcel.generic.InstructionTargeter; -import org.aspectj.apache.bcel.generic.LineNumberTag; -import org.aspectj.apache.bcel.generic.ObjectType; -import org.aspectj.apache.bcel.generic.ReferenceType; -import org.aspectj.apache.bcel.generic.SwitchBuilder; -import org.aspectj.apache.bcel.generic.TargetLostException; -import org.aspectj.apache.bcel.generic.Type; -import org.aspectj.bridge.ISourceLocation; -import org.aspectj.weaver.AjAttribute; -import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; -import org.aspectj.weaver.AnnotationAJ; -import org.aspectj.weaver.BCException; -import org.aspectj.weaver.ConstantPoolReader; -import org.aspectj.weaver.ISourceContext; -import org.aspectj.weaver.Lint; -import org.aspectj.weaver.Member; -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.UnresolvedType; -import org.aspectj.weaver.Utils; -import org.aspectj.weaver.World; - -public class Utility { - - private final static char PACKAGE_INITIAL_CHAR = AjAttribute.AttributePrefix.charAt(0); - - public static List readAjAttributes(String classname, Attribute[] as, ISourceContext context, World w, - AjAttribute.WeaverVersionInfo version, ConstantPoolReader dataDecompressor) { - List attributes = new ArrayList(); - - // first pass, look for version - List forSecondPass = new ArrayList(); - for (int i = as.length - 1; i >= 0; i--) { - Attribute a = as[i]; - if (a instanceof Unknown) { - Unknown u = (Unknown) a; - String name = u.getName(); - if (name.charAt(0) == PACKAGE_INITIAL_CHAR) { // 'o'rg.aspectj - if (name.startsWith(AjAttribute.AttributePrefix)) { - if (name.endsWith(WeaverVersionInfo.AttributeName)) { - version = (AjAttribute.WeaverVersionInfo) AjAttribute.read(version, name, u.getBytes(), context, w, - dataDecompressor); - if (version.getMajorVersion() > WeaverVersionInfo.getCurrentWeaverMajorVersion()) { - throw new BCException( - "Unable to continue, this version of AspectJ supports classes built with weaver version " - + WeaverVersionInfo.toCurrentVersionString() + " but the class " + classname - + " is version " + version.toString() + ". Please update your AspectJ."); - } - } - forSecondPass.add(u); - } - } - } - } - - // FIXASC why going backwards? is it important - for (int i = forSecondPass.size() - 1; i >= 0; i--) { - Unknown a = forSecondPass.get(i); - String name = a.getName(); - AjAttribute attr = AjAttribute.read(version, name, a.getBytes(), context, w, dataDecompressor); - if (attr != null) { - attributes.add(attr); - } - } - return attributes; - } - - /* - * Ensure we report a nice source location - particular in the case where the source info is missing (binary weave). - */ - public static String beautifyLocation(ISourceLocation isl) { - StringBuffer nice = new StringBuffer(); - if (isl == null || isl.getSourceFile() == null || isl.getSourceFile().getName().indexOf("no debug info available") != -1) { - nice.append("no debug info available"); - } else { - // can't use File.getName() as this fails when a Linux box - // encounters a path created on Windows and vice-versa - int takeFrom = isl.getSourceFile().getPath().lastIndexOf('/'); - if (takeFrom == -1) { - takeFrom = isl.getSourceFile().getPath().lastIndexOf('\\'); - } - nice.append(isl.getSourceFile().getPath().substring(takeFrom + 1)); - if (isl.getLine() != 0) { - nice.append(":").append(isl.getLine()); - } - } - return nice.toString(); - } - - public static Instruction createSuperInvoke(InstructionFactory fact, BcelWorld world, Member signature) { - short kind; - if (Modifier.isInterface(signature.getModifiers())) { - throw new RuntimeException("bad"); - } else if (Modifier.isPrivate(signature.getModifiers()) || signature.getName().equals("")) { - throw new RuntimeException("unimplemented, possibly bad"); - } else if (Modifier.isStatic(signature.getModifiers())) { - throw new RuntimeException("bad"); - } else { - kind = Constants.INVOKESPECIAL; - } - - return fact.createInvoke(signature.getDeclaringType().getName(), signature.getName(), - BcelWorld.makeBcelType(signature.getReturnType()), BcelWorld.makeBcelTypes(signature.getParameterTypes()), kind); - } - - public static Instruction createInvoke(InstructionFactory fact, BcelWorld world, Member signature) { - short kind; - int signatureModifiers = signature.getModifiers(); - if (Modifier.isInterface(signatureModifiers)) { - kind = Constants.INVOKEINTERFACE; - } else if (Modifier.isStatic(signatureModifiers)) { - kind = Constants.INVOKESTATIC; - } else if (Modifier.isPrivate(signatureModifiers) || signature.getName().equals("")) { - kind = Constants.INVOKESPECIAL; - } else { - kind = Constants.INVOKEVIRTUAL; - } - - UnresolvedType targetType = signature.getDeclaringType(); - if (targetType.isParameterizedType()) { - targetType = targetType.resolve(world).getGenericType(); - } - return fact.createInvoke(targetType.getName(), signature.getName(), BcelWorld.makeBcelType(signature.getReturnType()), - BcelWorld.makeBcelTypes(signature.getParameterTypes()), kind); - } - - public static Instruction createGet(InstructionFactory fact, Member signature) { - short kind; - if (Modifier.isStatic(signature.getModifiers())) { - kind = Constants.GETSTATIC; - } else { - kind = Constants.GETFIELD; - } - - return fact.createFieldAccess(signature.getDeclaringType().getName(), signature.getName(), - BcelWorld.makeBcelType(signature.getReturnType()), kind); - } - - public static Instruction createSet(InstructionFactory fact, Member signature) { - short kind; - if (Modifier.isStatic(signature.getModifiers())) { - kind = Constants.PUTSTATIC; - } else { - kind = Constants.PUTFIELD; - } - - return fact.createFieldAccess(signature.getDeclaringType().getName(), signature.getName(), - BcelWorld.makeBcelType(signature.getReturnType()), kind); - } - - public static Instruction createInstanceof(InstructionFactory fact, ReferenceType t) { - int cpoolEntry = (t instanceof ArrayType) ? fact.getConstantPool().addArrayClass((ArrayType) t) : fact.getConstantPool() - .addClass((ObjectType) t); - return new InstructionCP(Constants.INSTANCEOF, cpoolEntry); - } - - public static Instruction createInvoke(InstructionFactory fact, LazyMethodGen m) { - short kind; - if (m.getEnclosingClass().isInterface()) { - if (m.isStatic()) { - // For static methods on interfaces - kind = Constants.INVOKESTATIC; - } else { - kind = Constants.INVOKEINTERFACE; - } - } else if (m.isStatic()) { - kind = Constants.INVOKESTATIC; - } else if (m.isPrivate() || m.getName().equals("")) { - kind = Constants.INVOKESPECIAL; - } else { - kind = Constants.INVOKEVIRTUAL; - } - - return fact.createInvoke(m.getClassName(), m.getName(), m.getReturnType(), m.getArgumentTypes(), kind, m.getEnclosingClass().isInterface()); - } - - /** - * Create an invoke instruction - * - * @param fact - * @param kind INVOKEINTERFACE, INVOKEVIRTUAL.. - * @param member - * @return - */ - public static Instruction createInvoke(InstructionFactory fact, short kind, Member member) { - return fact.createInvoke(member.getDeclaringType().getName(), member.getName(), - BcelWorld.makeBcelType(member.getReturnType()), BcelWorld.makeBcelTypes(member.getParameterTypes()), kind); - } - - private static String[] argNames = new String[] { "arg0", "arg1", "arg2", "arg3", "arg4" }; - - // ??? these should perhaps be cached. Remember to profile this to see if - // it's a problem. - public static String[] makeArgNames(int n) { - String[] ret = new String[n]; - for (int i = 0; i < n; i++) { - if (i < 5) { - ret[i] = argNames[i]; - } else { - ret[i] = "arg" + i; - } - } - return ret; - } - - // Lookup table, for converting between pairs of types, it gives - // us the method name in the Conversions class - private static Hashtable validBoxing = new Hashtable(); - - static { - validBoxing.put("Ljava/lang/Byte;B", "byteObject"); - validBoxing.put("Ljava/lang/Character;C", "charObject"); - validBoxing.put("Ljava/lang/Double;D", "doubleObject"); - validBoxing.put("Ljava/lang/Float;F", "floatObject"); - validBoxing.put("Ljava/lang/Integer;I", "intObject"); - validBoxing.put("Ljava/lang/Long;J", "longObject"); - validBoxing.put("Ljava/lang/Short;S", "shortObject"); - validBoxing.put("Ljava/lang/Boolean;Z", "booleanObject"); - validBoxing.put("BLjava/lang/Byte;", "byteValue"); - validBoxing.put("CLjava/lang/Character;", "charValue"); - validBoxing.put("DLjava/lang/Double;", "doubleValue"); - validBoxing.put("FLjava/lang/Float;", "floatValue"); - validBoxing.put("ILjava/lang/Integer;", "intValue"); - validBoxing.put("JLjava/lang/Long;", "longValue"); - validBoxing.put("SLjava/lang/Short;", "shortValue"); - validBoxing.put("ZLjava/lang/Boolean;", "booleanValue"); - } - - public static void appendConversion(InstructionList il, InstructionFactory fact, ResolvedType fromType, ResolvedType toType) { - if (!toType.isConvertableFrom(fromType) && !fromType.isConvertableFrom(toType)) { - throw new BCException("can't convert from " + fromType + " to " + toType); - } - // XXX I'm sure this test can be simpler but my brain hurts and this works - World w = toType.getWorld(); - if (w == null) { // dbg349636 - throw new IllegalStateException("Debug349636: Unexpectedly found world null for type " + toType.getName()); - } - - if (!w.isInJava5Mode()) { - if (toType.needsNoConversionFrom(fromType)) { - return; - } - } else { - if (toType.needsNoConversionFrom(fromType) && !(toType.isPrimitiveType() ^ fromType.isPrimitiveType())) { - return; - } - } - if (toType.equals(UnresolvedType.VOID)) { - // assert fromType.equals(UnresolvedType.OBJECT) - il.append(InstructionFactory.createPop(fromType.getSize())); - } else if (fromType.equals(UnresolvedType.VOID)) { - // assert toType.equals(UnresolvedType.OBJECT) - il.append(InstructionFactory.createNull(Type.OBJECT)); - return; - } else if (fromType.equals(UnresolvedType.OBJECT)) { - Type to = BcelWorld.makeBcelType(toType); - if (toType.isPrimitiveType()) { - String name = toType.toString() + "Value"; - il.append(fact.createInvoke("org.aspectj.runtime.internal.Conversions", name, to, new Type[] { Type.OBJECT }, - Constants.INVOKESTATIC)); - } else { - il.append(fact.createCheckCast((ReferenceType) to)); - } - } else if (toType.equals(UnresolvedType.OBJECT)) { - // assert fromType.isPrimitive() - Type from = BcelWorld.makeBcelType(fromType); - String name = fromType.toString() + "Object"; - il.append(fact.createInvoke("org.aspectj.runtime.internal.Conversions", name, Type.OBJECT, new Type[] { from }, - Constants.INVOKESTATIC)); - } else if (toType.getWorld().isInJava5Mode() && validBoxing.get(toType.getSignature() + fromType.getSignature()) != null) { - // XXX could optimize by using any java boxing code that may be just - // before the call... - Type from = BcelWorld.makeBcelType(fromType); - Type to = BcelWorld.makeBcelType(toType); - String name = validBoxing.get(toType.getSignature() + fromType.getSignature()); - if (toType.isPrimitiveType()) { - il.append(fact.createInvoke("org.aspectj.runtime.internal.Conversions", name, to, new Type[] { Type.OBJECT }, - Constants.INVOKESTATIC)); - } else { - il.append(fact.createInvoke("org.aspectj.runtime.internal.Conversions", name, Type.OBJECT, new Type[] { from }, - Constants.INVOKESTATIC)); - il.append(fact.createCheckCast((ReferenceType) to)); - } - } else if (fromType.isPrimitiveType()) { - // assert toType.isPrimitive() - Type from = BcelWorld.makeBcelType(fromType); - Type to = BcelWorld.makeBcelType(toType); - try { - Instruction i = fact.createCast(from, to); - if (i != null) { - il.append(i); - } else { - il.append(fact.createCast(from, Type.INT)); - il.append(fact.createCast(Type.INT, to)); - } - } catch (RuntimeException e) { - il.append(fact.createCast(from, Type.INT)); - il.append(fact.createCast(Type.INT, to)); - } - } else { - Type to = BcelWorld.makeBcelType(toType); - // assert ! fromType.isPrimitive() && ! toType.isPrimitive() - il.append(fact.createCheckCast((ReferenceType) to)); - } - } - - public static InstructionList createConversion(InstructionFactory factory, Type fromType, Type toType) { - return createConversion(factory, fromType, toType, false); - } - - public static InstructionList createConversion(InstructionFactory fact, Type fromType, Type toType, boolean allowAutoboxing) { - // System.out.println("cast to: " + toType); - - InstructionList il = new InstructionList(); - - // PR71273 - if ((fromType.equals(Type.BYTE) || fromType.equals(Type.CHAR) || fromType.equals(Type.SHORT)) && (toType.equals(Type.INT))) { - return il; - } - - if (fromType.equals(toType)) { - return il; - } - if (toType.equals(Type.VOID)) { - il.append(InstructionFactory.createPop(fromType.getSize())); - return il; - } - - if (fromType.equals(Type.VOID)) { - if (toType instanceof BasicType) { - throw new BCException("attempting to cast from void to basic type"); - } - il.append(InstructionFactory.createNull(Type.OBJECT)); - return il; - } - - if (fromType.equals(Type.OBJECT)) { - if (toType instanceof BasicType) { - String name = toType.toString() + "Value"; - il.append(fact.createInvoke("org.aspectj.runtime.internal.Conversions", name, toType, new Type[] { Type.OBJECT }, - Constants.INVOKESTATIC)); - return il; - } - } - - if (toType.equals(Type.OBJECT)) { - if (fromType instanceof BasicType) { - String name = fromType.toString() + "Object"; - il.append(fact.createInvoke("org.aspectj.runtime.internal.Conversions", name, Type.OBJECT, new Type[] { fromType }, - Constants.INVOKESTATIC)); - return il; - } else if (fromType instanceof ReferenceType) { - return il; - } else { - throw new RuntimeException(); - } - } - - if (fromType instanceof ReferenceType && ((ReferenceType) fromType).isAssignmentCompatibleWith(toType)) { - return il; - } - - if (allowAutoboxing) { - if (toType instanceof BasicType && fromType instanceof ReferenceType) { - // unboxing - String name = toType.toString() + "Value"; - il.append(fact.createInvoke("org.aspectj.runtime.internal.Conversions", name, toType, new Type[] { Type.OBJECT }, - Constants.INVOKESTATIC)); - return il; - } - - if (fromType instanceof BasicType && toType instanceof ReferenceType) { - // boxing - String name = fromType.toString() + "Object"; - il.append(fact.createInvoke("org.aspectj.runtime.internal.Conversions", name, Type.OBJECT, new Type[] { fromType }, - Constants.INVOKESTATIC)); - il.append(fact.createCast(Type.OBJECT, toType)); - return il; - } - } - - il.append(fact.createCast(fromType, toType)); - return il; - } - - public static Instruction createConstant(InstructionFactory fact, int value) { - Instruction inst; - switch (value) { - case -1: - inst = InstructionConstants.ICONST_M1; - break; - case 0: - inst = InstructionConstants.ICONST_0; - break; - case 1: - inst = InstructionConstants.ICONST_1; - break; - case 2: - inst = InstructionConstants.ICONST_2; - break; - case 3: - inst = InstructionConstants.ICONST_3; - break; - case 4: - inst = InstructionConstants.ICONST_4; - break; - case 5: - inst = InstructionConstants.ICONST_5; - break; - default: - if (value <= Byte.MAX_VALUE && value >= Byte.MIN_VALUE) { - inst = new InstructionByte(Constants.BIPUSH, (byte) value); - } else if (value <= Short.MAX_VALUE && value >= Short.MIN_VALUE) { - inst = new InstructionShort(Constants.SIPUSH, (short) value); - } else { - int ii = fact.getClassGen().getConstantPool().addInteger(value); - inst = new InstructionCP(value <= Constants.MAX_BYTE ? Constants.LDC : Constants.LDC_W, ii); - } - break; - } - return inst; - } - - /** For testing purposes: bit clunky but does work */ - public static int testingParseCounter = 0; - - public static JavaClass makeJavaClass(String filename, byte[] bytes) { - try { - testingParseCounter++; - ClassParser parser = new ClassParser(new ByteArrayInputStream(bytes), filename); - return parser.parse(); - } catch (IOException e) { - throw new BCException("malformed class file"); - } - } - - /** - * replace an instruction handle with another instruction, in this case, a branch instruction. - * - * @param ih the instruction handle to replace. - * @param branchInstruction the branch instruction to replace ih with - * @param enclosingMethod where to find ih's instruction list. - */ - public static void replaceInstruction(InstructionHandle ih, InstructionList replacementInstructions, - LazyMethodGen enclosingMethod) { - InstructionList il = enclosingMethod.getBody(); - InstructionHandle fresh = il.append(ih, replacementInstructions); - deleteInstruction(ih, fresh, enclosingMethod); - } - - /** - * delete an instruction handle and retarget all targeters of the deleted instruction to the next instruction. Obviously, this - * should not be used to delete a control transfer instruction unless you know what you're doing. - * - * @param ih the instruction handle to delete. - * @param enclosingMethod where to find ih's instruction list. - */ - public static void deleteInstruction(InstructionHandle ih, LazyMethodGen enclosingMethod) { - deleteInstruction(ih, ih.getNext(), enclosingMethod); - } - - /** - * delete an instruction handle and retarget all targeters of the deleted instruction to the provided target. - * - * @param ih the instruction handle to delete - * @param retargetTo the instruction handle to retarget targeters of ih to. - * @param enclosingMethod where to find ih's instruction list. - */ - public static void deleteInstruction(InstructionHandle ih, InstructionHandle retargetTo, LazyMethodGen enclosingMethod) { - InstructionList il = enclosingMethod.getBody(); - for (InstructionTargeter targeter : ih.getTargetersCopy()) { - targeter.updateTarget(ih, retargetTo); - } - ih.removeAllTargeters(); - try { - il.delete(ih); - } catch (TargetLostException e) { - throw new BCException("this really can't happen"); - } - } - - /** - * Fix for Bugzilla #39479, #40109 patch contributed by Andy Clement - * - * Need to manually copy Select instructions - if we rely on the the 'fresh' object created by copy(), the InstructionHandle - * array 'targets' inside the Select object will not have been deep copied, so modifying targets in fresh will modify the - * original Select - not what we want ! (It is a bug in BCEL to do with cloning Select objects). - * - *
-	 * declare error:
-	 *     call(* Instruction.copy()) && within(org.aspectj.weaver)
-	 *       && !withincode(* Utility.copyInstruction(Instruction)):
-	 *     "use Utility.copyInstruction to work-around bug in Select.copy()";
-	 * 
- */ - public static Instruction copyInstruction(Instruction i) { - if (i instanceof InstructionSelect) { - InstructionSelect freshSelect = (InstructionSelect) i; - - // Create a new targets array that looks just like the existing one - InstructionHandle[] targets = new InstructionHandle[freshSelect.getTargets().length]; - for (int ii = 0; ii < targets.length; ii++) { - targets[ii] = freshSelect.getTargets()[ii]; - } - - // Create a new select statement with the new targets array - - return new SwitchBuilder(freshSelect.getMatchs(), targets, freshSelect.getTarget()).getInstruction(); - } else { - return i.copy(); // Use clone for shallow copy... - } - } - - /** returns -1 if no source line attribute */ - // this naive version overruns the JVM stack size, if only Java understood - // tail recursion... - // public static int getSourceLine(InstructionHandle ih) { - // if (ih == null) return -1; - // - // InstructionTargeter[] ts = ih.getTargeters(); - // if (ts != null) { - // for (int j = ts.length - 1; j >= 0; j--) { - // InstructionTargeter t = ts[j]; - // if (t instanceof LineNumberTag) { - // return ((LineNumberTag)t).getLineNumber(); - // } - // } - // } - // return getSourceLine(ih.getNext()); - // } - public static int getSourceLine(InstructionHandle ih) {// ,boolean - // goforwards) { - int lookahead = 0; - // arbitrary rule that we will never lookahead more than 100 - // instructions for a line # - while (lookahead++ < 100) { - if (ih == null) { - return -1; - } - Iterator tIter = ih.getTargeters().iterator(); - while (tIter.hasNext()) { - InstructionTargeter t = tIter.next(); - if (t instanceof LineNumberTag) { - return ((LineNumberTag) t).getLineNumber(); - } - } - // if (goforwards) ih=ih.getNext(); else - ih = ih.getPrev(); - } - // System.err.println("no line information available for: " + ih); - return -1; - } - - // public static int getSourceLine(InstructionHandle ih) { - // return getSourceLine(ih,false); - // } - - // assumes that there is no already extant source line tag. Otherwise we'll - // have to be better. - public static void setSourceLine(InstructionHandle ih, int lineNumber) { - // OPTIMIZE LineNumberTag instances for the same line could be shared - // throughout a method... - ih.addTargeter(new LineNumberTag(lineNumber)); - } - - public static int makePublic(int i) { - return i & ~(Modifier.PROTECTED | Modifier.PRIVATE) | Modifier.PUBLIC; - } - - public static BcelVar[] pushAndReturnArrayOfVars(ResolvedType[] proceedParamTypes, InstructionList il, InstructionFactory fact, - LazyMethodGen enclosingMethod) { - int len = proceedParamTypes.length; - BcelVar[] ret = new BcelVar[len]; - - for (int i = len - 1; i >= 0; i--) { - ResolvedType typeX = proceedParamTypes[i]; - Type type = BcelWorld.makeBcelType(typeX); - int local = enclosingMethod.allocateLocal(type); - - il.append(InstructionFactory.createStore(type, local)); - ret[i] = new BcelVar(typeX, local); - } - return ret; - } - - public static boolean isConstantPushInstruction(Instruction i) { - long ii = Constants.instFlags[i.opcode]; - return ((ii & Constants.PUSH_INST) != 0 && (ii & Constants.CONSTANT_INST) != 0); - } - - /** - * Checks for suppression specified on the member or on the declaring type of that member - */ - public static boolean isSuppressing(Member member, String lintkey) { - boolean isSuppressing = Utils.isSuppressing(member.getAnnotations(), lintkey); - if (isSuppressing) { - return true; - } - UnresolvedType type = member.getDeclaringType(); - if (type instanceof ResolvedType) { - return Utils.isSuppressing(((ResolvedType) type).getAnnotations(), lintkey); - } - return false; - } - - public static List getSuppressedWarnings(AnnotationAJ[] anns, Lint lint) { - if (anns == null) { - return Collections.emptyList(); - } - // Go through the annotation types - List suppressedWarnings = new ArrayList(); - boolean found = false; - for (int i = 0; !found && i < anns.length; i++) { - // Check for the SuppressAjWarnings annotation - if (UnresolvedType.SUPPRESS_AJ_WARNINGS.getSignature().equals( - ((BcelAnnotation) anns[i]).getBcelAnnotation().getTypeSignature())) { - found = true; - // Two possibilities: - // 1. there are no values specified (i.e. @SuppressAjWarnings) - // 2. there are values specified (i.e. @SuppressAjWarnings("A") - // or @SuppressAjWarnings({"A","B"}) - List vals = ((BcelAnnotation) anns[i]).getBcelAnnotation().getValues(); - if (vals == null || vals.isEmpty()) { // (1) - suppressedWarnings.addAll(lint.allKinds()); - } else { // (2) - // We know the value is an array value - ArrayElementValue array = (ArrayElementValue) (vals.get(0)).getValue(); - ElementValue[] values = array.getElementValuesArray(); - for (int j = 0; j < values.length; j++) { - // We know values in the array are strings - SimpleElementValue value = (SimpleElementValue) values[j]; - Lint.Kind lintKind = lint.getLintKind(value.getValueString()); - if (lintKind != null) { - suppressedWarnings.add(lintKind); - } - } - } - } - } - return suppressedWarnings; - } - - // not yet used... - // public static boolean isSimple(Method method) { - // if (method.getCode()==null) return true; - // if (method.getCode().getCode().length>10) return false; - // InstructionList instrucs = new - // InstructionList(method.getCode().getCode()); // expensive! - // InstructionHandle InstrHandle = instrucs.getStart(); - // while (InstrHandle != null) { - // Instruction Instr = InstrHandle.getInstruction(); - // int opCode = Instr.opcode; - // // if current instruction is a branch instruction, see if it's a backward - // branch. - // // if it is return immediately (can't be trivial) - // if (Instr instanceof InstructionBranch) { - // // InstructionBranch BI = (InstructionBranch) Instr; - // if (Instr.getIndex() < 0) return false; - // } else if (Instr instanceof InvokeInstruction) { - // // if current instruction is an invocation, indicate that it can't be - // trivial - // return false; - // } - // InstrHandle = InstrHandle.getNext(); - // } - // return true; - // } - - public static Attribute bcelAttribute(AjAttribute a, ConstantPool pool) { - int nameIndex = pool.addUtf8(a.getNameString()); - byte[] bytes = a.getBytes(new BcelConstantPoolWriter(pool)); - int length = bytes.length; - - return new Unknown(nameIndex, length, bytes, pool); - } -} \ No newline at end of file diff --git a/weaver/src/org/aspectj/weaver/bcel/asm/AsmDetector.java b/weaver/src/org/aspectj/weaver/bcel/asm/AsmDetector.java deleted file mode 100644 index 5d5bb6990..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/asm/AsmDetector.java +++ /dev/null @@ -1,36 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2008 Contributors - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Andy Clement - * ******************************************************************/ -package org.aspectj.weaver.bcel.asm; - -import java.lang.reflect.Method; - -/** - * Determines if a version of asm is around that will enable us to add stack map attributes to classes that we produce. - * - * @author Andy Clement - */ -public class AsmDetector { - - public static boolean isAsmAround; - - static { - try { - Class reader = Class.forName("aj.org.objectweb.asm.ClassReader"); - Class visitor = Class.forName("aj.org.objectweb.asm.ClassVisitor"); - Method m = reader.getMethod("accept", new Class[] { visitor, Integer.TYPE }); - isAsmAround = m != null; - } catch (Exception e) { - isAsmAround = false; - } - // System.out.println(isAsmAround?"ASM detected":"No ASM found"); - } -} diff --git a/weaver/src/org/aspectj/weaver/bcel/asm/StackMapAdder.java b/weaver/src/org/aspectj/weaver/bcel/asm/StackMapAdder.java deleted file mode 100644 index dd3965b61..000000000 --- a/weaver/src/org/aspectj/weaver/bcel/asm/StackMapAdder.java +++ /dev/null @@ -1,121 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2008, 2018 Contributors - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Andy Clement - * ******************************************************************/ -package org.aspectj.weaver.bcel.asm; - -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.UnresolvedType; -import org.aspectj.weaver.World; - -import aj.org.objectweb.asm.ClassReader; -import aj.org.objectweb.asm.ClassVisitor; -import aj.org.objectweb.asm.ClassWriter; -import aj.org.objectweb.asm.MethodVisitor; -import aj.org.objectweb.asm.Opcodes; - -/** - * Uses asm to add the stack map attribute to methods in a class. The class is passed in as pure byte data and then a reader/writer - * process it. The writer is wired into the world so that types can be resolved and getCommonSuperClass() can be implemented without - * class loading using the context class loader. - * - * It is important that the constant pool is preserved here and asm does not try to remove unused entries. That is because some - * entries are refered to from classfile attributes. Asm cannot see into these attributes so does not realise the constant pool - * entries are in use. In order to ensure the copying of cp occurs, we use the variant super constructor call in AspectJConnectClassWriter - * that passes in the classreader. However, ordinarily that change causes a further optimization: that if a classreader sees - * a methodvisitor that has been created by a ClassWriter then it just copies the data across without changing it (and so it - * fails to attach the stackmapattribute). In order to avoid this further optimization we use our own minimal MethodVisitor. - * - * @author Andy Clement - */ -public class StackMapAdder { - - public static byte[] addStackMaps(World world, byte[] data) { - try { - ClassReader cr = new ClassReader(data); - ClassWriter cw = new AspectJConnectClassWriter(cr, world); - ClassVisitor cv = new AspectJClassVisitor(cw); - cr.accept(cv, 0); - return cw.toByteArray(); - } catch (Throwable t) { - System.err.println("AspectJ Internal Error: unable to add stackmap attributes. " + t.getMessage()); - AsmDetector.isAsmAround = false; - return data; - } - } - - private static class AspectJClassVisitor extends ClassVisitor { - - public AspectJClassVisitor(ClassVisitor classwriter) { - super(Opcodes.ASM7, classwriter); - } - - @Override - public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { - MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); - return new AJMethodVisitor(mv); - } - - // Minimal pass through MethodVisitor just so that the ClassReader doesn't see one that has been directly - // created by a ClassWriter (see top level class comment) - static class AJMethodVisitor extends MethodVisitor { - public AJMethodVisitor(MethodVisitor mv) { - super(Opcodes.ASM7,mv); - } - } - - } - - private static class AspectJConnectClassWriter extends ClassWriter { - private final World world; - - public AspectJConnectClassWriter(ClassReader cr, World w) { - super(cr, ClassWriter.COMPUTE_FRAMES); // passing in cr is necessary so cpool isnt modified (see 2.2.4 of asm doc) - this.world = w; - } - - - // Implementation of getCommonSuperClass() that avoids Class.forName() - @Override - protected String getCommonSuperClass(final String type1, final String type2) { - - ResolvedType resolvedType1 = world.resolve(UnresolvedType.forName(type1.replace('/', '.'))); - ResolvedType resolvedType2 = world.resolve(UnresolvedType.forName(type2.replace('/', '.'))); - - if (resolvedType1.isAssignableFrom(resolvedType2)) { - return type1; - } - - if (resolvedType2.isAssignableFrom(resolvedType1)) { - return type2; - } - - if (resolvedType1.isInterface() || resolvedType2.isInterface()) { - return "java/lang/Object"; - } else { - do { - resolvedType1 = resolvedType1.getSuperclass(); - if (resolvedType1 == null) { - // This happens if some types are missing, the getSuperclass() call on - // MissingResolvedTypeWithKnownSignature will return the Missing type which - // in turn returns a superclass of null. By returning Object here it - // should surface the cantFindType message raised in the first problematic - // getSuperclass call - return "java/lang/Object"; - } - if (resolvedType1.isParameterizedOrGenericType()) { - resolvedType1 = resolvedType1.getRawType(); - } - } while (!resolvedType1.isAssignableFrom(resolvedType2)); - return resolvedType1.getRawName().replace('.', '/'); - } - } - } -} diff --git a/weaver/src/org/aspectj/weaver/loadtime/IWeavingContext.java b/weaver/src/org/aspectj/weaver/loadtime/IWeavingContext.java deleted file mode 100644 index 51b781e86..000000000 --- a/weaver/src/org/aspectj/weaver/loadtime/IWeavingContext.java +++ /dev/null @@ -1,92 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2005 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * David Knibb initial implementation - *******************************************************************************/ -package org.aspectj.weaver.loadtime; - -import java.io.IOException; -import java.net.URL; -import java.util.Enumeration; -import java.util.List; - -import org.aspectj.weaver.loadtime.definition.Definition; -import org.aspectj.weaver.tools.WeavingAdaptor; - -/** - * This class adds support to AspectJ for an OSGi environment - * - * @author David Knibb - */ -public interface IWeavingContext { - - /** - * Allows the standard ClassLoader.getResources() mechanisms to be - * replaced with a different implementation. - * In an OSGi environment, this will allow for filtering to take - * place on the results of ClassLoader.getResources(). In a non-OSGi - * environment, ClassLoader.getResources should be returned. - * @param name the name of the resource to search for - * @return an enumeration containing all of the matching resources found - * @throws IOException - */ - public Enumeration getResources(String name) throws IOException; - - /** - * In an OSGi environment, determin which bundle a URL originated from. - * In a non-OSGi environment, implementors should return null. - * @param url - * @return - * @deprecated use getFile() or getClassLoaderName() - */ - public String getBundleIdFromURL(URL url); - - /** - * In an environment with multiple class loaders allows each to be - * identified using something safer and possibly shorter than toString - * @return name of the associated class loader - */ - public String getClassLoaderName (); - - public ClassLoader getClassLoader(); - - /** - * Format a URL - * @return filename - */ - public String getFile(URL url); - - /** - * In an environment with multiple class loaders allows messages - * to identified according to the weaving context - * @return short name - */ - public String getId (); - - /** - * Return true if the classloader associated with this weaving context - * is the one that will define the class with the specified name. - * In a delegating classloader hierarchy this might check the parent won't - * define it and the child will - in OSGi it will do something else. - * @param classname name of the class, eg. "java.lang.String" - * @return true if the associated classloader will define the class - */ - public boolean isLocallyDefined(String classname); - - /** - * Allow custom parsing of aop.xml or alternative mechanism for providing - * Definitions - * - * @param loader - * @param adaptor - * @return List containing 0 or more Definition instances - */ - public List getDefinitions(final ClassLoader loader, WeavingAdaptor adaptor); - -} diff --git a/weaver/src/org/aspectj/weaver/loadtime/definition/Definition.java b/weaver/src/org/aspectj/weaver/loadtime/definition/Definition.java deleted file mode 100644 index de780156e..000000000 --- a/weaver/src/org/aspectj/weaver/loadtime/definition/Definition.java +++ /dev/null @@ -1,222 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2005 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * Alexandre Vasseur initial implementation - *******************************************************************************/ -package org.aspectj.weaver.loadtime.definition; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * A POJO that contains raw strings from the XML (sort of XMLBean for our simple LTW DTD) - * - * @author
Alexandre Vasseur - */ -public class Definition { - - private final StringBuffer weaverOptions; - private final List dumpPatterns; - private boolean dumpBefore; - private boolean perClassloaderDumpDir; - private final List includePatterns; - private final List excludePatterns; - private final List aspectClassNames; - private final List aspectExcludePatterns; - private final List aspectIncludePatterns; - private final List concreteAspects; - - /** - * When aspects are defined, they can specify a scope type pattern and then will only apply to types matching that pattern. - */ - private final Map scopedAspects; - - /** - * Some aspects (from aspect libraries) will describe a type that must be around for them to function properly - */ - private final Map requiredTypesForAspects; - - public Definition() { - weaverOptions = new StringBuffer(); - dumpBefore = false; - perClassloaderDumpDir = false; - dumpPatterns = new ArrayList(); - includePatterns = new ArrayList(); - excludePatterns = new ArrayList(); - aspectClassNames = new ArrayList(); - aspectExcludePatterns = new ArrayList(); - aspectIncludePatterns = new ArrayList(); - concreteAspects = new ArrayList(); - scopedAspects = new HashMap(); - requiredTypesForAspects = new HashMap(); - } - - public String getWeaverOptions() { - return weaverOptions.toString(); - } - - public List getDumpPatterns() { - return dumpPatterns; - } - - public void setDumpBefore(boolean b) { - dumpBefore = b; - } - - public boolean shouldDumpBefore() { - return dumpBefore; - } - - public void setCreateDumpDirPerClassloader(boolean b) { - perClassloaderDumpDir = b; - } - - public boolean createDumpDirPerClassloader() { - return perClassloaderDumpDir; - } - - public List getIncludePatterns() { - return includePatterns; - } - - public List getExcludePatterns() { - return excludePatterns; - } - - public List getAspectClassNames() { - return aspectClassNames; - } - - public List getAspectExcludePatterns() { - return aspectExcludePatterns; - } - - public List getAspectIncludePatterns() { - return aspectIncludePatterns; - } - - public List getConcreteAspects() { - return concreteAspects; - } - - public static class ConcreteAspect { - public final String name; - public final String extend; - public final String precedence; - public final List pointcuts; - public final List declareAnnotations; - public final List pointcutsAndAdvice; - public final String perclause; - public List deows; - - public ConcreteAspect(String name, String extend) { - this(name, extend, null, null); - } - - public ConcreteAspect(String name, String extend, String precedence, String perclause) { - this.name = name; - // make sure extend set to null if "" - if (extend == null || extend.length() == 0) { - this.extend = null; - if (precedence == null || precedence.length() == 0) { - // if (pointcutsAndAdvice.size() == 0) { - // throw new RuntimeException("Not allowed"); - // } - } - } else { - this.extend = extend; - } - this.precedence = precedence; - this.pointcuts = new ArrayList(); - this.declareAnnotations = new ArrayList(); - this.pointcutsAndAdvice = new ArrayList(); - this.deows = new ArrayList(); - this.perclause = perclause; - } - } - - public static class Pointcut { - public final String name; - public final String expression; - - public Pointcut(String name, String expression) { - this.name = name; - this.expression = expression; - } - } - - public enum AdviceKind { - Before, After, AfterReturning, AfterThrowing, Around; - } - - public enum DeclareAnnotationKind { - Method, Field, Type; - } - - public static class DeclareAnnotation { - public final DeclareAnnotationKind declareAnnotationKind; - public final String pattern; - public final String annotation; - - public DeclareAnnotation(DeclareAnnotationKind kind, String pattern, String annotation) { - this.declareAnnotationKind = kind; - this.pattern = pattern; - this.annotation = annotation; - } - } - - public static class PointcutAndAdvice { - public final AdviceKind adviceKind; - public final String pointcut; - public final String adviceClass; // com.foo.Bar - public final String adviceMethod; // foo(java.lang.String,org.aspectj.lang.JoinPoint) - - public PointcutAndAdvice(AdviceKind adviceKind, String pointcut, String adviceClass, String adviceMethod) { - this.adviceKind = adviceKind; - this.pointcut = pointcut; - this.adviceClass = adviceClass; - this.adviceMethod = adviceMethod; - } - } - - public static class DeclareErrorOrWarning { - public final boolean isError; - public final String pointcut; - public final String message; - - public DeclareErrorOrWarning(boolean isError, String pointcut, String message) { - this.isError = isError; - this.pointcut = pointcut; - this.message = message; - } - } - - public void appendWeaverOptions(String option) { - weaverOptions.append(option.trim()).append(' '); - } - - public void addScopedAspect(String name, String scopePattern) { - scopedAspects.put(name, scopePattern); - } - - public String getScopeForAspect(String name) { - return scopedAspects.get(name); - } - - public void setAspectRequires(String name, String requiredType) { - requiredTypesForAspects.put(name, requiredType); - } - - public String getAspectRequires(String name) { - return requiredTypesForAspects.get(name); - } - -} diff --git a/weaver/src/org/aspectj/weaver/loadtime/definition/DocumentParser.java b/weaver/src/org/aspectj/weaver/loadtime/definition/DocumentParser.java deleted file mode 100644 index 7ff275fd7..000000000 --- a/weaver/src/org/aspectj/weaver/loadtime/definition/DocumentParser.java +++ /dev/null @@ -1,382 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2005 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * Alexandre Vasseur initial implementation - * Abraham Nevado - Lucierna simple caching strategy - *******************************************************************************/ -package org.aspectj.weaver.loadtime.definition; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.Hashtable; - -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.parsers.SAXParserFactory; - -import org.aspectj.util.LangUtil; -import org.aspectj.weaver.loadtime.definition.Definition.AdviceKind; -import org.aspectj.weaver.loadtime.definition.Definition.DeclareAnnotation; -import org.aspectj.weaver.loadtime.definition.Definition.DeclareAnnotationKind; -import org.xml.sax.Attributes; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.SAXParseException; -import org.xml.sax.XMLReader; -import org.xml.sax.helpers.DefaultHandler; -import org.xml.sax.helpers.XMLReaderFactory; - -/** - * - * @author Alexandre Vasseur - * @author A. Nevado - * @author Andy Clement - */ -public class DocumentParser extends DefaultHandler { - /** - * The current DTD public id. The matching dtd will be searched as a resource. - */ - private final static String DTD_PUBLIC_ID = "-//AspectJ//DTD 1.5.0//EN"; - - /** - * The DTD alias, for better user experience. - */ - private final static String DTD_PUBLIC_ID_ALIAS = "-//AspectJ//DTD//EN"; - - private final static String ASPECTJ_ELEMENT = "aspectj"; - private final static String WEAVER_ELEMENT = "weaver"; - private final static String DUMP_ELEMENT = "dump"; - private final static String DUMP_BEFOREANDAFTER_ATTRIBUTE = "beforeandafter"; - private final static String DUMP_PERCLASSLOADERDIR_ATTRIBUTE = "perclassloaderdumpdir"; - private final static String INCLUDE_ELEMENT = "include"; - private final static String EXCLUDE_ELEMENT = "exclude"; - private final static String OPTIONS_ATTRIBUTE = "options"; - private final static String ASPECTS_ELEMENT = "aspects"; - private final static String ASPECT_ELEMENT = "aspect"; - private final static String CONCRETE_ASPECT_ELEMENT = "concrete-aspect"; - private final static String NAME_ATTRIBUTE = "name"; - private final static String SCOPE_ATTRIBUTE = "scope"; - private final static String REQUIRES_ATTRIBUTE = "requires"; - private final static String EXTEND_ATTRIBUTE = "extends"; - private final static String PRECEDENCE_ATTRIBUTE = "precedence"; - private final static String PERCLAUSE_ATTRIBUTE = "perclause"; - private final static String POINTCUT_ELEMENT = "pointcut"; - private final static String BEFORE_ELEMENT = "before"; - private final static String AFTER_ELEMENT = "after"; - private final static String AFTER_RETURNING_ELEMENT = "after-returning"; - private final static String AFTER_THROWING_ELEMENT = "after-throwing"; - private final static String AROUND_ELEMENT = "around"; - private final static String WITHIN_ATTRIBUTE = "within"; - private final static String EXPRESSION_ATTRIBUTE = "expression"; - private final static String DECLARE_ANNOTATION_ELEMENT = "declare-annotation"; - - private final Definition definition; - - private boolean inAspectJ; - private boolean inWeaver; - private boolean inAspects; - - private Definition.ConcreteAspect activeConcreteAspectDefinition; - - private static Hashtable parsedFiles = new Hashtable(); - private static boolean CACHE; - private static final boolean LIGHTPARSER; - - static { - boolean value = false; - try { - value = System.getProperty("org.aspectj.weaver.loadtime.configuration.cache", "true").equalsIgnoreCase("true"); - } catch (Throwable t) { - t.printStackTrace(); - } - CACHE = value; - - value = false; - try { - value = System.getProperty("org.aspectj.weaver.loadtime.configuration.lightxmlparser", "false") - .equalsIgnoreCase("true"); - } catch (Throwable t) { - t.printStackTrace(); - } - LIGHTPARSER = value; - } - - private DocumentParser() { - definition = new Definition(); - } - - public static Definition parse(final URL url) throws Exception { - if (CACHE && parsedFiles.containsKey(url.toString())) { - return parsedFiles.get(url.toString()); - } - Definition def = null; - - if (LIGHTPARSER) { - def = SimpleAOPParser.parse(url); - } else { - def = saxParsing(url); - } - - if (CACHE && def.getAspectClassNames().size() > 0) { - parsedFiles.put(url.toString(), def); - } - - return def; - } - - private static Definition saxParsing(URL url) throws SAXException, ParserConfigurationException, IOException { - DocumentParser parser = new DocumentParser(); - - XMLReader xmlReader = getXMLReader(); - xmlReader.setContentHandler(parser); - xmlReader.setErrorHandler(parser); - - try { - xmlReader.setFeature("http://xml.org/sax/features/validation", false); - } catch (SAXException e) { - // fine, the parser don't do validation - } - try { - xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", false); - } catch (SAXException e) { - // fine, the parser don't do validation - } - try { - xmlReader.setFeature("http://xml.org/sax/features/external-parameter-entities", false); - } catch (SAXException e) { - // fine, the parser don't do validation - } - - xmlReader.setEntityResolver(parser); - InputStream in = url.openStream(); - xmlReader.parse(new InputSource(in)); - return parser.definition; - } - - private static XMLReader getXMLReader() throws SAXException, ParserConfigurationException { - XMLReader xmlReader = null; - /* Try this first for Java 5 */ - try { - xmlReader = XMLReaderFactory.createXMLReader(); - } - - /* .. and ignore "System property ... not set" and then try this instead */ - catch (SAXException ex) { - xmlReader = SAXParserFactory.newInstance().newSAXParser().getXMLReader(); - } - return xmlReader; - } - - public InputSource resolveEntity(String publicId, String systemId) throws SAXException { - if (publicId.equals(DTD_PUBLIC_ID) || publicId.equals(DTD_PUBLIC_ID_ALIAS)) { - InputStream in = DocumentParser.class.getResourceAsStream("/aspectj_1_5_0.dtd"); - if (in == null) { - System.err.println("AspectJ - WARN - could not read DTD " + publicId); - return null; - } else { - return new InputSource(in); - } - } else { - System.err.println("AspectJ - WARN - unknown DTD " + publicId + " - consider using " + DTD_PUBLIC_ID); - return null; - } - } - - public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { - if (ASPECT_ELEMENT.equals(qName)) { - String name = attributes.getValue(NAME_ATTRIBUTE); - String scopePattern = replaceXmlAnd(attributes.getValue(SCOPE_ATTRIBUTE)); - String requiredType = attributes.getValue(REQUIRES_ATTRIBUTE); - if (!isNull(name)) { - definition.getAspectClassNames().add(name); - if (scopePattern != null) { - definition.addScopedAspect(name, scopePattern); - } - if (requiredType != null) { - definition.setAspectRequires(name, requiredType); - } - } - } else if (WEAVER_ELEMENT.equals(qName)) { - String options = attributes.getValue(OPTIONS_ATTRIBUTE); - if (!isNull(options)) { - definition.appendWeaverOptions(options); - } - inWeaver = true; - } else if (CONCRETE_ASPECT_ELEMENT.equals(qName)) { - String name = attributes.getValue(NAME_ATTRIBUTE); - String extend = attributes.getValue(EXTEND_ATTRIBUTE); - String precedence = attributes.getValue(PRECEDENCE_ATTRIBUTE); - String perclause = attributes.getValue(PERCLAUSE_ATTRIBUTE); - if (!isNull(name)) { - activeConcreteAspectDefinition = new Definition.ConcreteAspect(name, extend, precedence, perclause); - // if (isNull(precedence) && !isNull(extend)) {// if no precedence, then extends must be there - // m_lastConcreteAspect = new Definition.ConcreteAspect(name, extend); - // } else if (!isNull(precedence)) { - // // wether a pure precedence def, or an extendsANDprecedence def. - // m_lastConcreteAspect = new Definition.ConcreteAspect(name, extend, precedence, perclause); - // } - definition.getConcreteAspects().add(activeConcreteAspectDefinition); - } - } else if (POINTCUT_ELEMENT.equals(qName) && activeConcreteAspectDefinition != null) { - String name = attributes.getValue(NAME_ATTRIBUTE); - String expression = attributes.getValue(EXPRESSION_ATTRIBUTE); - if (!isNull(name) && !isNull(expression)) { - activeConcreteAspectDefinition.pointcuts.add(new Definition.Pointcut(name, replaceXmlAnd(expression))); - } - } else if (DECLARE_ANNOTATION_ELEMENT.equals(qName) && activeConcreteAspectDefinition!=null) { - String methodSig = attributes.getValue("method"); - String fieldSig = attributes.getValue("field"); - String typePat = attributes.getValue("type"); - String anno = attributes.getValue("annotation"); - if (isNull(anno)) { - throw new SAXException("Badly formed element, 'annotation' value is missing"); - } - if (isNull(methodSig) && isNull(fieldSig) && isNull(typePat)) { - throw new SAXException("Badly formed element, need one of 'method'/'field'/'type' specified"); - } - if (!isNull(methodSig)) { - // declare @method - activeConcreteAspectDefinition.declareAnnotations.add(new Definition.DeclareAnnotation(DeclareAnnotationKind.Method, - methodSig, anno)); - } else if (!isNull(fieldSig)) { - // declare @field - activeConcreteAspectDefinition.declareAnnotations.add(new Definition.DeclareAnnotation(DeclareAnnotationKind.Field, - fieldSig, anno)); - } else if (!isNull(typePat)) { - // declare @type - activeConcreteAspectDefinition.declareAnnotations.add(new Definition.DeclareAnnotation(DeclareAnnotationKind.Type, - typePat, anno)); - } - } else if (BEFORE_ELEMENT.equals(qName) && activeConcreteAspectDefinition != null) { - String pointcut = attributes.getValue(POINTCUT_ELEMENT); - String adviceClass = attributes.getValue("invokeClass"); - String adviceMethod = attributes.getValue("invokeMethod"); - if (!isNull(pointcut) && !isNull(adviceClass) && !isNull(adviceMethod)) { - activeConcreteAspectDefinition.pointcutsAndAdvice.add(new Definition.PointcutAndAdvice(AdviceKind.Before, - replaceXmlAnd(pointcut), adviceClass, adviceMethod)); - } else { - throw new SAXException("Badly formed element"); - } - } else if (AFTER_ELEMENT.equals(qName) && activeConcreteAspectDefinition != null) { - String pointcut = attributes.getValue(POINTCUT_ELEMENT); - String adviceClass = attributes.getValue("invokeClass"); - String adviceMethod = attributes.getValue("invokeMethod"); - if (!isNull(pointcut) && !isNull(adviceClass) && !isNull(adviceMethod)) { - activeConcreteAspectDefinition.pointcutsAndAdvice.add(new Definition.PointcutAndAdvice(AdviceKind.After, - replaceXmlAnd(pointcut), adviceClass, adviceMethod)); - } else { - throw new SAXException("Badly formed element"); - } - } else if (AROUND_ELEMENT.equals(qName) && activeConcreteAspectDefinition != null) { - String pointcut = attributes.getValue(POINTCUT_ELEMENT); - String adviceClass = attributes.getValue("invokeClass"); - String adviceMethod = attributes.getValue("invokeMethod"); - if (!isNull(pointcut) && !isNull(adviceClass) && !isNull(adviceMethod)) { - activeConcreteAspectDefinition.pointcutsAndAdvice.add(new Definition.PointcutAndAdvice(AdviceKind.Around, - replaceXmlAnd(pointcut), adviceClass, adviceMethod)); - } else { - throw new SAXException("Badly formed element"); - } - } else if (ASPECTJ_ELEMENT.equals(qName)) { - if (inAspectJ) { - throw new SAXException("Found nested element"); - } - inAspectJ = true; - } else if (ASPECTS_ELEMENT.equals(qName)) { - inAspects = true; - } else if (INCLUDE_ELEMENT.equals(qName) && inWeaver) { - String typePattern = getWithinAttribute(attributes); - if (!isNull(typePattern)) { - definition.getIncludePatterns().add(typePattern); - } - } else if (EXCLUDE_ELEMENT.equals(qName) && inWeaver) { - String typePattern = getWithinAttribute(attributes); - if (!isNull(typePattern)) { - definition.getExcludePatterns().add(typePattern); - } - } else if (DUMP_ELEMENT.equals(qName) && inWeaver) { - String typePattern = getWithinAttribute(attributes); - if (!isNull(typePattern)) { - definition.getDumpPatterns().add(typePattern); - } - String beforeAndAfter = attributes.getValue(DUMP_BEFOREANDAFTER_ATTRIBUTE); - if (isTrue(beforeAndAfter)) { - definition.setDumpBefore(true); - } - String perWeaverDumpDir = attributes.getValue(DUMP_PERCLASSLOADERDIR_ATTRIBUTE); - if (isTrue(perWeaverDumpDir)) { - definition.setCreateDumpDirPerClassloader(true); - } - } else if (EXCLUDE_ELEMENT.equals(qName) && inAspects) { - String typePattern = getWithinAttribute(attributes); - if (!isNull(typePattern)) { - definition.getAspectExcludePatterns().add(typePattern); - } - } else if (INCLUDE_ELEMENT.equals(qName) && inAspects) { - String typePattern = getWithinAttribute(attributes); - if (!isNull(typePattern)) { - definition.getAspectIncludePatterns().add(typePattern); - } - } else { - throw new SAXException("Unknown element while parsing element: " + qName); - } - super.startElement(uri, localName, qName, attributes); - } - - private String getWithinAttribute(Attributes attributes) { - return replaceXmlAnd(attributes.getValue(WITHIN_ATTRIBUTE)); - } - - public void endElement(String uri, String localName, String qName) throws SAXException { - if (CONCRETE_ASPECT_ELEMENT.equals(qName)) { - activeConcreteAspectDefinition = null; - } else if (ASPECTJ_ELEMENT.equals(qName)) { - inAspectJ = false; - } else if (WEAVER_ELEMENT.equals(qName)) { - inWeaver = false; - } else if (ASPECTS_ELEMENT.equals(qName)) { - inAspects = false; - } - super.endElement(uri, localName, qName); - } - - // TODO AV - define what we want for XML parser error - for now stderr - public void warning(SAXParseException e) throws SAXException { - super.warning(e); - } - - public void error(SAXParseException e) throws SAXException { - super.error(e); - } - - public void fatalError(SAXParseException e) throws SAXException { - super.fatalError(e); - } - - private static String replaceXmlAnd(String expression) { - // TODO AV do we need to handle "..)AND" or "AND(.." ? - return LangUtil.replace(expression, " AND ", " && "); - } - - private boolean isNull(String s) { - return (s == null || s.length() <= 0); - } - - private boolean isTrue(String s) { - return (s != null && s.equals("true")); - } - - /** - * Turn off caching - */ - public static void deactivateCaching() { - CACHE = false; - } - -} diff --git a/weaver/src/org/aspectj/weaver/loadtime/definition/LightXMLParser.java b/weaver/src/org/aspectj/weaver/loadtime/definition/LightXMLParser.java deleted file mode 100644 index 09a9df96f..000000000 --- a/weaver/src/org/aspectj/weaver/loadtime/definition/LightXMLParser.java +++ /dev/null @@ -1,471 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2011 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * Abraham Nevado - Lucierna initial implementation - *******************************************************************************/ -package org.aspectj.weaver.loadtime.definition; - -import java.io.Reader; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; - -public class LightXMLParser { - - private final static char NULL_CHAR = '\0'; - private Map attributes; - private ArrayList children; - private String name; - private char pushedBackChar; - private Reader reader; - - private static Map entities = new HashMap(); - - static { - entities.put("amp", new char[] { '&' }); - entities.put("quot", new char[] { '"' }); - entities.put("apos", new char[] { '\'' }); - entities.put("lt", new char[] { '<' }); - entities.put("gt", new char[] { '>' }); - } - - public LightXMLParser() { - this.name = null; - this.attributes = new HashMap(); - this.children = new ArrayList(); - } - - public ArrayList getChildrens() { - return this.children; - } - - public String getName() { - return this.name; - } - - public void parseFromReader(Reader reader) throws Exception { - this.pushedBackChar = NULL_CHAR; - this.attributes = new HashMap(); - this.name = null; - this.children = new ArrayList(); - this.reader = reader; - - while (true) { - // Skips whiteSpaces, blanks, \r\n.. - char c = this.skipBlanks(); - - // All xml should start by 'z')) && ((c > 'Z') || (c < 'A')) && ((c > '9') || (c < '0')) && (c != '_') && (c != '-') - && (c != '.') && (c != ':')) { - this.pushBackChar(c); - return; - } - result.append(c); - } - } - - private void getString(StringBuffer string) throws Exception { - char delimiter = this.getNextChar(); - if ((delimiter != '\'') && (delimiter != '"')) { - throw new Exception("Parsing error. Expected ' or \" but got: " + delimiter); - - } - - while (true) { - char c = this.getNextChar(); - if (c == delimiter) { - return; - } else if (c == '&') { - this.mapEntity(string); - } else { - string.append(c); - } - } - } - - private void getPCData(StringBuffer data) throws Exception { - while (true) { - char c = this.getNextChar(); - if (c == '<') { - c = this.getNextChar(); - if (c == '!') { - this.checkCDATA(data); - } else { - this.pushBackChar(c); - return; - } - } else { - data.append(c); - } - } - } - - private boolean checkCDATA(StringBuffer buf) throws Exception { - char c = this.getNextChar(); - if (c != '[') { - this.pushBackChar(c); - this.skipCommentOrXmlTag(0); - return false; - } else if (!this.checkLiteral("CDATA[")) { - this.skipCommentOrXmlTag(1); // one [ has already been read - return false; - } else { - int delimiterCharsSkipped = 0; - while (delimiterCharsSkipped < 3) { - c = this.getNextChar(); - switch (c) { - case ']': - if (delimiterCharsSkipped < 2) { - delimiterCharsSkipped++; - } else { - buf.append(']'); - buf.append(']'); - delimiterCharsSkipped = 0; - } - break; - case '>': - if (delimiterCharsSkipped < 2) { - for (int i = 0; i < delimiterCharsSkipped; i++) { - buf.append(']'); - } - delimiterCharsSkipped = 0; - buf.append('>'); - } else { - delimiterCharsSkipped = 3; - } - break; - default: - for (int i = 0; i < delimiterCharsSkipped; i++) { - buf.append(']'); - } - buf.append(c); - delimiterCharsSkipped = 0; - } - } - return true; - } - } - - private void skipCommentOrXmlTag(int bracketLevel) throws Exception { - char delim = NULL_CHAR; - int level = 1; - char c; - if (bracketLevel == 0) { - c = this.getNextChar(); - if (c == '-') { - c = this.getNextChar(); - if (c == ']') { - bracketLevel--; - } else if (c == '[') { - bracketLevel++; - } else if (c == '-') { - this.skipComment(); - return; - } - } else if (c == '[') { - bracketLevel++; - } - } - while (level > 0) { - c = this.getNextChar(); - if (delim == NULL_CHAR) { - if ((c == '"') || (c == '\'')) { - delim = c; - } else if (bracketLevel <= 0) { - if (c == '<') { - level++; - } else if (c == '>') { - level--; - } - } - if (c == '[') { - bracketLevel++; - } else if (c == ']') { - bracketLevel--; - } - } else { - if (c == delim) { - delim = NULL_CHAR; - } - } - } - } - - private void parseNode(LightXMLParser elt) throws Exception { - // Now we are in a new node element. Get its name - StringBuffer buf = new StringBuffer(); - this.getNodeName(buf); - String name = buf.toString(); - elt.setName(name); - - char c = this.skipBlanks(); - while ((c != '>') && (c != '/')) { - // Get attributes - emptyBuf(buf); - this.pushBackChar(c); - this.getNodeName(buf); - String key = buf.toString(); - c = this.skipBlanks(); - if (c != '=') { - throw new Exception("Parsing error. Expected = but got: " + c); - } - // Go up to " character and push it back - this.pushBackChar(this.skipBlanks()); - - emptyBuf(buf); - this.getString(buf); - - elt.setAttribute(key, buf); - - // Skip blanks - c = this.skipBlanks(); - } - if (c == '/') { - c = this.getNextChar(); - if (c != '>') { - throw new Exception("Parsing error. Expected > but got: " + c); - } - return; - } - - // Now see if we got content, or CDATA, if content get it: it is free... - emptyBuf(buf); - c = this.getWhitespaces(buf); - if (c != '<') { - // It is PCDATA - this.pushBackChar(c); - this.getPCData(buf); - } else { - // It is content: get it, or CDATA. - while (true) { - c = this.getNextChar(); - if (c == '!') { - if (this.checkCDATA(buf)) { - this.getPCData(buf); - break; - } else { - c = this.getWhitespaces(buf); - if (c != '<') { - this.pushBackChar(c); - this.getPCData(buf); - break; - } - } - } else { - if (c != '/') { - emptyBuf(buf); - } - if (c == '/') { - this.pushBackChar(c); - } - break; - } - } - } - if (buf.length() == 0) { - // It is a comment - while (c != '/') { - if (c == '!') { - for (int i = 0; i < 2; i++) { - c = this.getNextChar(); - if (c != '-') { - throw new Exception("Parsing error. Expected element or comment"); - } - } - this.skipComment(); - } else { - // it is a new node - this.pushBackChar(c); - LightXMLParser child = this.createAnotherElement(); - this.parseNode(child); - elt.addChild(child); - } - c = this.skipBlanks(); - if (c != '<') { - throw new Exception("Parsing error. Expected <, but got: " + c); - } - c = this.getNextChar(); - } - this.pushBackChar(c); - } // Here content could be grabbed - - c = this.getNextChar(); - if (c != '/') { - throw new Exception("Parsing error. Expected /, but got: " + c); - } - this.pushBackChar(this.skipBlanks()); - if (!this.checkLiteral(name)) { - throw new Exception("Parsing error. Expected " + name); - } - if (this.skipBlanks() != '>') { - throw new Exception("Parsing error. Expected >, but got: " + c); - } - } - - private void skipComment() throws Exception { - int dashes = 2; - while (dashes > 0) { - char ch = this.getNextChar(); - if (ch == '-') { - dashes -= 1; - } else { - dashes = 2; - } - } - - char nextChar = this.getNextChar(); - if (nextChar != '>') { - throw new Exception("Parsing error. Expected > but got: " + nextChar); - } - } - - private boolean checkLiteral(String literal) throws Exception { - int length = literal.length(); - for (int i = 0; i < length; i++) { - if (this.getNextChar() != literal.charAt(i)) { - return false; - } - } - return true; - } - - private char getNextChar() throws Exception { - if (this.pushedBackChar != NULL_CHAR) { - char c = this.pushedBackChar; - this.pushedBackChar = NULL_CHAR; - return c; - } else { - int i = this.reader.read(); - if (i < 0) { - throw new Exception("Parsing error. Unexpected end of data"); - } else { - return (char) i; - } - } - } - - private void mapEntity(StringBuffer buf) throws Exception { - char c = this.NULL_CHAR; - StringBuffer keyBuf = new StringBuffer(); - while (true) { - c = this.getNextChar(); - if (c == ';') { - break; - } - keyBuf.append(c); - } - String key = keyBuf.toString(); - if (key.charAt(0) == '#') { - try { - if (key.charAt(1) == 'x') { - c = (char) Integer.parseInt(key.substring(2), 16); - } else { - c = (char) Integer.parseInt(key.substring(1), 10); - } - } catch (NumberFormatException e) { - throw new Exception("Unknown entity: " + key); - } - buf.append(c); - } else { - char[] value = (char[]) entities.get(key); - if (value == null) { - throw new Exception("Unknown entity: " + key); - } - buf.append(value); - } - } - - private void pushBackChar(char c) { - this.pushedBackChar = c; - } - - private void addChild(LightXMLParser child) { - this.children.add(child); - } - - private void setAttribute(String name, Object value) { - this.attributes.put(name, value.toString()); - } - - public Map getAttributes() { - return this.attributes; - } - - private LightXMLParser createAnotherElement() { - return new LightXMLParser(); - } - - private void setName(String name) { - this.name = name; - } - - private void emptyBuf(StringBuffer buf) { - buf.setLength(0); - } - -} diff --git a/weaver/src/org/aspectj/weaver/loadtime/definition/SimpleAOPParser.java b/weaver/src/org/aspectj/weaver/loadtime/definition/SimpleAOPParser.java deleted file mode 100644 index bcd6ddcd0..000000000 --- a/weaver/src/org/aspectj/weaver/loadtime/definition/SimpleAOPParser.java +++ /dev/null @@ -1,265 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2011 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * Abraham Nevado - Lucierna initial implementation - * Just a slight variation of current DocumentParser.java from Alexandre Vasseur. - *******************************************************************************/ -package org.aspectj.weaver.loadtime.definition; - -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.URL; -import java.util.ArrayList; -import java.util.Map; -import java.util.Set; - -import org.aspectj.util.LangUtil; -import org.aspectj.weaver.loadtime.definition.Definition.AdviceKind; -import org.aspectj.weaver.loadtime.definition.Definition.DeclareAnnotationKind; -import org.xml.sax.SAXException; - -/** - * This class has been created to avoid deadlocks when instrumenting SAXParser. - * So it is used as a wrapper for the ligthweigh XML parser LightXMLParser. - * - * @author A. Nevado - */ -public class SimpleAOPParser { - - private final static String ASPECTJ_ELEMENT = "aspectj"; - private final static String WEAVER_ELEMENT = "weaver"; - private final static String DUMP_ELEMENT = "dump"; - private final static String DUMP_BEFOREANDAFTER_ATTRIBUTE = "beforeandafter"; - private final static String DUMP_PERCLASSLOADERDIR_ATTRIBUTE = "perclassloaderdumpdir"; - private final static String INCLUDE_ELEMENT = "include"; - private final static String EXCLUDE_ELEMENT = "exclude"; - private final static String OPTIONS_ATTRIBUTE = "options"; - private final static String ASPECTS_ELEMENT = "aspects"; - private final static String ASPECT_ELEMENT = "aspect"; - private final static String CONCRETE_ASPECT_ELEMENT = "concrete-aspect"; - private final static String NAME_ATTRIBUTE = "name"; - private final static String SCOPE_ATTRIBUTE = "scope"; - private final static String REQUIRES_ATTRIBUTE = "requires"; - private final static String EXTEND_ATTRIBUTE = "extends"; - private final static String PRECEDENCE_ATTRIBUTE = "precedence"; - private final static String PERCLAUSE_ATTRIBUTE = "perclause"; - private final static String POINTCUT_ELEMENT = "pointcut"; - private final static String WITHIN_ATTRIBUTE = "within"; - private final static String EXPRESSION_ATTRIBUTE = "expression"; - private static final String DECLARE_ANNOTATION = "declare-annotation"; - private static final String ANNONATION_TAG = "annotation"; - private static final String ANNO_KIND_TYPE = "type"; - private static final String ANNO_KIND_METHOD = "method"; - private static final String ANNO_KIND_FIELD = "field"; - private final static String BEFORE_ELEMENT = "before"; - private final static String AFTER_ELEMENT = "after"; - private final static String AROUND_ELEMENT = "around"; - private final Definition m_definition; - private boolean m_inAspectJ; - private boolean m_inWeaver; - private boolean m_inAspects; - - private Definition.ConcreteAspect m_lastConcreteAspect; - - private SimpleAOPParser() { - m_definition = new Definition(); - } - - public static Definition parse(final URL url) throws Exception { - // FileReader freader = new FileReader("/tmp/aop.xml"); - InputStream in = url.openStream(); - LightXMLParser xml = new LightXMLParser(); - xml.parseFromReader(new InputStreamReader(in)); - SimpleAOPParser sap = new SimpleAOPParser(); - traverse(sap, xml); - return sap.m_definition; - } - - private void startElement(String qName, Map attrMap) throws Exception { - if (ASPECT_ELEMENT.equals(qName)) { - String name = (String) attrMap.get(NAME_ATTRIBUTE); - String scopePattern = replaceXmlAnd((String) attrMap - .get(SCOPE_ATTRIBUTE)); - String requiredType = (String) attrMap.get(REQUIRES_ATTRIBUTE); - if (!isNull(name)) { - m_definition.getAspectClassNames().add(name); - if (scopePattern != null) { - m_definition.addScopedAspect(name, scopePattern); - } - if (requiredType != null) { - m_definition.setAspectRequires(name, requiredType); - } - } - } else if (WEAVER_ELEMENT.equals(qName)) { - String options = (String) attrMap.get(OPTIONS_ATTRIBUTE); - if (!isNull(options)) { - m_definition.appendWeaverOptions(options); - } - m_inWeaver = true; - } else if (CONCRETE_ASPECT_ELEMENT.equals(qName)) { - String name = (String) attrMap.get(NAME_ATTRIBUTE); - String extend = (String) attrMap.get(EXTEND_ATTRIBUTE); - String precedence = (String) attrMap.get(PRECEDENCE_ATTRIBUTE); - String perclause = (String) attrMap.get(PERCLAUSE_ATTRIBUTE); - if (!isNull(name)) { - m_lastConcreteAspect = new Definition.ConcreteAspect(name, - extend, precedence, perclause); - m_definition.getConcreteAspects().add(m_lastConcreteAspect); - } - } else if (POINTCUT_ELEMENT.equals(qName) - && m_lastConcreteAspect != null) { - String name = (String) attrMap.get(NAME_ATTRIBUTE); - String expression = (String) attrMap.get(EXPRESSION_ATTRIBUTE); - if (!isNull(name) && !isNull(expression)) { - m_lastConcreteAspect.pointcuts.add(new Definition.Pointcut( - name, replaceXmlAnd(expression))); - } - } else if (ASPECTJ_ELEMENT.equals(qName)) { - if (m_inAspectJ) { - throw new Exception("Found nested element"); - } - m_inAspectJ = true; - } else if (ASPECTS_ELEMENT.equals(qName)) { - m_inAspects = true; - } else if (INCLUDE_ELEMENT.equals(qName) && m_inWeaver) { - String typePattern = getWithinAttribute(attrMap); - if (!isNull(typePattern)) { - m_definition.getIncludePatterns().add(typePattern); - } - } else if (EXCLUDE_ELEMENT.equals(qName) && m_inWeaver) { - String typePattern = getWithinAttribute(attrMap); - if (!isNull(typePattern)) { - m_definition.getExcludePatterns().add(typePattern); - } - } else if (DUMP_ELEMENT.equals(qName) && m_inWeaver) { - String typePattern = getWithinAttribute(attrMap); - if (!isNull(typePattern)) { - m_definition.getDumpPatterns().add(typePattern); - } - String beforeAndAfter = (String) attrMap - .get(DUMP_BEFOREANDAFTER_ATTRIBUTE); - if (isTrue(beforeAndAfter)) { - m_definition.setDumpBefore(true); - } - String perWeaverDumpDir = (String) attrMap - .get(DUMP_PERCLASSLOADERDIR_ATTRIBUTE); - if (isTrue(perWeaverDumpDir)) { - m_definition.setCreateDumpDirPerClassloader(true); - } - } else if (EXCLUDE_ELEMENT.equals(qName) && m_inAspects) { - String typePattern = getWithinAttribute(attrMap); - if (!isNull(typePattern)) { - m_definition.getAspectExcludePatterns().add(typePattern); - } - } else if (INCLUDE_ELEMENT.equals(qName) && m_inAspects) { - String typePattern = getWithinAttribute(attrMap); - if (!isNull(typePattern)) { - m_definition.getAspectIncludePatterns().add(typePattern); - } - }else if (DECLARE_ANNOTATION.equals(qName) && m_inAspects) { - String anno = (String) attrMap.get(ANNONATION_TAG); - if (!isNull(anno)){ - String pattern = (String) attrMap.get(ANNO_KIND_FIELD); - if (pattern != null){ - m_lastConcreteAspect.declareAnnotations.add(new Definition.DeclareAnnotation( - DeclareAnnotationKind.Field, pattern, anno)); - } - else{ - pattern = (String) attrMap.get(ANNO_KIND_METHOD); - if (pattern != null){ - m_lastConcreteAspect.declareAnnotations.add(new Definition.DeclareAnnotation( - DeclareAnnotationKind.Method, pattern, anno)); - } - else{ - pattern = (String) attrMap.get(ANNO_KIND_TYPE); - if (pattern != null){ - m_lastConcreteAspect.declareAnnotations.add(new Definition.DeclareAnnotation( - DeclareAnnotationKind.Type, pattern, anno)); - } - } - } - - } - } - else if (BEFORE_ELEMENT.equals(qName) && m_inAspects ) { - String pointcut = (String) attrMap.get(POINTCUT_ELEMENT); - String adviceClass = (String) attrMap.get("invokeClass"); - String adviceMethod = (String) attrMap.get("invokeMethod"); - if (!isNull(pointcut) && !isNull(adviceClass) && !isNull(adviceMethod)) { - m_lastConcreteAspect.pointcutsAndAdvice.add(new Definition.PointcutAndAdvice(AdviceKind.Before, - replaceXmlAnd(pointcut), adviceClass, adviceMethod)); - } else { - throw new SAXException("Badly formed element"); - } - } else if (AFTER_ELEMENT.equals(qName) && m_inAspects) { - String pointcut = (String) attrMap.get(POINTCUT_ELEMENT); - String adviceClass = (String) attrMap.get("invokeClass"); - String adviceMethod = (String) attrMap.get("invokeMethod"); - if (!isNull(pointcut) && !isNull(adviceClass) && !isNull(adviceMethod)) { - m_lastConcreteAspect.pointcutsAndAdvice.add(new Definition.PointcutAndAdvice(AdviceKind.After, - replaceXmlAnd(pointcut), adviceClass, adviceMethod)); - } else { - throw new SAXException("Badly formed element"); - } - } else if (AROUND_ELEMENT.equals(qName) && m_inAspects) { - String pointcut = (String) attrMap.get(POINTCUT_ELEMENT); - String adviceClass = (String) attrMap.get("invokeClass"); - String adviceMethod = (String) attrMap.get("invokeMethod"); - if (!isNull(pointcut) && !isNull(adviceClass) && !isNull(adviceMethod)) { - m_lastConcreteAspect.pointcutsAndAdvice.add(new Definition.PointcutAndAdvice(AdviceKind.Around, - replaceXmlAnd(pointcut), adviceClass, adviceMethod)); - } - } - else { - throw new Exception( - "Unknown element while parsing element: " + qName); - } - } - - private void endElement(String qName) throws Exception { - if (CONCRETE_ASPECT_ELEMENT.equals(qName)) { - m_lastConcreteAspect = null; - } else if (ASPECTJ_ELEMENT.equals(qName)) { - m_inAspectJ = false; - } else if (WEAVER_ELEMENT.equals(qName)) { - m_inWeaver = false; - } else if (ASPECTS_ELEMENT.equals(qName)) { - m_inAspects = false; - } - } - - private String getWithinAttribute(Map attributes) { - return replaceXmlAnd((String) attributes.get(WITHIN_ATTRIBUTE)); - } - - private static String replaceXmlAnd(String expression) { - // TODO AV do we need to handle "..)AND" or "AND(.." ? - return LangUtil.replace(expression, " AND ", " && "); - } - - private boolean isNull(String s) { - return (s == null || s.length() <= 0); - } - - private boolean isTrue(String s) { - return (s != null && s.equals("true")); - } - - private static void traverse(SimpleAOPParser sap, LightXMLParser xml) - throws Exception { - sap.startElement(xml.getName(), xml.getAttributes()); - ArrayList childrens = xml.getChildrens(); - for (int i = 0; i < childrens.size(); i++) { - LightXMLParser child = (LightXMLParser) childrens.get(i); - traverse(sap, child); - } - sap.endElement(xml.getName()); - - } -} diff --git a/weaver/src/org/aspectj/weaver/ltw/LTWWorld.java b/weaver/src/org/aspectj/weaver/ltw/LTWWorld.java deleted file mode 100644 index de5a4d854..000000000 --- a/weaver/src/org/aspectj/weaver/ltw/LTWWorld.java +++ /dev/null @@ -1,294 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2005 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * Ron Bodkin Initial implementation - * ******************************************************************/ -package org.aspectj.weaver.ltw; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.aspectj.apache.bcel.classfile.JavaClass; -import org.aspectj.bridge.IMessageHandler; -import org.aspectj.util.LangUtil; -import org.aspectj.weaver.Dump.IVisitor; -import org.aspectj.weaver.ICrossReferenceHandler; -import org.aspectj.weaver.ReferenceType; -import org.aspectj.weaver.ReferenceTypeDelegate; -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.bcel.BcelWorld; -import org.aspectj.weaver.loadtime.IWeavingContext; -import org.aspectj.weaver.reflect.AnnotationFinder; -import org.aspectj.weaver.reflect.IReflectionWorld; -import org.aspectj.weaver.reflect.ReflectionBasedReferenceTypeDelegateFactory; -import org.aspectj.weaver.reflect.ReflectionWorld; - -/** - * @author adrian - * @author Ron Bodkin - * - * For use in LT weaving - * - * Backed by both a BcelWorld and a ReflectionWorld - * - * Needs a callback when a woven class is defined This is the trigger for us to ditch the class from Bcel and cache it in - * the reflective world instead. - * - * Create by passing in a classloader, message handler - */ -public class LTWWorld extends BcelWorld implements IReflectionWorld { - - private AnnotationFinder annotationFinder; - private IWeavingContext weavingContext; - private String classLoaderString; - - private String classLoaderParentString; - - protected final static Class concurrentMapClass; - - private static final boolean ShareBootstrapTypes = false; - protected static Map/* > */bootstrapTypes; - - static { - if (ShareBootstrapTypes) { - concurrentMapClass = makeConcurrentMapClass(); - bootstrapTypes = makeConcurrentMap(); - } else { - concurrentMapClass = null; - } - } - - /** - * Build a World from a ClassLoader, for LTW support - */ - public LTWWorld(ClassLoader loader, IWeavingContext weavingContext, IMessageHandler handler, ICrossReferenceHandler xrefHandler) { - super(loader, handler, xrefHandler); - this.weavingContext = weavingContext; - try { - classLoaderString = loader.toString(); - } catch (Throwable t) { - // Possibly some state in the loader isn't initialized but is used in the toString() - classLoaderString = loader.getClass().getName()+":"+Integer.toString(System.identityHashCode(loader)); - } - classLoaderParentString = (loader.getParent() == null ? "" : loader.getParent().toString()); - setBehaveInJava5Way(LangUtil.is15VMOrGreater()); - annotationFinder = ReflectionWorld.makeAnnotationFinderIfAny(loader, this); - } - - public ClassLoader getClassLoader() { - return weavingContext.getClassLoader(); - } - - // TEST - // this is probably easier: just mark anything loaded while loading aspects as not - // expendible... it also fixes a possible bug whereby non-rewoven aspects are deemed expendible - // - // protected boolean isExpendable(ResolvedType type) { - // return ((type != null) && !loadingAspects && !type.isAspect() && (!type - // .isPrimitiveType())); - // } - - /** - * @Override - */ - @Override - protected ReferenceTypeDelegate resolveDelegate(ReferenceType ty) { - - // use reflection delegates for all bootstrap types - ReferenceTypeDelegate bootstrapLoaderDelegate = resolveIfBootstrapDelegate(ty); - if (bootstrapLoaderDelegate != null) { - return bootstrapLoaderDelegate; - } - - return super.resolveDelegate(ty); - } - - protected ReferenceTypeDelegate resolveIfBootstrapDelegate(ReferenceType ty) { - // first check for anything available in the bootstrap loader: these types are just defined from that without allowing - // nondelegation - // if (!ShareBootstrapTypes) return null; - // String name = ty.getName(); - // Reference bootRef = (Reference) bootstrapTypes.get(name); - // if (bootRef != null) { - // ReferenceTypeDelegate rtd = (ReferenceTypeDelegate) bootRef.get(); - // if (rtd != null) { - // return rtd; - // } - // } - // - // char fc = name.charAt(0); - // if (fc == 'j' || fc == 'c' || fc == 'o' || fc == 's') { // cheaper than imminent string startsWith tests - // if (name.startsWith("java") || name.startsWith("com.sun.") || name.startsWith("org.w3c") || - // name.startsWith("sun.") || name.startsWith("org.omg")) { - // ReferenceTypeDelegate bootstrapLoaderDelegate = resolveReflectionTypeDelegate(ty, null); - // if (bootstrapLoaderDelegate != null) { - // // it's always fine to load these bytes: there's no weaving into them - // // and since the class isn't initialized, all we are doing at this point is loading the bytes - // // processedRefTypes.put(ty, this); // has no effect - and probably too aggressive if we did store - // // these in the type map - // - // // should we share these, like we do the BCEL delegates? - // bootstrapTypes.put(ty.getName(), new WeakReference(bootstrapLoaderDelegate)); - // } - // return bootstrapLoaderDelegate; - // } - // } - return null; - } - - /** - * Helper method to resolve the delegate from the reflection delegate factory. - */ - private ReferenceTypeDelegate resolveReflectionTypeDelegate(ReferenceType ty, ClassLoader resolutionLoader) { - ReferenceTypeDelegate res = ReflectionBasedReferenceTypeDelegateFactory.createDelegate(ty, this, resolutionLoader); - return res; - } - - /** - * Remove this class from the typeMap. Call back to be made from a publishing class loader The class loader should, ideally, - * make this call on each not yet working - * - * @param clazz - */ - public void loadedClass(Class clazz) { - } - - private static final long serialVersionUID = 1; - - public AnnotationFinder getAnnotationFinder() { - return this.annotationFinder; - } - - /* - * (non-Javadoc) - * - * @see org.aspectj.weaver.reflect.IReflectionWorld#resolve(java.lang.Class) - */ - public ResolvedType resolve(Class aClass) { - return ReflectionWorld.resolve(this, aClass); - } - - private static Map makeConcurrentMap() { - if (concurrentMapClass != null) { - try { - return (Map) concurrentMapClass.newInstance(); - } catch (InstantiationException ie) { - } catch (IllegalAccessException iae) { - } - // fall through if exceptions - } - return Collections.synchronizedMap(new HashMap()); - } - - private static Class makeConcurrentMapClass() { - String betterChoices[] = { "java.util.concurrent.ConcurrentHashMap", - "edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap", - "EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap" }; - for (int i = 0; i < betterChoices.length; i++) { - try { - return Class.forName(betterChoices[i]); - } catch (ClassNotFoundException cnfe) { - // try the next one - } catch (SecurityException se) { - // you get one of these if you dare to try to load an undefined class in a - // package starting with java like java.util.concurrent - } - } - return null; - } - - @Override - public boolean isRunMinimalMemory() { - if (isRunMinimalMemorySet()) { - return super.isRunMinimalMemory(); - } - return false; - } - - // One type is completed at a time, if multiple need doing then they - // are queued up - private boolean typeCompletionInProgress = false; - private List/* ResolvedType */typesForCompletion = new ArrayList(); - - @Override - protected void completeBinaryType(ResolvedType ret) { - if (isLocallyDefined(ret.getName())) { - if (typeCompletionInProgress) { - typesForCompletion.add(ret); - } else { - try { - typeCompletionInProgress = true; - completeHierarchyForType(ret); - } finally { - typeCompletionInProgress = false; - } - while (typesForCompletion.size() != 0) { - ResolvedType rt = (ResolvedType) typesForCompletion.get(0); - completeHierarchyForType(rt); - typesForCompletion.remove(0); - } - } - } else { - if (!ret.needsModifiableDelegate()) { - ret = completeNonLocalType(ret); - } - } - } - - private void completeHierarchyForType(ResolvedType ret) { - getLint().typeNotExposedToWeaver.setSuppressed(true); - weaveInterTypeDeclarations(ret); - getLint().typeNotExposedToWeaver.setSuppressed(false); - } - - protected boolean needsCompletion() { - return true; - } - - @Override - public boolean isLocallyDefined(String classname) { - return weavingContext.isLocallyDefined(classname); - } - - protected ResolvedType completeNonLocalType(ResolvedType ret) { - if (ret.isMissing()) { - return ret; // who knows ?!? - } - ResolvedType toResolve = ret; - if (ret.isParameterizedType() || ret.isGenericType()) { - toResolve = toResolve.getGenericType(); - } - ReferenceTypeDelegate rtd = resolveReflectionTypeDelegate((ReferenceType) toResolve, getClassLoader()); - ((ReferenceType) ret).setDelegate(rtd); - return ret; - } - - @Override - public void storeClass(JavaClass clazz) { - ensureRepositorySetup(); - delegate.storeClass(clazz); - } - - @Override - public void accept(IVisitor visitor) { - visitor.visitObject("Class loader:"); - visitor.visitObject(classLoaderString); - visitor.visitObject("Class loader parent:"); - visitor.visitObject(classLoaderParentString); - super.accept(visitor); - } - - public boolean isLoadtimeWeaving() { - return true; - } - -} diff --git a/weaver/src/org/aspectj/weaver/model/AsmRelationshipProvider.java b/weaver/src/org/aspectj/weaver/model/AsmRelationshipProvider.java deleted file mode 100644 index 5801397a8..000000000 --- a/weaver/src/org/aspectj/weaver/model/AsmRelationshipProvider.java +++ /dev/null @@ -1,1133 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - -package org.aspectj.weaver.model; - -import java.io.File; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - -import org.aspectj.asm.AsmManager; -import org.aspectj.asm.IHierarchy; -import org.aspectj.asm.IProgramElement; -import org.aspectj.asm.IRelationship; -import org.aspectj.asm.IRelationshipMap; -import org.aspectj.asm.internal.HandleProviderDelimiter; -import org.aspectj.asm.internal.ProgramElement; -import org.aspectj.bridge.ISourceLocation; -import org.aspectj.bridge.SourceLocation; -import org.aspectj.weaver.Advice; -import org.aspectj.weaver.AdviceKind; -import org.aspectj.weaver.Checker; -import org.aspectj.weaver.Lint; -import org.aspectj.weaver.Member; -import org.aspectj.weaver.NewParentTypeMunger; -import org.aspectj.weaver.ReferenceType; -import org.aspectj.weaver.ResolvedMember; -import org.aspectj.weaver.ResolvedPointcutDefinition; -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.ResolvedTypeMunger; -import org.aspectj.weaver.ResolvedTypeMunger.Kind; -import org.aspectj.weaver.Shadow; -import org.aspectj.weaver.ShadowMunger; -import org.aspectj.weaver.UnresolvedType; -import org.aspectj.weaver.World; -import org.aspectj.weaver.bcel.BcelShadow; -import org.aspectj.weaver.bcel.BcelTypeMunger; -import org.aspectj.weaver.patterns.DeclareErrorOrWarning; -import org.aspectj.weaver.patterns.DeclareParents; -import org.aspectj.weaver.patterns.Pointcut; -import org.aspectj.weaver.patterns.TypePatternList; - -public class AsmRelationshipProvider { - - public static final String ADVISES = "advises"; - public static final String ADVISED_BY = "advised by"; - public static final String DECLARES_ON = "declares on"; - public static final String DECLAREDY_BY = "declared by"; - public static final String SOFTENS = "softens"; - public static final String SOFTENED_BY = "softened by"; - public static final String MATCHED_BY = "matched by"; - public static final String MATCHES_DECLARE = "matches declare"; - public static final String INTER_TYPE_DECLARES = "declared on"; - public static final String INTER_TYPE_DECLARED_BY = "aspect declarations"; - - public static final String ANNOTATES = "annotates"; - public static final String ANNOTATED_BY = "annotated by"; - - // public static final String REMOVES_ANNOTATION = "removes annotation"; - // public static final String ANNOTATION_REMOVED_BY = "annotated removed by"; - - /** - * Add a relationship for a declare error or declare warning - */ - public static void addDeclareErrorOrWarningRelationship(AsmManager model, Shadow affectedShadow, Checker deow) { - if (model == null) { - return; - } - if (affectedShadow.getSourceLocation() == null || deow.getSourceLocation() == null) { - return; - } - - if (World.createInjarHierarchy) { - createHierarchyForBinaryAspect(model, deow); - } - - IProgramElement targetNode = getNode(model, affectedShadow); - if (targetNode == null) { - return; - } - String targetHandle = targetNode.getHandleIdentifier(); - if (targetHandle == null) { - return; - } - - IProgramElement sourceNode = model.getHierarchy().findElementForSourceLine(deow.getSourceLocation()); - String sourceHandle = sourceNode.getHandleIdentifier(); - if (sourceHandle == null) { - return; - } - - IRelationshipMap relmap = model.getRelationshipMap(); - IRelationship foreward = relmap.get(sourceHandle, IRelationship.Kind.DECLARE, MATCHED_BY, false, true); - foreward.addTarget(targetHandle); - - IRelationship back = relmap.get(targetHandle, IRelationship.Kind.DECLARE, MATCHES_DECLARE, false, true); - if (back != null && back.getTargets() != null) { - back.addTarget(sourceHandle); - } - if (sourceNode.getSourceLocation() != null) { - model.addAspectInEffectThisBuild(sourceNode.getSourceLocation().getSourceFile()); - } - } - - private static boolean isMixinRelated(ResolvedTypeMunger typeTransformer) { - Kind kind = typeTransformer.getKind(); - return kind == ResolvedTypeMunger.MethodDelegate2 || kind == ResolvedTypeMunger.FieldHost - || (kind == ResolvedTypeMunger.Parent && ((NewParentTypeMunger) typeTransformer).isMixin()); - } - - /** - * Add a relationship for a type transformation (declare parents, intertype method declaration, declare annotation on type). - */ - public static void addRelationship(AsmManager model, ResolvedType onType, ResolvedTypeMunger typeTransformer, - ResolvedType originatingAspect) { - if (model == null) { - return; - } - - if (World.createInjarHierarchy && isBinaryAspect(originatingAspect)) { - createHierarchy(model, typeTransformer, originatingAspect); - } - - if (originatingAspect.getSourceLocation() != null) { - String sourceHandle = ""; - IProgramElement sourceNode = null; - if (typeTransformer.getSourceLocation() != null && typeTransformer.getSourceLocation().getOffset() != -1 - && !isMixinRelated(typeTransformer)) { - sourceNode = model.getHierarchy().findElementForType(originatingAspect.getPackageName(), - originatingAspect.getClassName()); - IProgramElement closer = model.getHierarchy().findCloserMatchForLineNumber(sourceNode, - typeTransformer.getSourceLocation().getLine()); - if (closer != null) { - sourceNode = closer; - } - if (sourceNode == null) { - // This can be caused by the aspect defining the type munger actually being on the classpath and not the - // inpath or aspectpath. Rather than NPE at the next line, let's have another go at faulting it in. - // This inner loop is a small duplicate of the outer loop that attempts to find something closer than - // the type declaration - if (World.createInjarHierarchy) { - createHierarchy(model, typeTransformer, originatingAspect); - if (typeTransformer.getSourceLocation() != null && typeTransformer.getSourceLocation().getOffset() != -1 - && !isMixinRelated(typeTransformer)) { - sourceNode = model.getHierarchy().findElementForType(originatingAspect.getPackageName(), - originatingAspect.getClassName()); - IProgramElement closer2 = model.getHierarchy().findCloserMatchForLineNumber(sourceNode, - typeTransformer.getSourceLocation().getLine()); - if (closer2 != null) { - sourceNode = closer2; - } - } else { - sourceNode = model.getHierarchy().findElementForType(originatingAspect.getPackageName(), - originatingAspect.getClassName()); - } - } - } - sourceHandle = sourceNode.getHandleIdentifier(); - } else { - sourceNode = model.getHierarchy().findElementForType(originatingAspect.getPackageName(), - originatingAspect.getClassName()); - // sourceNode = - // asm.getHierarchy().findElementForSourceLine(originatingAspect - // .getSourceLocation()); - sourceHandle = sourceNode.getHandleIdentifier(); - } - // sourceNode = - // asm.getHierarchy().findElementForType(originatingAspect - // .getPackageName(), - // originatingAspect.getClassName()); - // // sourceNode = - // asm.getHierarchy().findElementForSourceLine(munger - // .getSourceLocation()); - // sourceHandle = - // asm.getHandleProvider().createHandleIdentifier(sourceNode); - if (sourceHandle == null) { - return; - } - String targetHandle = findOrFakeUpNode(model, onType); - if (targetHandle == null) { - return; - } - IRelationshipMap mapper = model.getRelationshipMap(); - IRelationship foreward = mapper.get(sourceHandle, IRelationship.Kind.DECLARE_INTER_TYPE, INTER_TYPE_DECLARES, false, - true); - foreward.addTarget(targetHandle); - - IRelationship back = mapper.get(targetHandle, IRelationship.Kind.DECLARE_INTER_TYPE, INTER_TYPE_DECLARED_BY, false, - true); - back.addTarget(sourceHandle); - if (sourceNode != null && sourceNode.getSourceLocation() != null) { - // May have been a bug in the compiled aspect - so it didn't get put in the model - model.addAspectInEffectThisBuild(sourceNode.getSourceLocation().getSourceFile()); - } - } - } - - private static String findOrFakeUpNode(AsmManager model, ResolvedType onType) { - IHierarchy hierarchy = model.getHierarchy(); - ISourceLocation sourceLocation = onType.getSourceLocation(); - String canonicalFilePath = model.getCanonicalFilePath(sourceLocation.getSourceFile()); - int lineNumber = sourceLocation.getLine(); - // Find the relevant source file node first - IProgramElement node = hierarchy.findNodeForSourceFile(hierarchy.getRoot(), canonicalFilePath); - if (node == null) { - // Does not exist in the model - probably an inpath - String bpath = onType.getBinaryPath(); - if (bpath == null) { - return model.getHandleProvider().createHandleIdentifier(createFileStructureNode(model, canonicalFilePath)); - } else { - IProgramElement programElement = model.getHierarchy().getRoot(); - // =Foo/, 0 && ((ch = bpath.charAt(startPosition)) != '/' && ch != '\\' && ch != '!')) { - startPosition--; - } - String classFile = bpath.substring(startPosition + 1, dotClassPosition + 6); - phantomHandle.append(HandleProviderDelimiter.CLASSFILE.getDelimiter()).append(classFile); - } - - // [G - phantomHandle.append(HandleProviderDelimiter.TYPE.getDelimiter()).append(onType.getClassName()); - - return phantomHandle.toString(); - } - } else { - // Check if there is a more accurate child node of that source file node: - IProgramElement closernode = hierarchy.findCloserMatchForLineNumber(node, lineNumber); - if (closernode == null) { - return node.getHandleIdentifier(); - } else { - return closernode.getHandleIdentifier(); - } - } - - } - - public static IProgramElement createFileStructureNode(AsmManager asm, String sourceFilePath) { - // SourceFilePath might have originated on windows on linux... - int lastSlash = sourceFilePath.lastIndexOf('\\'); - if (lastSlash == -1) { - lastSlash = sourceFilePath.lastIndexOf('/'); - } - // '!' is used like in URLs "c:/blahblah/X.jar!a/b.class" - int i = sourceFilePath.lastIndexOf('!'); - int j = sourceFilePath.indexOf(".class"); - if (i > lastSlash && i != -1 && j != -1) { - // we are a binary aspect in the default package - lastSlash = i; - } - String fileName = sourceFilePath.substring(lastSlash + 1); - IProgramElement fileNode = new ProgramElement(asm, fileName, IProgramElement.Kind.FILE_JAVA, new SourceLocation(new File( - sourceFilePath), 1, 1), 0, null, null); - // fileNode.setSourceLocation(); - fileNode.addChild(IHierarchy.NO_STRUCTURE); - return fileNode; - } - - private static boolean isBinaryAspect(ResolvedType aspect) { - return aspect.getBinaryPath() != null; - } - - /** - * Returns the binarySourceLocation for the given sourcelocation. This isn't cached because it's used when faulting in the - * binary nodes and is called with ISourceLocations for all advice, pointcuts and deows contained within the - * resolvedDeclaringAspect. - */ - private static ISourceLocation getBinarySourceLocation(ResolvedType aspect, ISourceLocation sl) { - if (sl == null) { - return null; - } - String sourceFileName = null; - if (aspect instanceof ReferenceType) { - String s = ((ReferenceType) aspect).getDelegate().getSourcefilename(); - int i = s.lastIndexOf('/'); - if (i != -1) { - sourceFileName = s.substring(i + 1); - } else { - sourceFileName = s; - } - } - ISourceLocation sLoc = new SourceLocation(getBinaryFile(aspect), sl.getLine(), sl.getEndLine(), - ((sl.getColumn() == 0) ? ISourceLocation.NO_COLUMN : sl.getColumn()), sl.getContext(), sourceFileName); - return sLoc; - } - - private static ISourceLocation createSourceLocation(String sourcefilename, ResolvedType aspect, ISourceLocation sl) { - ISourceLocation sLoc = new SourceLocation(getBinaryFile(aspect), sl.getLine(), sl.getEndLine(), - ((sl.getColumn() == 0) ? ISourceLocation.NO_COLUMN : sl.getColumn()), sl.getContext(), sourcefilename); - return sLoc; - } - - private static String getSourceFileName(ResolvedType aspect) { - String sourceFileName = null; - if (aspect instanceof ReferenceType) { - String s = ((ReferenceType) aspect).getDelegate().getSourcefilename(); - int i = s.lastIndexOf('/'); - if (i != -1) { - sourceFileName = s.substring(i + 1); - } else { - sourceFileName = s; - } - } - return sourceFileName; - } - - /** - * Returns the File with pathname to the class file, for example either C:\temp - * \ajcSandbox\workspace\ajcTest16957.tmp\simple.jar!pkg\BinaryAspect.class if the class file is in a jar file, or - * C:\temp\ajcSandbox\workspace\ajcTest16957.tmp!pkg\BinaryAspect.class if the class file is in a directory - */ - private static File getBinaryFile(ResolvedType aspect) { - String s = aspect.getBinaryPath(); - File f = aspect.getSourceLocation().getSourceFile(); - // Replace the source file suffix with .class - int i = f.getPath().lastIndexOf('.'); - String path = null; - if (i != -1) { - path = f.getPath().substring(0, i) + ".class"; - } else { - path = f.getPath() + ".class"; - } - return new File(s + "!" + path); - } - - /** - * Create a basic hierarchy to represent an aspect only available in binary (from the aspectpath). - */ - private static void createHierarchy(AsmManager model, ResolvedTypeMunger typeTransformer, ResolvedType aspect) { - // assert aspect != null; - - // Check if already defined in the model - // IProgramElement filenode = - // model.getHierarchy().findElementForType(aspect.getPackageName(), - // aspect.getClassName()); - // SourceLine(typeTransformer.getSourceLocation()); - IProgramElement filenode = model.getHierarchy().findElementForSourceLine(typeTransformer.getSourceLocation()); - if (filenode == null) { - if (typeTransformer.getKind() == ResolvedTypeMunger.MethodDelegate2 - || typeTransformer.getKind() == ResolvedTypeMunger.FieldHost) { - // not yet faulting these in - return; - } - } - // the call to findElementForSourceLine(ISourceLocation) returns a file - // node - // if it can't find a node in the hierarchy for the given - // sourcelocation. - // Therefore, if this is returned, we know we can't find one and have to - // // continue to fault in the model. - // if (filenode != null) { // - if (!filenode.getKind().equals(IProgramElement.Kind.FILE_JAVA)) { - return; - } - - // create the class file node - ISourceLocation binLocation = getBinarySourceLocation(aspect, aspect.getSourceLocation()); - String f = getBinaryFile(aspect).getName(); - IProgramElement classFileNode = new ProgramElement(model, f, IProgramElement.Kind.FILE, binLocation, 0, null, null); - - // create package ipe if one exists.... - IProgramElement root = model.getHierarchy().getRoot(); - IProgramElement binaries = model.getHierarchy().findElementForLabel(root, IProgramElement.Kind.SOURCE_FOLDER, "binaries"); - if (binaries == null) { - binaries = new ProgramElement(model, "binaries", IProgramElement.Kind.SOURCE_FOLDER, new ArrayList()); - root.addChild(binaries); - } - // if (aspect.getPackageName() != null) { - String packagename = aspect.getPackageName() == null ? "" : aspect.getPackageName(); - // check that there doesn't already exist a node with this name - IProgramElement pkgNode = model.getHierarchy().findElementForLabel(binaries, IProgramElement.Kind.PACKAGE, packagename); - // note packages themselves have no source location - if (pkgNode == null) { - pkgNode = new ProgramElement(model, packagename, IProgramElement.Kind.PACKAGE, new ArrayList()); - binaries.addChild(pkgNode); - pkgNode.addChild(classFileNode); - } else { - // need to add it first otherwise the handle for classFileNode - // may not be generated correctly if it uses information from - // it's parent node - pkgNode.addChild(classFileNode); - for (IProgramElement element: pkgNode.getChildren()) { - if (!element.equals(classFileNode) && element.getHandleIdentifier().equals(classFileNode.getHandleIdentifier())) { - // already added the classfile so have already - // added the structure for this aspect - pkgNode.removeChild(classFileNode); - return; - } - } - } - // } else { - // // need to add it first otherwise the handle for classFileNode - // // may not be generated correctly if it uses information from - // // it's parent node - // root.addChild(classFileNode); - // for (Iterator iter = root.getChildren().iterator(); iter.hasNext();) - // { - // IProgramElement element = (IProgramElement) iter.next(); - // if (!element.equals(classFileNode) && - // element.getHandleIdentifier().equals - // (classFileNode.getHandleIdentifier())) { - // // already added the sourcefile so have already - // // added the structure for this aspect - // root.removeChild(classFileNode); - // return; - // } - // } - // } - - // add and create empty import declaration ipe - // no import container for binary type - 265693 - // classFileNode.addChild(new ProgramElement(model, "import declarations", IProgramElement.Kind.IMPORT_REFERENCE, null, 0, - // null, null)); - - // add and create aspect ipe - IProgramElement aspectNode = new ProgramElement(model, aspect.getSimpleName(), IProgramElement.Kind.ASPECT, - getBinarySourceLocation(aspect, aspect.getSourceLocation()), aspect.getModifiers(), null, null); - classFileNode.addChild(aspectNode); - - addChildNodes(model, aspect, aspectNode, aspect.getDeclaredPointcuts()); - - addChildNodes(model, aspect, aspectNode, aspect.getDeclaredAdvice()); - addChildNodes(model, aspect, aspectNode, aspect.getDeclares()); - addChildNodes(model, aspect, aspectNode, aspect.getTypeMungers()); - } - - /** - * Adds a declare annotation relationship, sometimes entities don't have source locs (methods/fields) so use other variants of - * this method if that is the case as they will look the entities up in the structure model. - */ - public static void addDeclareAnnotationRelationship(AsmManager model, ISourceLocation declareAnnotationLocation, - ISourceLocation annotatedLocation, boolean isRemove) { - if (model == null) { - return; - } - - IProgramElement sourceNode = model.getHierarchy().findElementForSourceLine(declareAnnotationLocation); - String sourceHandle = sourceNode.getHandleIdentifier(); - if (sourceHandle == null) { - return; - } - - IProgramElement targetNode = model.getHierarchy().findElementForSourceLine(annotatedLocation); - String targetHandle = targetNode.getHandleIdentifier(); - if (targetHandle == null) { - return; - } - - IRelationshipMap mapper = model.getRelationshipMap(); - // if (isRemove) { - // IRelationship foreward = mapper.get(sourceHandle, IRelationship.Kind.DECLARE_INTER_TYPE, REMOVES_ANNOTATION, false, - // true); - // foreward.addTarget(targetHandle); - // - // IRelationship back = mapper - // .get(targetHandle, IRelationship.Kind.DECLARE_INTER_TYPE, ANNOTATION_REMOVED_BY, false, true); - // back.addTarget(sourceHandle); - // if (sourceNode.getSourceLocation() != null) { - // model.addAspectInEffectThisBuild(sourceNode.getSourceLocation().getSourceFile()); - // } - // } else { - IRelationship foreward = mapper.get(sourceHandle, IRelationship.Kind.DECLARE_INTER_TYPE, ANNOTATES, false, true); - foreward.addTarget(targetHandle); - - IRelationship back = mapper.get(targetHandle, IRelationship.Kind.DECLARE_INTER_TYPE, ANNOTATED_BY, false, true); - back.addTarget(sourceHandle); - if (sourceNode.getSourceLocation() != null) { - model.addAspectInEffectThisBuild(sourceNode.getSourceLocation().getSourceFile()); - } - // } - } - - /** - * Creates the hierarchy for binary aspects - */ - public static void createHierarchyForBinaryAspect(AsmManager asm, ShadowMunger munger) { - if (!munger.isBinary()) { - return; - } - - IProgramElement sourceFileNode = asm.getHierarchy().findElementForSourceLine(munger.getSourceLocation()); - // the call to findElementForSourceLine(ISourceLocation) returns a file - // node if it can't find a node in the hierarchy for the given sourcelocation. - // Therefore, if this is returned, we know we can't find one and have to - // continue to fault in the model. - if (!sourceFileNode.getKind().equals(IProgramElement.Kind.FILE_JAVA)) { - return; - } - - ResolvedType aspect = munger.getDeclaringType(); - - // create the class file node - IProgramElement classFileNode = new ProgramElement(asm, sourceFileNode.getName(), IProgramElement.Kind.FILE, - munger.getBinarySourceLocation(aspect.getSourceLocation()), 0, null, null); - - // create package ipe if one exists.... - IProgramElement root = asm.getHierarchy().getRoot(); - IProgramElement binaries = asm.getHierarchy().findElementForLabel(root, IProgramElement.Kind.SOURCE_FOLDER, "binaries"); - if (binaries == null) { - binaries = new ProgramElement(asm, "binaries", IProgramElement.Kind.SOURCE_FOLDER, new ArrayList()); - root.addChild(binaries); - } - // if (aspect.getPackageName() != null) { - String packagename = aspect.getPackageName() == null ? "" : aspect.getPackageName(); - // check that there doesn't already exist a node with this name - IProgramElement pkgNode = asm.getHierarchy().findElementForLabel(binaries, IProgramElement.Kind.PACKAGE, packagename); - // note packages themselves have no source location - if (pkgNode == null) { - pkgNode = new ProgramElement(asm, packagename, IProgramElement.Kind.PACKAGE, new ArrayList()); - binaries.addChild(pkgNode); - pkgNode.addChild(classFileNode); - } else { - // need to add it first otherwise the handle for classFileNode - // may not be generated correctly if it uses information from - // it's parent node - pkgNode.addChild(classFileNode); - for (IProgramElement element: pkgNode.getChildren()) { - if (!element.equals(classFileNode) && element.getHandleIdentifier().equals(classFileNode.getHandleIdentifier())) { - // already added the classfile so have already - // added the structure for this aspect - pkgNode.removeChild(classFileNode); - return; - } - } - } - // } else { - // // need to add it first otherwise the handle for classFileNode - // // may not be generated correctly if it uses information from - // // it's parent node - // root.addChild(classFileNode); - // for (Iterator iter = root.getChildren().iterator(); iter.hasNext();) - // { - // IProgramElement element = (IProgramElement) iter.next(); - // if (!element.equals(classFileNode) && - // element.getHandleIdentifier().equals - // (classFileNode.getHandleIdentifier())) { - // // already added the sourcefile so have already - // // added the structure for this aspect - // root.removeChild(classFileNode); - // return; - // } - // } - // } - - // add and create empty import declaration ipe - // classFileNode.addChild(new ProgramElement(asm, "import declarations", IProgramElement.Kind.IMPORT_REFERENCE, null, 0, - // null, - // null)); - - // add and create aspect ipe - IProgramElement aspectNode = new ProgramElement(asm, aspect.getSimpleName(), IProgramElement.Kind.ASPECT, - munger.getBinarySourceLocation(aspect.getSourceLocation()), aspect.getModifiers(), null, null); - classFileNode.addChild(aspectNode); - - String sourcefilename = getSourceFileName(aspect); - addPointcuts(asm, sourcefilename, aspect, aspectNode, aspect.getDeclaredPointcuts()); - addChildNodes(asm, aspect, aspectNode, aspect.getDeclaredAdvice()); - addChildNodes(asm, aspect, aspectNode, aspect.getDeclares()); - addChildNodes(asm, aspect, aspectNode, aspect.getTypeMungers()); - - } - - private static void addPointcuts(AsmManager model, String sourcefilename, ResolvedType aspect, - IProgramElement containingAspect, ResolvedMember[] pointcuts) { - for (int i = 0; i < pointcuts.length; i++) { - ResolvedMember pointcut = pointcuts[i]; - if (pointcut instanceof ResolvedPointcutDefinition) { - ResolvedPointcutDefinition rpcd = (ResolvedPointcutDefinition) pointcut; - Pointcut p = rpcd.getPointcut(); - ISourceLocation sLoc = (p == null ? null : p.getSourceLocation()); - if (sLoc == null) { - sLoc = rpcd.getSourceLocation(); - } - ISourceLocation pointcutLocation = (sLoc == null ? null : createSourceLocation(sourcefilename, aspect, sLoc)); - ProgramElement pointcutElement = new ProgramElement(model, pointcut.getName(), IProgramElement.Kind.POINTCUT, - pointcutLocation, pointcut.getModifiers(), NO_COMMENT, Collections.emptyList()); - containingAspect.addChild(pointcutElement); - } - } - } - - private static final String NO_COMMENT = null; - - private static void addChildNodes(AsmManager asm, ResolvedType aspect, IProgramElement parent, ResolvedMember[] children) { - for (int i = 0; i < children.length; i++) { - ResolvedMember pcd = children[i]; - if (pcd instanceof ResolvedPointcutDefinition) { - ResolvedPointcutDefinition rpcd = (ResolvedPointcutDefinition) pcd; - Pointcut p = rpcd.getPointcut(); - ISourceLocation sLoc = (p == null ? null : p.getSourceLocation()); - if (sLoc == null) { - sLoc = rpcd.getSourceLocation(); - } - parent.addChild(new ProgramElement(asm, pcd.getName(), IProgramElement.Kind.POINTCUT, getBinarySourceLocation( - aspect, sLoc), pcd.getModifiers(), null, Collections.emptyList())); - } - } - } - - private static void addChildNodes(AsmManager asm, ResolvedType aspect, IProgramElement parent, Collection children) { - int deCtr = 1; - int dwCtr = 1; - for (Object element: children) { - if (element instanceof DeclareErrorOrWarning) { - DeclareErrorOrWarning decl = (DeclareErrorOrWarning) element; - int counter = 0; - if (decl.isError()) { - counter = deCtr++; - } else { - counter = dwCtr++; - } - parent.addChild(createDeclareErrorOrWarningChild(asm, aspect, decl, counter)); - } else if (element instanceof Advice) { - Advice advice = (Advice) element; - parent.addChild(createAdviceChild(asm, advice)); - } else if (element instanceof DeclareParents) { - parent.addChild(createDeclareParentsChild(asm, (DeclareParents) element)); - } else if (element instanceof BcelTypeMunger) { - IProgramElement newChild = createIntertypeDeclaredChild(asm, aspect, (BcelTypeMunger) element); - // newChild==null means it is something that could not be handled by createIntertypeDeclaredChild() - if (newChild != null) { - parent.addChild(newChild); - } - } - } - } - - // private static IProgramElement - // createDeclareErrorOrWarningChild(AsmManager asm, ShadowMunger munger, - // DeclareErrorOrWarning decl, int count) { - // IProgramElement deowNode = new ProgramElement(asm, decl.getName(), - // decl.isError() ? IProgramElement.Kind.DECLARE_ERROR - // : IProgramElement.Kind.DECLARE_WARNING, - // munger.getBinarySourceLocation(decl.getSourceLocation()), decl - // .getDeclaringType().getModifiers(), null, null); - // deowNode.setDetails("\"" + - // AsmRelationshipUtils.genDeclareMessage(decl.getMessage()) + "\""); - // if (count != -1) { - // deowNode.setBytecodeName(decl.getName() + "_" + count); - // } - // return deowNode; - // } - - private static IProgramElement createDeclareErrorOrWarningChild(AsmManager model, ResolvedType aspect, - DeclareErrorOrWarning decl, int count) { - IProgramElement deowNode = new ProgramElement(model, decl.getName(), decl.isError() ? IProgramElement.Kind.DECLARE_ERROR - : IProgramElement.Kind.DECLARE_WARNING, getBinarySourceLocation(aspect, decl.getSourceLocation()), decl - .getDeclaringType().getModifiers(), null, null); - deowNode.setDetails("\"" + AsmRelationshipUtils.genDeclareMessage(decl.getMessage()) + "\""); - if (count != -1) { - deowNode.setBytecodeName(decl.getName() + "_" + count); - } - return deowNode; - } - - private static IProgramElement createAdviceChild(AsmManager model, Advice advice) { - IProgramElement adviceNode = new ProgramElement(model, advice.getKind().getName(), IProgramElement.Kind.ADVICE, - advice.getBinarySourceLocation(advice.getSourceLocation()), advice.getSignature().getModifiers(), null, - Collections.emptyList()); - adviceNode.setDetails(AsmRelationshipUtils.genPointcutDetails(advice.getPointcut())); - adviceNode.setBytecodeName(advice.getSignature().getName()); - return adviceNode; - } - - /** - * Half baked implementation - will need completing if we go down this route rather than replacing it all for binary aspects. - * Doesn't attempt to get parameter names correct - they may have been lost during (de)serialization of the munger, but the - * member could still be located so they might be retrievable. - */ - private static IProgramElement createIntertypeDeclaredChild(AsmManager model, ResolvedType aspect, BcelTypeMunger itd) { - ResolvedTypeMunger rtMunger = itd.getMunger(); - - ResolvedMember sig = rtMunger.getSignature(); - Kind kind = rtMunger.getKind(); - if (kind == ResolvedTypeMunger.Field) { // ITD FIELD - // String name = rtMunger.getSignature().toString(); - String name = sig.getDeclaringType().getClassName() + "." + sig.getName(); - if (name.indexOf("$") != -1) { - name = name.substring(name.indexOf("$") + 1); - } - IProgramElement pe = new ProgramElement(model, name, IProgramElement.Kind.INTER_TYPE_FIELD, getBinarySourceLocation( - aspect, itd.getSourceLocation()), rtMunger.getSignature().getModifiers(), null, Collections.emptyList()); - pe.setCorrespondingType(sig.getReturnType().getName()); - return pe; - } else if (kind == ResolvedTypeMunger.Method) { // ITD - // METHOD - String name = sig.getDeclaringType().getClassName() + "." + sig.getName(); - if (name.indexOf("$") != -1) { - name = name.substring(name.indexOf("$") + 1); - } - IProgramElement pe = new ProgramElement(model, name, IProgramElement.Kind.INTER_TYPE_METHOD, getBinarySourceLocation( - aspect, itd.getSourceLocation()), rtMunger.getSignature().getModifiers(), null, Collections.emptyList()); - setParams(pe, sig); - return pe; - } else if (kind == ResolvedTypeMunger.Constructor) { - String name = sig.getDeclaringType().getClassName() + "." + sig.getDeclaringType().getClassName(); - if (name.indexOf("$") != -1) { - name = name.substring(name.indexOf("$") + 1); - } - IProgramElement pe = new ProgramElement(model, name, IProgramElement.Kind.INTER_TYPE_CONSTRUCTOR, - getBinarySourceLocation(aspect, itd.getSourceLocation()), rtMunger.getSignature().getModifiers(), null, - Collections.emptyList()); - setParams(pe, sig); - return pe; - // } else if (kind == ResolvedTypeMunger.MethodDelegate2) { - // String name = sig.getDeclaringType().getClassName() + "." + sig.getName(); - // if (name.indexOf("$") != -1) { - // name = name.substring(name.indexOf("$") + 1); - // } - // IProgramElement pe = new ProgramElement(model, name, IProgramElement.Kind.INTER_TYPE_METHOD, getBinarySourceLocation( - // aspect, itd.getSourceLocation()), rtMunger.getSignature().getModifiers(), null, Collections.EMPTY_LIST); - // setParams(pe, sig); - // return pe; - } - // other cases ignored for now - return null; - } - - private static void setParams(IProgramElement pe, ResolvedMember sig) { - // do it for itds too - UnresolvedType[] ts = sig.getParameterTypes(); - pe.setParameterNames(Collections.emptyList()); - // TODO should be doing param names? - if (ts == null) { - pe.setParameterSignatures(Collections.emptyList(), Collections.emptyList()); - } else { - List paramSigs = new ArrayList(); - for (int i = 0; i < ts.length; i++) { - paramSigs.add(ts[i].getSignature().toCharArray()); - } - pe.setParameterSignatures(paramSigs, Collections.emptyList()); - } - pe.setCorrespondingType(sig.getReturnType().getName()); - } - - private static IProgramElement createDeclareParentsChild(AsmManager model, DeclareParents decp) { - IProgramElement decpElement = new ProgramElement(model, "declare parents", IProgramElement.Kind.DECLARE_PARENTS, - getBinarySourceLocation(decp.getDeclaringType(), decp.getSourceLocation()), Modifier.PUBLIC, null, - Collections.emptyList()); - setParentTypesOnDeclareParentsNode(decp, decpElement); - return decpElement; - } - - private static void setParentTypesOnDeclareParentsNode(DeclareParents decp, IProgramElement decpElement) { - TypePatternList tpl = decp.getParents(); - List parents = new ArrayList(); - for (int i = 0; i < tpl.size(); i++) { - parents.add(tpl.get(i).getExactType().getName().replaceAll("\\$", ".")); - } - decpElement.setParentTypes(parents); - } - - public static String getHandle(AsmManager asm, Advice advice) { - if (null == advice.handle) { - ISourceLocation sl = advice.getSourceLocation(); - if (sl != null) { - IProgramElement ipe = asm.getHierarchy().findElementForSourceLine(sl); - advice.handle = ipe.getHandleIdentifier(); - } - } - return advice.handle; - } - - public static void addAdvisedRelationship(AsmManager model, Shadow matchedShadow, ShadowMunger munger) { - if (model == null) { - return; - } - - if (munger instanceof Advice) { - Advice advice = (Advice) munger; - - if (advice.getKind().isPerEntry() || advice.getKind().isCflow()) { - // TODO: might want to show these in the future - return; - } - - if (World.createInjarHierarchy) { - createHierarchyForBinaryAspect(model, advice); - } - - IRelationshipMap mapper = model.getRelationshipMap(); - IProgramElement targetNode = getNode(model, matchedShadow); - if (targetNode == null) { - return; - } - boolean runtimeTest = advice.hasDynamicTests(); - - IProgramElement.ExtraInformation extra = new IProgramElement.ExtraInformation(); - - String adviceHandle = getHandle(model, advice); - if (adviceHandle == null) { - return; - } - - extra.setExtraAdviceInformation(advice.getKind().getName()); - IProgramElement adviceElement = model.getHierarchy().findElementForHandle(adviceHandle); - if (adviceElement != null) { - adviceElement.setExtraInfo(extra); - } - String targetHandle = targetNode.getHandleIdentifier(); - if (advice.getKind().equals(AdviceKind.Softener)) { - IRelationship foreward = mapper.get(adviceHandle, IRelationship.Kind.DECLARE_SOFT, SOFTENS, runtimeTest, true); - if (foreward != null) { - foreward.addTarget(targetHandle); - } - - IRelationship back = mapper.get(targetHandle, IRelationship.Kind.DECLARE, SOFTENED_BY, runtimeTest, true); - if (back != null) { - back.addTarget(adviceHandle); - } - } else { - IRelationship foreward = mapper.get(adviceHandle, IRelationship.Kind.ADVICE, ADVISES, runtimeTest, true); - if (foreward != null) { - foreward.addTarget(targetHandle); - } - - IRelationship back = mapper.get(targetHandle, IRelationship.Kind.ADVICE, ADVISED_BY, runtimeTest, true); - if (back != null) { - back.addTarget(adviceHandle); - } - } - if (adviceElement.getSourceLocation() != null) { - model.addAspectInEffectThisBuild(adviceElement.getSourceLocation().getSourceFile()); - } - } - } - - protected static IProgramElement getNode(AsmManager model, Shadow shadow) { - Member enclosingMember = shadow.getEnclosingCodeSignature(); - // This variant will not be tricked by ITDs that would report they are - // in the target type already. - // This enables us to discover the ITD declaration (in the aspect) and - // advise it appropriately. - - // Have to be smart here, for a code node within an ITD we want to - // lookup the declaration of the - // ITD in the aspect in order to add the code node at the right place - - // and not lookup the - // ITD as it applies in some target type. Due to the use of - // effectiveSignature we will find - // that shadow.getEnclosingCodeSignature() will return a member - // representing the ITD as it will - // appear in the target type. So here, we do an extra bit of analysis to - // make sure we - // do the right thing in the ITD case. - IProgramElement enclosingNode = null; - if (shadow instanceof BcelShadow) { - Member actualEnclosingMember = ((BcelShadow) shadow).getRealEnclosingCodeSignature(); - - if (actualEnclosingMember == null) { - enclosingNode = lookupMember(model.getHierarchy(), shadow.getEnclosingType(), enclosingMember); - } else { - UnresolvedType type = enclosingMember.getDeclaringType(); - UnresolvedType actualType = actualEnclosingMember.getDeclaringType(); - - // if these are not the same, it is an ITD and we need to use - // the latter to lookup - if (type.equals(actualType)) { - enclosingNode = lookupMember(model.getHierarchy(), shadow.getEnclosingType(), enclosingMember); - } else { - enclosingNode = lookupMember(model.getHierarchy(), shadow.getEnclosingType(), actualEnclosingMember); - } - } - } else { - enclosingNode = lookupMember(model.getHierarchy(), shadow.getEnclosingType(), enclosingMember); - } - - if (enclosingNode == null) { - Lint.Kind err = shadow.getIWorld().getLint().shadowNotInStructure; - if (err.isEnabled()) { - err.signal(shadow.toString(), shadow.getSourceLocation()); - } - return null; - } - - Member shadowSig = shadow.getSignature(); - // pr235204 - if (shadow.getKind() == Shadow.MethodCall || shadow.getKind() == Shadow.ConstructorCall - || !shadowSig.equals(enclosingMember)) { - IProgramElement bodyNode = findOrCreateCodeNode(model, enclosingNode, shadowSig, shadow); - return bodyNode; - } else { - return enclosingNode; - } - } - - private static boolean sourceLinesMatch(ISourceLocation location1, ISourceLocation location2) { - return (location1.getLine() == location2.getLine()); - } - - /** - * Finds or creates a code IProgramElement for the given shadow. - * - * The byteCodeName of the created node is set to 'shadowSig.getName() + "!" + counter', eg "println!3". The counter is the - * occurence count of children within the enclosingNode which have the same name. So, for example, if a method contains two - * System.out.println statements, the first one will have byteCodeName 'println!1' and the second will have byteCodeName - * 'println!2'. This is to ensure the two nodes have unique handles when the handles do not depend on sourcelocations. - * - * Currently the shadows are examined in the sequence they appear in the source file. This means that the counters are - * consistent over incremental builds. All aspects are compiled up front and any new aspect created will force a full build. - * Moreover, if the body of the enclosingShadow is changed, then the model for this is rebuilt from scratch. - */ - private static IProgramElement findOrCreateCodeNode(AsmManager asm, IProgramElement enclosingNode, Member shadowSig, - Shadow shadow) { - for (Iterator it = enclosingNode.getChildren().iterator(); it.hasNext();) { - IProgramElement node = (IProgramElement) it.next(); - int excl = node.getBytecodeName().lastIndexOf('!'); - if (((excl != -1 && shadowSig.getName().equals(node.getBytecodeName().substring(0, excl))) || shadowSig.getName() - .equals(node.getBytecodeName())) - && shadowSig.getSignature().equals(node.getBytecodeSignature()) - && sourceLinesMatch(node.getSourceLocation(), shadow.getSourceLocation())) { - return node; - } - } - - ISourceLocation sl = shadow.getSourceLocation(); - - // XXX why not use shadow file? new SourceLocation(sl.getSourceFile(), - // sl.getLine()), - SourceLocation peLoc = new SourceLocation(enclosingNode.getSourceLocation().getSourceFile(), sl.getLine()); - peLoc.setOffset(sl.getOffset()); - IProgramElement peNode = new ProgramElement(asm, shadow.toString(), IProgramElement.Kind.CODE, peLoc, 0, null, null); - - // check to see if the enclosing shadow already has children with the - // same name. If so we want to add a counter to the byteCodeName - // otherwise - // we wont get unique handles - int numberOfChildrenWithThisName = 0; - for (IProgramElement child: enclosingNode.getChildren()) { - if (child.getName().equals(shadow.toString())) { - numberOfChildrenWithThisName++; - } - } - peNode.setBytecodeName(shadowSig.getName() + "!" + String.valueOf(numberOfChildrenWithThisName + 1)); - peNode.setBytecodeSignature(shadowSig.getSignature()); - enclosingNode.addChild(peNode); - return peNode; - } - - private static IProgramElement lookupMember(IHierarchy model, UnresolvedType declaringType, Member member) { - IProgramElement typeElement = model.findElementForType(declaringType.getPackageName(), declaringType.getClassName()); - if (typeElement == null) { - return null; - } - for (Iterator it = typeElement.getChildren().iterator(); it.hasNext();) { - IProgramElement element = (IProgramElement) it.next(); - if (member.getName().equals(element.getBytecodeName()) && member.getSignature().equals(element.getBytecodeSignature())) { - return element; - } - } - // if we can't find the member, we'll just put it in the class - return typeElement; - } - - /** - * Add a relationship for a matching declare annotation method or declare annotation constructor. Locating the method is a messy - * (for messy read 'fragile') bit of code that could break at any moment but it's working for my simple testcase. - */ - public static void addDeclareAnnotationMethodRelationship(ISourceLocation sourceLocation, String affectedTypeName, - ResolvedMember affectedMethod, AsmManager model) { - if (model == null) { - return; - } - - String pkg = null; - String type = affectedTypeName; - int packageSeparator = affectedTypeName.lastIndexOf("."); - if (packageSeparator != -1) { - pkg = affectedTypeName.substring(0, packageSeparator); - type = affectedTypeName.substring(packageSeparator + 1); - } - - IHierarchy hierarchy = model.getHierarchy(); - - IProgramElement typeElem = hierarchy.findElementForType(pkg, type); - if (typeElem == null) { - return; - } - if (!typeElem.getKind().isType()) { - throw new IllegalStateException("Did not find a type element, found a "+typeElem.getKind()+" element"); - } - - StringBuilder parmString = new StringBuilder("("); - UnresolvedType[] args = affectedMethod.getParameterTypes(); - for (int i = 0; i < args.length; i++) { - parmString.append(args[i].getName()); - if ((i + 1) < args.length) { - parmString.append(","); - } - } - parmString.append(")"); - IProgramElement methodElem = null; - - if (affectedMethod.getName().startsWith("")) { - // its a ctor - methodElem = hierarchy.findElementForSignature(typeElem, IProgramElement.Kind.CONSTRUCTOR, type + parmString); - if (methodElem == null && args.length == 0) { - methodElem = typeElem; // assume default ctor - } - } else { - // its a method - methodElem = hierarchy.findElementForSignature(typeElem, IProgramElement.Kind.METHOD, affectedMethod.getName() - + parmString); - } - - if (methodElem == null) { - return; - } - - try { - String targetHandle = methodElem.getHandleIdentifier(); - if (targetHandle == null) { - return; - } - - IProgramElement sourceNode = hierarchy.findElementForSourceLine(sourceLocation); - String sourceHandle = sourceNode.getHandleIdentifier(); - if (sourceHandle == null) { - return; - } - - IRelationshipMap mapper = model.getRelationshipMap(); - IRelationship foreward = mapper.get(sourceHandle, IRelationship.Kind.DECLARE_INTER_TYPE, ANNOTATES, false, true); - foreward.addTarget(targetHandle); - - IRelationship back = mapper.get(targetHandle, IRelationship.Kind.DECLARE_INTER_TYPE, ANNOTATED_BY, false, true); - back.addTarget(sourceHandle); - } catch (Throwable t) { // I'm worried about that code above, this will - // make sure we don't explode if it plays up - t.printStackTrace(); // I know I know .. but I don't want to lose - // it! - } - } - - /** - * Add a relationship for a matching declare ATfield. Locating the field is trickier than it might seem since we have no line - * number info for it, we have to dig through the structure model under the fields' type in order to locate it. - */ - public static void addDeclareAnnotationFieldRelationship(AsmManager model, ISourceLocation declareLocation, - String affectedTypeName, ResolvedMember affectedFieldName, boolean isRemove) { - if (model == null) { - return; - } - - String pkg = null; - String type = affectedTypeName; - int packageSeparator = affectedTypeName.lastIndexOf("."); - if (packageSeparator != -1) { - pkg = affectedTypeName.substring(0, packageSeparator); - type = affectedTypeName.substring(packageSeparator + 1); - } - IHierarchy hierarchy = model.getHierarchy(); - IProgramElement typeElem = hierarchy.findElementForType(pkg, type); - if (typeElem == null) { - return; - } - - IProgramElement fieldElem = hierarchy.findElementForSignature(typeElem, IProgramElement.Kind.FIELD, - affectedFieldName.getName()); - if (fieldElem == null) { - return; - } - - String targetHandle = fieldElem.getHandleIdentifier(); - if (targetHandle == null) { - return; - } - - IProgramElement sourceNode = hierarchy.findElementForSourceLine(declareLocation); - String sourceHandle = sourceNode.getHandleIdentifier(); - if (sourceHandle == null) { - return; - } - - IRelationshipMap relmap = model.getRelationshipMap(); - // if (isRemove) { - // IRelationship foreward = relmap.get(sourceHandle, IRelationship.Kind.DECLARE_INTER_TYPE, REMOVES_ANNOTATION, false, - // true); - // foreward.addTarget(targetHandle); - // IRelationship back = relmap - // .get(targetHandle, IRelationship.Kind.DECLARE_INTER_TYPE, ANNOTATION_REMOVED_BY, false, true); - // back.addTarget(sourceHandle); - // } else { - IRelationship foreward = relmap.get(sourceHandle, IRelationship.Kind.DECLARE_INTER_TYPE, ANNOTATES, false, true); - foreward.addTarget(targetHandle); - IRelationship back = relmap.get(targetHandle, IRelationship.Kind.DECLARE_INTER_TYPE, ANNOTATED_BY, false, true); - back.addTarget(sourceHandle); - // } - } - -} diff --git a/weaver/src/org/aspectj/weaver/model/AsmRelationshipUtils.java b/weaver/src/org/aspectj/weaver/model/AsmRelationshipUtils.java deleted file mode 100644 index f9c9f6ae2..000000000 --- a/weaver/src/org/aspectj/weaver/model/AsmRelationshipUtils.java +++ /dev/null @@ -1,79 +0,0 @@ -/******************************************************************** - * Copyright (c) 2006 Contributors. All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: IBM Corporation - initial API and implementation - * Helen Hawkins - initial version - *******************************************************************/ -package org.aspectj.weaver.model; - -import org.aspectj.weaver.patterns.AndPointcut; -import org.aspectj.weaver.patterns.OrPointcut; -import org.aspectj.weaver.patterns.Pointcut; -import org.aspectj.weaver.patterns.ReferencePointcut; - -/** - * Provides utility methods for generating details for IProgramElements used when creating the model both from source (via - * AsmElementFormatter.visit(..)) and when filling in the model for binary aspects (via AsmRelationshipProvider bug 145963) - */ -public class AsmRelationshipUtils { - - // public static final String UNDEFINED=""; - public static final String DECLARE_PRECEDENCE = "precedence"; - public static final String DECLARE_SOFT = "soft"; - public static final String DECLARE_PARENTS = "parents"; - public static final String DECLARE_WARNING = "warning"; - public static final String DECLARE_ERROR = "error"; - public static final String DECLARE_UNKNONWN = ""; - public static final String POINTCUT_ABSTRACT = ""; - public static final String POINTCUT_ANONYMOUS = ""; - public static final String DOUBLE_DOTS = ".."; - public static final int MAX_MESSAGE_LENGTH = 18; - public static final String DEC_LABEL = "declare"; - - /** - * Generates the declare message used in the details, for example if the declare warning statement has message - * "There should be no printlns" will return 'declare warning: "There should be n.."' - */ - public static String genDeclareMessage(String message) { - int length = message.length(); - if (length < MAX_MESSAGE_LENGTH) { - return message; - } else { - return message.substring(0, MAX_MESSAGE_LENGTH - 1) + DOUBLE_DOTS; - } - } - - /** - * Generates the pointcut details for the given pointcut, for example an anonymous pointcut will return '' - * and a named pointcut called p() will return 'p()..' - */ - public static String genPointcutDetails(Pointcut pcd) { - StringBuffer details = new StringBuffer(); - if (pcd instanceof ReferencePointcut) { - ReferencePointcut rp = (ReferencePointcut) pcd; - details.append(rp.name).append(DOUBLE_DOTS); - } else if (pcd instanceof AndPointcut) { - AndPointcut ap = (AndPointcut) pcd; - if (ap.getLeft() instanceof ReferencePointcut) { - details.append(ap.getLeft().toString()).append(DOUBLE_DOTS); - } else { - details.append(POINTCUT_ANONYMOUS).append(DOUBLE_DOTS); - } - } else if (pcd instanceof OrPointcut) { - OrPointcut op = (OrPointcut) pcd; - if (op.getLeft() instanceof ReferencePointcut) { - details.append(op.getLeft().toString()).append(DOUBLE_DOTS); - } else { - details.append(POINTCUT_ANONYMOUS).append(DOUBLE_DOTS); - } - } else { - details.append(POINTCUT_ANONYMOUS); - } - return details.toString(); - } - -} diff --git a/weaver/src/org/aspectj/weaver/tools/WeavingAdaptor.java b/weaver/src/org/aspectj/weaver/tools/WeavingAdaptor.java deleted file mode 100644 index a02400fb0..000000000 --- a/weaver/src/org/aspectj/weaver/tools/WeavingAdaptor.java +++ /dev/null @@ -1,951 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2004 IBM Corporation - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Matthew Webster, Adrian Colyer, John Kew + Lyor Goldstein (caching) - * Martin Lippert initial implementation - * ******************************************************************/ - -package org.aspectj.weaver.tools; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintWriter; -import java.net.URL; -import java.net.URLClassLoader; -import java.security.ProtectionDomain; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; -import java.util.StringTokenizer; - -import org.aspectj.bridge.AbortException; -import org.aspectj.bridge.IMessage; -import org.aspectj.bridge.IMessage.Kind; -import org.aspectj.bridge.IMessageContext; -import org.aspectj.bridge.IMessageHandler; -import org.aspectj.bridge.IMessageHolder; -import org.aspectj.bridge.Message; -import org.aspectj.bridge.MessageHandler; -import org.aspectj.bridge.MessageUtil; -import org.aspectj.bridge.MessageWriter; -import org.aspectj.bridge.Version; -import org.aspectj.bridge.WeaveMessage; -import org.aspectj.util.FileUtil; -import org.aspectj.util.LangUtil; -import org.aspectj.weaver.IClassFileProvider; -import org.aspectj.weaver.IUnwovenClassFile; -import org.aspectj.weaver.IWeaveRequestor; -import org.aspectj.weaver.World; -import org.aspectj.weaver.bcel.BcelObjectType; -import org.aspectj.weaver.bcel.BcelWeaver; -import org.aspectj.weaver.bcel.BcelWorld; -import org.aspectj.weaver.bcel.UnwovenClassFile; -import org.aspectj.weaver.tools.cache.CachedClassEntry; -import org.aspectj.weaver.tools.cache.CachedClassReference; -import org.aspectj.weaver.tools.cache.SimpleCache; -import org.aspectj.weaver.tools.cache.SimpleCacheFactory; -import org.aspectj.weaver.tools.cache.WeavedClassCache; - -// OPTIMIZE add guards for all the debug/info/etc -/** - * This adaptor allows the AspectJ compiler to be embedded in an existing system to facilitate load-time weaving. It provides an - * interface for a weaving class loader to provide a classpath to be woven by a set of aspects. A callback is supplied to allow a - * class loader to define classes generated by the compiler during the weaving process. - *

- * A weaving class loader should create a WeavingAdaptor before any classes are defined, typically during construction. - * The set of aspects passed to the adaptor is fixed for the lifetime of the adaptor although the classpath can be augmented. A - * system property can be set to allow verbose weaving messages to be written to the console. - * - */ -public class WeavingAdaptor implements IMessageContext { - - /** - * System property used to turn on verbose weaving messages - */ - public static final String WEAVING_ADAPTOR_VERBOSE = "aj.weaving.verbose"; - public static final String SHOW_WEAVE_INFO_PROPERTY = "org.aspectj.weaver.showWeaveInfo"; - public static final String TRACE_MESSAGES_PROPERTY = "org.aspectj.tracing.messages"; - - private final static String ASPECTJ_BASE_PACKAGE = "org.aspectj."; - private final static String PACKAGE_INITIAL_CHARS = ASPECTJ_BASE_PACKAGE.charAt(0) + "sj"; - - private boolean enabled = false; - protected boolean verbose = getVerbose(); - protected BcelWorld bcelWorld; - protected BcelWeaver weaver; - private IMessageHandler messageHandler; - private WeavingAdaptorMessageHolder messageHolder; - private boolean abortOnError = false; - protected GeneratedClassHandler generatedClassHandler; - protected Map generatedClasses = new HashMap(); - public BcelObjectType delegateForCurrentClass; // lazily initialized, should be used to prevent parsing bytecode multiple - // times - protected ProtectionDomain activeProtectionDomain; - - private boolean haveWarnedOnJavax = false; - protected WeavedClassCache cache; - - private int weavingSpecialTypes = 0; - private static final int INITIALIZED = 0x1; - private static final int WEAVE_JAVA_PACKAGE = 0x2; - private static final int WEAVE_JAVAX_PACKAGE = 0x4; - - private static Trace trace = TraceFactory.getTraceFactory().getTrace(WeavingAdaptor.class); - - protected WeavingAdaptor() { - } - - /** - * Construct a WeavingAdaptor with a reference to a weaving class loader. The adaptor will automatically search the class loader - * hierarchy to resolve classes. The adaptor will also search the hierarchy for WeavingClassLoader instances to determine the - * set of aspects to be used for weaving. - * - * @param loader instance of ClassLoader - */ - public WeavingAdaptor(WeavingClassLoader loader) { - // System.err.println("? WeavingAdaptor.(" + loader +"," + aspectURLs.length + ")"); - generatedClassHandler = loader; - init((ClassLoader)loader, getFullClassPath((ClassLoader) loader), getFullAspectPath((ClassLoader) loader/* ,aspectURLs */)); - } - - /** - * Construct a WeavingAdaptor with a reference to a GeneratedClassHandler, a full search path for resolving classes - * and a complete set of aspects. The search path must include classes loaded by the class loader constructing the - * WeavingAdaptor and all its parents in the hierarchy. - * - * @param handler GeneratedClassHandler - * @param classURLs the URLs from which to resolve classes - * @param aspectURLs the aspects used to weave classes defined by this class loader - */ - public WeavingAdaptor(GeneratedClassHandler handler, URL[] classURLs, URL[] aspectURLs) { - // System.err.println("? WeavingAdaptor.()"); - generatedClassHandler = handler; - init(null, FileUtil.makeClasspath(classURLs), FileUtil.makeClasspath(aspectURLs)); - } - - protected List getFullClassPath(ClassLoader loader) { - List list = new LinkedList(); - for (; loader != null; loader = loader.getParent()) { - if (loader instanceof URLClassLoader) { - URL[] urls = ((URLClassLoader) loader).getURLs(); - list.addAll(0, FileUtil.makeClasspath(urls)); - } else { - warn("cannot determine classpath"); - } - } - // On Java9 it is possible to fail to find a URLClassLoader from which to derive a suitable classpath - // For now we can determine it from the java.class.path: - if (LangUtil.is19VMOrGreater()) { - list.add(0, LangUtil.getJrtFsFilePath()); - List javaClassPathEntries = makeClasspath(System.getProperty("java.class.path")); - for (int i=javaClassPathEntries.size()-1;i>=0;i--) { - String javaClassPathEntry = javaClassPathEntries.get(i); - if (!list.contains(javaClassPathEntry)) { - list.add(0,javaClassPathEntry); - } - } - } - // On Java9 the sun.boot.class.path won't be set. System classes accessible through JRT filesystem - list.addAll(0, makeClasspath(System.getProperty("sun.boot.class.path"))); - return list; - } - - private List getFullAspectPath(ClassLoader loader) { - List list = new LinkedList(); - for (; loader != null; loader = loader.getParent()) { - if (loader instanceof WeavingClassLoader) { - URL[] urls = ((WeavingClassLoader) loader).getAspectURLs(); - list.addAll(0, FileUtil.makeClasspath(urls)); - } - } - return list; - } - - private static boolean getVerbose() { - try { - return Boolean.getBoolean(WEAVING_ADAPTOR_VERBOSE); - } catch (Throwable t) { - // security exception - return false; - } - } - - /** - * Initialize the WeavingAdapter - * @param loader ClassLoader used by this adapter; which can be null - * @param classPath classpath of this adapter - * @param aspectPath list of aspect paths - */ - private void init(ClassLoader loader, List classPath, List aspectPath) { - abortOnError = true; - createMessageHandler(); - - info("using classpath: " + classPath); - info("using aspectpath: " + aspectPath); - - bcelWorld = new BcelWorld(classPath, messageHandler, null); - bcelWorld.setXnoInline(false); - bcelWorld.getLint().loadDefaultProperties(); - if (LangUtil.is15VMOrGreater()) { - bcelWorld.setBehaveInJava5Way(true); - } - - weaver = new BcelWeaver(bcelWorld); - registerAspectLibraries(aspectPath); - initializeCache(loader, aspectPath, null, getMessageHandler()); - enabled = true; - } - - /** - * If the cache is enabled, initialize it and swap out the existing classhandler - * for the caching one - - * - * @param loader classloader for this adapter, may be null - * @param aspects List of strings representing aspects managed by the adapter; these could be urls or classnames - * @param existingClassHandler current class handler - * @param myMessageHandler current message handler - */ - protected void initializeCache(ClassLoader loader, List aspects, GeneratedClassHandler existingClassHandler, IMessageHandler myMessageHandler) { - if (WeavedClassCache.isEnabled()) { - cache = WeavedClassCache.createCache(loader, aspects, existingClassHandler, myMessageHandler); - // Wrap the existing class handler so that any generated classes are also cached - if (cache != null) { - this.generatedClassHandler = cache.getCachingClassHandler(); - } - } - } - - - protected void createMessageHandler() { - messageHolder = new WeavingAdaptorMessageHolder(new PrintWriter(System.err)); - messageHandler = messageHolder; - if (verbose) { - messageHandler.dontIgnore(IMessage.INFO); - } - if (Boolean.getBoolean(SHOW_WEAVE_INFO_PROPERTY)) { - messageHandler.dontIgnore(IMessage.WEAVEINFO); - } - info("AspectJ Weaver Version " + Version.text + " built on " + Version.time_text); //$NON-NLS-1$ - } - - protected IMessageHandler getMessageHandler() { - return messageHandler; - } - - public IMessageHolder getMessageHolder() { - return messageHolder; - } - - protected void setMessageHandler(IMessageHandler mh) { - if (mh instanceof ISupportsMessageContext) { - ISupportsMessageContext smc = (ISupportsMessageContext) mh; - smc.setMessageContext(this); - } - if (mh != messageHolder) { - messageHolder.setDelegate(mh); - } - messageHolder.flushMessages(); - } - - protected void disable() { - if (trace.isTraceEnabled()) { - trace.enter("disable", this); - } - - enabled = false; - messageHolder.flushMessages(); - - if (trace.isTraceEnabled()) { - trace.exit("disable"); - } - } - - protected void enable() { - enabled = true; - messageHolder.flushMessages(); - } - - protected boolean isEnabled() { - return enabled; - } - - /** - * Appends URL to path used by the WeavingAdptor to resolve classes - * - * @param url to be appended to search path - */ - public void addURL(URL url) { - File libFile = new File(url.getPath()); - try { - weaver.addLibraryJarFile(libFile); - } catch (IOException ex) { - warn("bad library: '" + libFile + "'"); - } - } - - /** - * Weave a class using aspects previously supplied to the adaptor. - * - * @param name the name of the class - * @param bytes the class bytes - * @return the woven bytes - * @exception IOException weave failed - */ - public byte[] weaveClass(String name, byte[] bytes) throws IOException { - return weaveClass(name, bytes, false); - } - - // Track if the weaver is already running on this thread - don't allow re-entrant calls - private ThreadLocal weaverRunning = new ThreadLocal() { - @Override - protected Boolean initialValue() { - return Boolean.FALSE; - } - }; - - /** - * Weave a class using aspects previously supplied to the adaptor. - * - * @param name the name of the class - * @param bytes the class bytes - * @param mustWeave if true then this class *must* get woven (used for concrete aspects generated from XML) - * @return the woven bytes - * @exception IOException weave failed - */ - public byte[] weaveClass(String name, byte[] bytes, boolean mustWeave) throws IOException { - if (trace == null) { - // Pr231945: we are likely to be under tomcat and ENABLE_CLEAR_REFERENCES hasn't been set - System.err - .println("AspectJ Weaver cannot continue to weave, static state has been cleared. Are you under Tomcat? In order to weave '" - + name - + "' during shutdown, 'org.apache.catalina.loader.WebappClassLoader.ENABLE_CLEAR_REFERENCES=false' must be set (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=231945)."); - return bytes; - } - if (weaverRunning.get()) { - // System.out.println("AJC: avoiding re-entrant call to transform " + name); - return bytes; - } - try { - weaverRunning.set(true); - if (trace.isTraceEnabled()) { - trace.enter("weaveClass", this, new Object[] { name, bytes }); - } - - if (!enabled) { - if (trace.isTraceEnabled()) { - trace.exit("weaveClass", false); - } - return bytes; - } - - boolean debugOn = !messageHandler.isIgnoring(Message.DEBUG); - - try { - delegateForCurrentClass = null; - name = name.replace('/', '.'); - if (couldWeave(name, bytes)) { - if (accept(name, bytes)) { - - // Determine if we have the weaved class cached - CachedClassReference cacheKey = null; - final byte[] original_bytes = bytes; - if (cache != null && !mustWeave) { - cacheKey = cache.createCacheKey(name, original_bytes); - CachedClassEntry entry = cache.get(cacheKey, original_bytes); - if (entry != null) { - // If the entry has been explicitly ignored - // return the original bytes - if (entry.isIgnored()) { - return bytes; - } - return entry.getBytes(); - } - } - - // TODO @AspectJ problem - // Annotation style aspects need to be included regardless in order to get - // a valid aspectOf()/hasAspect() generated in them. However - if they are excluded - // (via include/exclude in aop.xml) they really should only get aspectOf()/hasAspect() - // and not be included in the full set of aspects being applied by 'this' weaver - if (debugOn) { - debug("weaving '" + name + "'"); - } - bytes = getWovenBytes(name, bytes); - // temporarily out - searching for @Aspect annotated types is a slow thing to do - we should - // expect the user to name them if they want them woven - just like code style - // } else if (shouldWeaveAnnotationStyleAspect(name, bytes)) { - // if (mustWeave) { - // if (bcelWorld.getLint().mustWeaveXmlDefinedAspects.isEnabled()) { - // bcelWorld.getLint().mustWeaveXmlDefinedAspects.signal(name, null); - // } - // } - // // an @AspectJ aspect needs to be at least munged by the aspectOf munger - // if (debugOn) { - // debug("weaving '" + name + "'"); - // } - // bytes = getAtAspectJAspectBytes(name, bytes); - - // Add the weaved class to the cache only if there - // has been an actual change - // JVK: Is there a better way to check if the class has - // been transformed without carrying up some value - // from the depths? - if (cacheKey != null) { - // If no transform has been applied, mark the class - // as ignored. - if (Arrays.equals(original_bytes, bytes)) { - cache.ignore(cacheKey, original_bytes); - } else { - cache.put(cacheKey, original_bytes, bytes); - } - } - } else if (debugOn) { - debug("not weaving '" + name + "'"); - } - } else if (debugOn) { - debug("cannot weave '" + name + "'"); - } - } finally { - delegateForCurrentClass = null; - } - - if (trace.isTraceEnabled()) { - trace.exit("weaveClass", bytes); - } - return bytes; - } finally { - weaverRunning.set(false); - } - } - - /** - * @param name - * @return true if even valid to weave: either with an accept check or to munge it for @AspectJ aspectof support - */ - private boolean couldWeave(String name, byte[] bytes) { - return !generatedClasses.containsKey(name) && shouldWeaveName(name); - } - - // ATAJ - protected boolean accept(String name, byte[] bytes) { - return true; - } - - protected boolean shouldDump(String name, boolean before) { - return false; - } - - private boolean shouldWeaveName(String name) { - if (PACKAGE_INITIAL_CHARS.indexOf(name.charAt(0)) != -1) { - if ((weavingSpecialTypes & INITIALIZED) == 0) { - weavingSpecialTypes |= INITIALIZED; - // initialize it - Properties p = weaver.getWorld().getExtraConfiguration(); - if (p != null) { - boolean b = p.getProperty(World.xsetWEAVE_JAVA_PACKAGES, "false").equalsIgnoreCase("true"); - if (b) { - weavingSpecialTypes |= WEAVE_JAVA_PACKAGE; - } - b = p.getProperty(World.xsetWEAVE_JAVAX_PACKAGES, "false").equalsIgnoreCase("true"); - if (b) { - weavingSpecialTypes |= WEAVE_JAVAX_PACKAGE; - } - } - } - if (name.startsWith(ASPECTJ_BASE_PACKAGE)) { - return false; - } - if (name.startsWith("sun.reflect.")) {// JDK reflect - return false; - } - if (name.startsWith("javax.")) { - if ((weavingSpecialTypes & WEAVE_JAVAX_PACKAGE) != 0) { - return true; - } else { - if (!haveWarnedOnJavax) { - haveWarnedOnJavax = true; - warn("javax.* types are not being woven because the weaver option '-Xset:weaveJavaxPackages=true' has not been specified"); - } - return false; - } - } - if (name.startsWith("java.")) { - if ((weavingSpecialTypes & WEAVE_JAVA_PACKAGE) != 0) { - return true; - } else { - return false; - } - } - } - // boolean should = !(name.startsWith("org.aspectj.") - // || (name.startsWith("java.") && (weavingSpecialTypes & WEAVE_JAVA_PACKAGE) == 0) - // || (name.startsWith("javax.") && (weavingSpecialTypes & WEAVE_JAVAX_PACKAGE) == 0) - // // || name.startsWith("$Proxy")//JDK proxies//FIXME AV is that 1.3 proxy ? fe. ataspect.$Proxy0 is a java5 proxy... - // || name.startsWith("sun.reflect.")); - return true; - } - - /** - * We allow @AJ aspect weaving so that we can add aspectOf() as part of the weaving (and not part of the source compilation) - * - * @param name - * @param bytes bytecode (from classloader), allow to NOT lookup stuff on disk again during resolve - * @return true if @Aspect - */ - private boolean shouldWeaveAnnotationStyleAspect(String name, byte[] bytes) { - if (delegateForCurrentClass == null) { - // if (weaver.getWorld().isASMAround()) return asmCheckAnnotationStyleAspect(bytes); - // else - ensureDelegateInitialized(name, bytes); - } - return (delegateForCurrentClass.isAnnotationStyleAspect()); - } - - // private boolean asmCheckAnnotationStyleAspect(byte[] bytes) { - // IsAtAspectAnnotationVisitor detector = new IsAtAspectAnnotationVisitor(); - // - // ClassReader cr = new ClassReader(bytes); - // try { - // cr.accept(detector, true);//, ClassReader.SKIP_DEBUG | ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES); - // } catch (Exception spe) { - // // if anything goes wrong, e.g., an NPE, then assume it's NOT an @AspectJ aspect... - // System.err.println("Unexpected problem parsing bytes to discover @Aspect annotation"); - // spe.printStackTrace(); - // return false; - // } - // - // return detector.isAspect(); - // } - - protected void ensureDelegateInitialized(String name, byte[] bytes) { - if (delegateForCurrentClass == null) { - BcelWorld world = (BcelWorld) weaver.getWorld(); - delegateForCurrentClass = world.addSourceObjectType(name, bytes, false); - } - } - - /** - * Weave a set of bytes defining a class. - * - * @param name the name of the class being woven - * @param bytes the bytes that define the class - * @return byte[] the woven bytes for the class - * @throws IOException - */ - private byte[] getWovenBytes(String name, byte[] bytes) throws IOException { - WeavingClassFileProvider wcp = new WeavingClassFileProvider(name, bytes); - weaver.weave(wcp); - return wcp.getBytes(); - } - - /** - * Weave a set of bytes defining a class for only what is needed to turn @AspectJ aspect in a usefull form ie with aspectOf - * method - see #113587 - * - * @param name the name of the class being woven - * @param bytes the bytes that define the class - * @return byte[] the woven bytes for the class - * @throws IOException - */ - private byte[] getAtAspectJAspectBytes(String name, byte[] bytes) throws IOException { - WeavingClassFileProvider wcp = new WeavingClassFileProvider(name, bytes); - wcp.setApplyAtAspectJMungersOnly(); - weaver.weave(wcp); - return wcp.getBytes(); - } - - private void registerAspectLibraries(List aspectPath) { - // System.err.println("? WeavingAdaptor.registerAspectLibraries(" + aspectPath + ")"); - for (Iterator i = aspectPath.iterator(); i.hasNext();) { - String libName = (String) i.next(); - addAspectLibrary(libName); - } - - weaver.prepareForWeave(); - } - - /* - * Register an aspect library with this classloader for use during weaving. This class loader will also return (unmodified) any - * of the classes in the library in response to a findClass() request. The library is not required to be on the - * weavingClasspath given when this classloader was constructed. - * - * @param aspectLibraryJarFile a jar file representing an aspect library - * - * @throws IOException - */ - private void addAspectLibrary(String aspectLibraryName) { - File aspectLibrary = new File(aspectLibraryName); - if (aspectLibrary.isDirectory() || (FileUtil.isZipFile(aspectLibrary))) { - try { - info("adding aspect library: '" + aspectLibrary + "'"); - weaver.addLibraryJarFile(aspectLibrary); - } catch (IOException ex) { - error("exception adding aspect library: '" + ex + "'"); - } - } else { - error("bad aspect library: '" + aspectLibrary + "'"); - } - } - - private static List makeClasspath(String cp) { - List ret = new ArrayList(); - if (cp != null) { - StringTokenizer tok = new StringTokenizer(cp, File.pathSeparator); - while (tok.hasMoreTokens()) { - ret.add(tok.nextToken()); - } - } - return ret; - } - - protected boolean debug(String message) { - return MessageUtil.debug(messageHandler, message); - } - - protected boolean info(String message) { - return MessageUtil.info(messageHandler, message); - } - - protected boolean warn(String message) { - return MessageUtil.warn(messageHandler, message); - } - - protected boolean warn(String message, Throwable th) { - return messageHandler.handleMessage(new Message(message, IMessage.WARNING, th, null)); - } - - protected boolean error(String message) { - return MessageUtil.error(messageHandler, message); - } - - protected boolean error(String message, Throwable th) { - return messageHandler.handleMessage(new Message(message, IMessage.ERROR, th, null)); - } - - public String getContextId() { - return "WeavingAdaptor"; - } - - /** - * Dump the given bytcode in _dump/... (dev mode) - * - * @param name - * @param b - * @param before whether we are dumping before weaving - * @throws Throwable - */ - protected void dump(String name, byte[] b, boolean before) { - String dirName = getDumpDir(); - - if (before) { - dirName = dirName + File.separator + "_before"; - } - - String className = name.replace('.', '/'); - final File dir; - if (className.indexOf('/') > 0) { - dir = new File(dirName + File.separator + className.substring(0, className.lastIndexOf('/'))); - } else { - dir = new File(dirName); - } - dir.mkdirs(); - String fileName = dirName + File.separator + className + ".class"; - try { - // System.out.println("WeavingAdaptor.dump() fileName=" + new File(fileName).getAbsolutePath()); - FileOutputStream os = new FileOutputStream(fileName); - os.write(b); - os.close(); - } catch (IOException ex) { - warn("unable to dump class " + name + " in directory " + dirName, ex); - } - } - - /** - * @return the directory in which to dump - default is _ajdump but it - */ - protected String getDumpDir() { - return "_ajdump"; - } - - /** - * Processes messages arising from weaver operations. Tell weaver to abort on any message more severe than warning. - */ - protected class WeavingAdaptorMessageHolder extends MessageHandler { - - private IMessageHandler delegate; - private List savedMessages; - - protected boolean traceMessages = Boolean.getBoolean(TRACE_MESSAGES_PROPERTY); - - public WeavingAdaptorMessageHolder(PrintWriter writer) { - - this.delegate = new WeavingAdaptorMessageWriter(writer); - super.dontIgnore(IMessage.WEAVEINFO); - } - - private void traceMessage(IMessage message) { - if (message instanceof WeaveMessage) { - trace.debug(render(message)); - } else if (message.isDebug()) { - trace.debug(render(message)); - } else if (message.isInfo()) { - trace.info(render(message)); - } else if (message.isWarning()) { - trace.warn(render(message), message.getThrown()); - } else if (message.isError()) { - trace.error(render(message), message.getThrown()); - } else if (message.isFailed()) { - trace.fatal(render(message), message.getThrown()); - } else if (message.isAbort()) { - trace.fatal(render(message), message.getThrown()); - } else { - trace.error(render(message), message.getThrown()); - } - } - - protected String render(IMessage message) { - return "[" + getContextId() + "] " + message.toString(); - } - - public void flushMessages() { - if (savedMessages == null) { - savedMessages = new ArrayList(); - savedMessages.addAll(super.getUnmodifiableListView()); - clearMessages(); - for (IMessage message : savedMessages) { - delegate.handleMessage(message); - } - } - // accumulating = false; - // messages.clear(); - } - - public void setDelegate(IMessageHandler messageHandler) { - delegate = messageHandler; - } - - /* - * IMessageHandler - */ - - @Override - public boolean handleMessage(IMessage message) throws AbortException { - if (traceMessages) { - traceMessage(message); - } - - super.handleMessage(message); - - if (abortOnError && 0 <= message.getKind().compareTo(IMessage.ERROR)) { - throw new AbortException(message); - } - // if (accumulating) { - // boolean result = addMessage(message); - // if (abortOnError && 0 <= message.getKind().compareTo(IMessage.ERROR)) { - // throw new AbortException(message); - // } - // return result; - // } - // else return delegate.handleMessage(message); - - if (savedMessages != null) { - delegate.handleMessage(message); - } - return true; - } - - @Override - public boolean isIgnoring(Kind kind) { - return delegate.isIgnoring(kind); - } - - @Override - public void dontIgnore(IMessage.Kind kind) { - if (null != kind && delegate != null) { - delegate.dontIgnore(kind); - } - } - - @Override - public void ignore(Kind kind) { - if (null != kind && delegate != null) { - delegate.ignore(kind); - } - } - - /* - * IMessageHolder - */ - - @Override - public List getUnmodifiableListView() { - // System.err.println("? WeavingAdaptorMessageHolder.getUnmodifiableListView() savedMessages=" + savedMessages); - List allMessages = new ArrayList(); - allMessages.addAll(savedMessages); - allMessages.addAll(super.getUnmodifiableListView()); - return allMessages; - } - } - - protected class WeavingAdaptorMessageWriter extends MessageWriter { - - private final Set ignoring = new HashSet(); - private final IMessage.Kind failKind; - - public WeavingAdaptorMessageWriter(PrintWriter writer) { - super(writer, true); - - ignore(IMessage.WEAVEINFO); - ignore(IMessage.DEBUG); - ignore(IMessage.INFO); - this.failKind = IMessage.ERROR; - } - - @Override - public boolean handleMessage(IMessage message) throws AbortException { - // boolean result = - super.handleMessage(message); - if (abortOnError && 0 <= message.getKind().compareTo(failKind)) { - throw new AbortException(message); - } - return true; - } - - @Override - public boolean isIgnoring(Kind kind) { - return ((null != kind) && (ignoring.contains(kind))); - } - - /** - * Set a message kind to be ignored from now on - */ - @Override - public void ignore(IMessage.Kind kind) { - if ((null != kind) && (!ignoring.contains(kind))) { - ignoring.add(kind); - } - } - - /** - * Remove a message kind from the list of those ignored from now on. - */ - @Override - public void dontIgnore(IMessage.Kind kind) { - if (null != kind) { - ignoring.remove(kind); - } - } - - @Override - protected String render(IMessage message) { - return "[" + getContextId() + "] " + super.render(message); - } - } - - private class WeavingClassFileProvider implements IClassFileProvider { - - private final UnwovenClassFile unwovenClass; - private final List unwovenClasses = new ArrayList(); - private IUnwovenClassFile wovenClass; - private boolean isApplyAtAspectJMungersOnly = false; - - public WeavingClassFileProvider(String name, byte[] bytes) { - ensureDelegateInitialized(name, bytes); - this.unwovenClass = new UnwovenClassFile(name, delegateForCurrentClass.getResolvedTypeX().getName(), bytes); - this.unwovenClasses.add(unwovenClass); - - if (shouldDump(name.replace('/', '.'), true)) { - dump(name, bytes, true); - } - - } - - public void setApplyAtAspectJMungersOnly() { - isApplyAtAspectJMungersOnly = true; - } - - public boolean isApplyAtAspectJMungersOnly() { - return isApplyAtAspectJMungersOnly; - } - - public byte[] getBytes() { - if (wovenClass != null) { - return wovenClass.getBytes(); - } else { - return unwovenClass.getBytes(); - } - } - - public Iterator getClassFileIterator() { - return unwovenClasses.iterator(); - } - - public IWeaveRequestor getRequestor() { - return new IWeaveRequestor() { - - public void acceptResult(IUnwovenClassFile result) { - if (wovenClass == null) { - wovenClass = result; - String name = result.getClassName(); - if (shouldDump(name.replace('/', '.'), false)) { - dump(name, result.getBytes(), false); - } - } else { - // Classes generated by weaver e.g. around closure advice - String className = result.getClassName(); - byte[] resultBytes = result.getBytes(); - - if (SimpleCacheFactory.isEnabled()) { - SimpleCache lacache=SimpleCacheFactory.createSimpleCache(); - lacache.put(result.getClassName(), wovenClass.getBytes(), result.getBytes()); - lacache.addGeneratedClassesNames(wovenClass.getClassName(), wovenClass.getBytes(), result.getClassName()); - } - - generatedClasses.put(className, result); - generatedClasses.put(wovenClass.getClassName(), result); - generatedClassHandler.acceptClass(className, null, resultBytes); - } - } - - public void processingReweavableState() { - } - - public void addingTypeMungers() { - } - - public void weavingAspects() { - } - - public void weavingClasses() { - } - - public void weaveCompleted() { - // ResolvedType.resetPrimitives(); - if (delegateForCurrentClass != null) { - delegateForCurrentClass.weavingCompleted(); - } - // ResolvedType.resetPrimitives(); - // bcelWorld.discardType(typeBeingProcessed.getResolvedTypeX()); // work in progress - } - }; - } - } - - public void setActiveProtectionDomain(ProtectionDomain protectionDomain) { - activeProtectionDomain = protectionDomain; - } -} \ No newline at end of file diff --git a/weaver/src/org/aspectj/weaver/tools/cache/AbstractCacheBacking.java b/weaver/src/org/aspectj/weaver/tools/cache/AbstractCacheBacking.java deleted file mode 100644 index 649e21d05..000000000 --- a/weaver/src/org/aspectj/weaver/tools/cache/AbstractCacheBacking.java +++ /dev/null @@ -1,45 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * John Kew (vmware) initial implementation - * Lyor Goldstein (vmware) add support for weaved class being re-defined - *******************************************************************************/ -package org.aspectj.weaver.tools.cache; - -import java.util.zip.CRC32; - -import org.aspectj.weaver.tools.Trace; -import org.aspectj.weaver.tools.TraceFactory; - -/** - * Basic "common" {@link CacheBacking} implementation - */ -public abstract class AbstractCacheBacking implements CacheBacking { - protected final Trace logger=TraceFactory.getTraceFactory().getTrace(getClass()); - - protected AbstractCacheBacking () { - super(); - } - - /** - * Calculates CRC32 on the provided bytes - * @param bytes The bytes array - ignored if null/empty - * @return Calculated CRC - * @see {@link CRC32} - */ - public static final long crc (byte[] bytes) { - if ((bytes == null) || (bytes.length <= 0)) { - return 0L; - } - - CRC32 crc32=new CRC32(); - crc32.update(bytes); - return crc32.getValue(); - } -} diff --git a/weaver/src/org/aspectj/weaver/tools/cache/AbstractFileCacheBacking.java b/weaver/src/org/aspectj/weaver/tools/cache/AbstractFileCacheBacking.java deleted file mode 100644 index 5447c158b..000000000 --- a/weaver/src/org/aspectj/weaver/tools/cache/AbstractFileCacheBacking.java +++ /dev/null @@ -1,86 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * John Kew (vmware) initial implementation - * Lyor Goldstein (vmware) add support for weaved class being re-defined - *******************************************************************************/ -package org.aspectj.weaver.tools.cache; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -/** - * Useful "common" functionality for caching to files - */ -public abstract class AbstractFileCacheBacking extends AbstractCacheBacking { - /** - * Default property used to specify a default weaving cache dir location - */ - public static final String WEAVED_CLASS_CACHE_DIR = "aj.weaving.cache.dir"; - private final File cacheDirectory; - - protected AbstractFileCacheBacking (File cacheDirectory) { - if ((this.cacheDirectory=cacheDirectory) == null) { - throw new IllegalStateException("No cache directory specified"); - } - } - - public File getCacheDirectory () { - return cacheDirectory; - } - - protected void writeClassBytes (String key, byte[] bytes) throws Exception { - File dir=getCacheDirectory(), file=new File(dir, key); - FileOutputStream out=new FileOutputStream(file); - try { - out.write(bytes); - } finally { - close(out, file); - } - } - - protected void delete(File file) { - if (file.exists() && (!file.delete())) { - if ((logger != null) && logger.isTraceEnabled()) { - logger.error("Error deleting file " + file.getAbsolutePath()); - } - } - } - - protected void close(OutputStream out, File file) { - if (out != null) { - try { - out.close(); - } catch (IOException e) { - if ((logger != null) && logger.isTraceEnabled()) { - logger.error("Failed (" + e.getClass().getSimpleName() + ")" - + " to close write file " + file.getAbsolutePath() - + ": " + e.getMessage(), e); - } - } - } - } - - protected void close(InputStream in, File file) { - if (in != null) { - try { - in.close(); - } catch (IOException e) { - if ((logger != null) && logger.isTraceEnabled()) { - logger.error("Failed (" + e.getClass().getSimpleName() + ")" - + " to close read file " + file.getAbsolutePath() - + ": " + e.getMessage(), e); - } - } - } - } -} diff --git a/weaver/src/org/aspectj/weaver/tools/cache/AbstractIndexedFileCacheBacking.java b/weaver/src/org/aspectj/weaver/tools/cache/AbstractIndexedFileCacheBacking.java deleted file mode 100644 index 1580277b1..000000000 --- a/weaver/src/org/aspectj/weaver/tools/cache/AbstractIndexedFileCacheBacking.java +++ /dev/null @@ -1,262 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * John Kew (vmware) initial implementation - * Lyor Goldstein (vmware) add support for weaved class being re-defined - *******************************************************************************/ -package org.aspectj.weaver.tools.cache; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; -import java.io.StreamCorruptedException; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedList; -import java.util.Map; -import java.util.TreeMap; - -import org.aspectj.util.LangUtil; - -/** - * Uses an index file to keep track of the cached entries - */ -public abstract class AbstractIndexedFileCacheBacking extends AbstractFileCacheBacking { - /** - * Default name of cache index file - assumed to contain {@link IndexEntry}-s - */ - public static final String INDEX_FILE = "cache.idx"; - protected static final IndexEntry[] EMPTY_INDEX=new IndexEntry[0]; - protected static final String[] EMPTY_KEYS=new String[0]; - - private final File indexFile; - - protected AbstractIndexedFileCacheBacking(File cacheDir) { - super(cacheDir); - - indexFile = new File(cacheDir, INDEX_FILE); - } - - public File getIndexFile () { - return indexFile; - } - - public String[] getKeys(String regex) { - Map index=getIndex(); - if ((index == null) || index.isEmpty()) { - return EMPTY_KEYS; - } - - Collection matches=new LinkedList(); - synchronized(index) { - for (String key : index.keySet()) { - if (key.matches(regex)) { - matches.add(key); - } - } - } - - if (matches.isEmpty()) { - return EMPTY_KEYS; - } else { - return matches.toArray(new String[matches.size()]); - } - } - - protected Map readIndex () { - return readIndex(getCacheDirectory(), getIndexFile()); - } - - protected void writeIndex () { - writeIndex(getIndexFile()); - } - - protected void writeIndex (File file) { - try { - writeIndex(file, getIndex()); - } catch(Exception e) { - if ((logger != null) && logger.isTraceEnabled()) { - logger.warn("writeIndex(" + file + ") " + e.getClass().getSimpleName() + ": " + e.getMessage(), e); - } - } - } - - protected abstract Map getIndex (); - - protected Map readIndex (File cacheDir, File cacheFile) { - Map indexMap=new TreeMap(); - IndexEntry[] idxValues=readIndex(cacheFile); - if (LangUtil.isEmpty(idxValues)) { - if ((logger != null) && logger.isTraceEnabled()) { - logger.debug("readIndex(" + cacheFile + ") no index entries"); - } - return indexMap; - } - - for (IndexEntry ie : idxValues) { - IndexEntry resEntry=resolveIndexMapEntry(cacheDir, ie); - if (resEntry != null) { - indexMap.put(resEntry.key, resEntry); - } else if ((logger != null) && logger.isTraceEnabled()) { - logger.debug("readIndex(" + cacheFile + ") skip " + ie.key); - } - } - - return indexMap; - } - - protected IndexEntry resolveIndexMapEntry (File cacheDir, IndexEntry ie) { - return ie; - } - - public IndexEntry[] readIndex(File indexFile) { - if (!indexFile.canRead()) { - return EMPTY_INDEX; - } - - ObjectInputStream ois = null; - try { - ois = new ObjectInputStream(new FileInputStream(indexFile)); - return (IndexEntry[]) ois.readObject(); - } catch (Exception e) { - if ((logger != null) && logger.isTraceEnabled()) { - logger.error("Failed (" + e.getClass().getSimpleName() + ")" - + " to read index from " + indexFile.getAbsolutePath() - + " : " + e.getMessage(), e); - } - delete(indexFile); - } finally { - close(ois, indexFile); - } - - return EMPTY_INDEX; - } - - protected void writeIndex (File indexFile, Map index) throws IOException { - writeIndex(indexFile, LangUtil.isEmpty(index) ? Collections.emptyList() : index.values()); - } - - protected void writeIndex (File indexFile, IndexEntry ... entries) throws IOException { - writeIndex(indexFile, LangUtil.isEmpty(entries) ? Collections.emptyList() : Arrays.asList(entries)); - } - - protected void writeIndex (File indexFile, Collection entries) throws IOException { - File indexDir=indexFile.getParentFile(); - if ((!indexDir.exists()) && (!indexDir.mkdirs())) { - throw new IOException("Failed to create path to " + indexFile.getAbsolutePath()); - } - - int numEntries=LangUtil.isEmpty(entries) ? 0 : entries.size(); - IndexEntry[] entryValues=(numEntries <= 0) ? null : entries.toArray(new IndexEntry[numEntries]); - // if no entries, simply delete the index file - if (LangUtil.isEmpty(entryValues)) { - if (indexFile.exists() && (!indexFile.delete())) { - throw new StreamCorruptedException("Failed to clean up index file at " + indexFile.getAbsolutePath()); - } - - return; - } - - ObjectOutputStream oos=new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(indexFile), 4096)); - try { - oos.writeObject(entryValues); - } finally { - close(oos, indexFile); - } - } - - public static final IndexEntry createIndexEntry (CachedClassEntry classEntry, byte[] originalBytes) { - if (classEntry == null) { - return null; - } - - IndexEntry indexEntry = new IndexEntry(); - indexEntry.key = classEntry.getKey(); - indexEntry.generated = classEntry.isGenerated(); - indexEntry.ignored = classEntry.isIgnored(); - indexEntry.crcClass = crc(originalBytes); - if (!classEntry.isIgnored()) { - indexEntry.crcWeaved = crc(classEntry.getBytes()); - } - - return indexEntry; - } - - /** - * The default index entry in the index file - */ - public static class IndexEntry implements Serializable, Cloneable { - private static final long serialVersionUID = 756391290557029363L; - - public String key; - public boolean generated; - public boolean ignored; - public long crcClass; - public long crcWeaved; - - public IndexEntry () { - super(); - } - - @Override - public IndexEntry clone () { - try { - return getClass().cast(super.clone()); - } catch(CloneNotSupportedException e) { - throw new RuntimeException("Failed to clone: " + toString() + ": " + e.getMessage(), e); - } - } - - @Override - public int hashCode() { - return (int) (key.hashCode() - + (generated ? 1 : 0) - + (ignored ? 1 : 0) - + crcClass - + crcWeaved); - } - - @Override - public boolean equals(Object obj) { - if (obj == null) - return false; - if (this == obj) - return true; - if (getClass() != obj.getClass()) - return false; - - IndexEntry other=(IndexEntry) obj; - if (this.key.equals(other.key) - && (this.ignored == other.ignored) - && (this.generated == other.generated) - && (this.crcClass == other.crcClass) - && (this.crcWeaved == other.crcWeaved)) { - return true; - } else { - return false; - } - } - - @Override - public String toString() { - return key - + "[" + (generated ? "generated" : "ignored") + "]" - + ";crcClass=0x" + Long.toHexString(crcClass) - + ";crcWeaved=0x" + Long.toHexString(crcWeaved) - ; - } - } - -} diff --git a/weaver/src/org/aspectj/weaver/tools/cache/AsynchronousFileCacheBacking.java b/weaver/src/org/aspectj/weaver/tools/cache/AsynchronousFileCacheBacking.java deleted file mode 100644 index 636a82a7a..000000000 --- a/weaver/src/org/aspectj/weaver/tools/cache/AsynchronousFileCacheBacking.java +++ /dev/null @@ -1,424 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * Lyor Goldstein (vmware) add support for weaved class being re-defined - *******************************************************************************/ - -package org.aspectj.weaver.tools.cache; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.LinkedBlockingQueue; - -import org.aspectj.util.FileUtil; -import org.aspectj.util.LangUtil; -import org.aspectj.weaver.tools.Trace; -import org.aspectj.weaver.tools.TraceFactory; - -/** - * Uses a background thread to do the actual I/O and for caching "persistence" - * so that the caching works faster on repeated activations of the application. - * The class maintains an in-memory cache, and uses a queue of {@link AsyncCommand}s - * to signal to a background thread various actions required to "synchronize" - * the in-memory cache with the persisted copy. Whenever there is a cache miss - * from the {@link #get(CachedClassReference)} call, the weaver issues a - * {@link #put(CachedClassEntry)} call. This call has 2 side-effects:
- *

    - *
  • - * The in-memory cache is updated so that subsequent calls to {@link #get(CachedClassReference)} - * will not return the mapped value. - *
  • - * - *
  • - * An "update index" {@link AsyncCommand} is posted to the background - * thread so that the newly mapped value will be persisted (eventually) - *
  • - *
- * The actual persistence is implemented by the concrete classes - */ -public abstract class AsynchronousFileCacheBacking extends AbstractIndexedFileCacheBacking { - private static final BlockingQueue commandsQ=new LinkedBlockingQueue(); - private static final ExecutorService execService=Executors.newSingleThreadExecutor(); - private static Future commandsRunner; - - protected final Map index, exposedIndex; - protected final Map bytesMap, exposedBytes; - - protected AsynchronousFileCacheBacking (File cacheDir) { - super(cacheDir); - - index = readIndex(cacheDir, getIndexFile()); - exposedIndex = Collections.unmodifiableMap(index); - bytesMap = readClassBytes(index, cacheDir); - exposedBytes = Collections.unmodifiableMap(bytesMap); - } - - @Override - protected Map getIndex() { - return index; - } - - public CachedClassEntry get(CachedClassReference ref, byte[] originalBytes) { - String key=ref.getKey(); - final IndexEntry indexEntry; - synchronized(index) { - if ((indexEntry=index.get(key)) == null) { - return null; - } - } - - if (crc(originalBytes) != indexEntry.crcClass) { - if ((logger != null) && logger.isTraceEnabled()) { - logger.debug("get(" + getCacheDirectory() + ") mismatched original class bytes CRC for " + key); - } - - remove(key); - return null; - } - - if (indexEntry.ignored) { - return new CachedClassEntry(ref, WeavedClassCache.ZERO_BYTES, CachedClassEntry.EntryType.IGNORED); - } - - final byte[] bytes; - synchronized(bytesMap) { - /* - * NOTE: we assume that keys represent classes so if we have their - * bytes they will not be re-created - */ - if ((bytes=bytesMap.remove(key)) == null) { - return null; - } - } - - if (indexEntry.generated) { - return new CachedClassEntry(ref, bytes, CachedClassEntry.EntryType.GENERATED); - } else { - return new CachedClassEntry(ref, bytes, CachedClassEntry.EntryType.WEAVED); - } - } - - public void put(CachedClassEntry entry, byte[] originalBytes) { - String key=entry.getKey(); - byte[] bytes=entry.isIgnored() ? null : entry.getBytes(); - synchronized(index) { - IndexEntry indexEntry=index.get(key); - if (indexEntry != null) { - return; - } - - /* - * Note: we do not cache the class bytes - only send them to - * be saved. The assumption is that the 'put' call was invoked - * because 'get' failed to return any bytes. And since we assume - * that each class bytes are required only once, there is no - * need to cache them - */ - indexEntry = createIndexEntry(entry, originalBytes); - index.put(key, indexEntry); - } - - if (!postCacheCommand(new InsertCommand(this, key, bytes))) { - if ((logger != null) && logger.isTraceEnabled()) { - logger.error("put(" + getCacheDirectory() + ") Failed to post insert command for " + key); - } - } - - if ((logger != null) && logger.isTraceEnabled()) { - logger.debug("put(" + getCacheDirectory() + ")[" + key + "] inserted"); - } - } - - public void remove(CachedClassReference ref) { - remove(ref.getKey()); - } - - protected IndexEntry remove (String key) { - IndexEntry entry; - synchronized(index) { - entry = index.remove(key); - } - - synchronized(bytesMap) { - bytesMap.remove(key); - } - - if (!postCacheCommand(new RemoveCommand(this, key))) { - if ((logger != null) && logger.isTraceEnabled()) { - logger.error("remove(" + getCacheDirectory() + ") Failed to post remove command for " + key); - } - } - - if (entry != null) { - if (!key.equals(entry.key)) { - if ((logger != null) && logger.isTraceEnabled()) { - logger.error("remove(" + getCacheDirectory() + ") Mismatched keys: " + key + " / " + entry.key); - } - } else if ((logger != null) && logger.isTraceEnabled()) { - logger.debug("remove(" + getCacheDirectory() + ")[" + key + "] removed"); - } - } - - return entry; - } - - public List getIndexEntries () { - synchronized(index) { - if (index.isEmpty()) { - return Collections.emptyList(); - } else { - return new ArrayList(index.values()); - } - } - } - - public Map getIndexMap () { - return exposedIndex; - } - - public Map getBytesMap () { - return exposedBytes; - } - - public void clear() { - synchronized(index) { - index.clear(); - } - - if (!postCacheCommand(new ClearCommand(this))) { - if ((logger != null) && logger.isTraceEnabled()) { - logger.error("Failed to post clear command for " + getIndexFile()); - } - } - } - - protected void executeCommand (AsyncCommand cmd) throws Exception { - if (cmd instanceof ClearCommand) { - executeClearCommand(); - } else if (cmd instanceof UpdateIndexCommand) { - executeUpdateIndexCommand(); - } else if (cmd instanceof InsertCommand) { - executeInsertCommand((InsertCommand) cmd); - } else if (cmd instanceof RemoveCommand) { - executeRemoveCommand((RemoveCommand) cmd); - } else { - throw new UnsupportedOperationException("Unknown command: " + cmd); - } - } - - protected void executeClearCommand () throws Exception { - FileUtil.deleteContents(getIndexFile()); - FileUtil.deleteContents(getCacheDirectory()); - } - - protected void executeUpdateIndexCommand () throws Exception { - writeIndex(getIndexFile(), getIndexEntries()); - } - - protected void executeInsertCommand (InsertCommand cmd) throws Exception { - writeIndex(getIndexFile(), getIndexEntries()); - - byte[] bytes=cmd.getClassBytes(); - if (bytes != null) { - writeClassBytes(cmd.getKey(), bytes); - } - } - - protected void executeRemoveCommand (RemoveCommand cmd) throws Exception { - Exception err=null; - try { - removeClassBytes(cmd.getKey()); - } catch(Exception e) { - err = e; - } - - writeIndex(getIndexFile(), getIndexEntries()); - - if (err != null) { - throw err; // check if the class bytes remove had any problems - } - } - - /** - * Helper for {@link #executeRemoveCommand(RemoveCommand)} - * @param key The key representing the class whose bytes are to be removed - * @throws Exception if failed to remove class bytes - */ - protected abstract void removeClassBytes (String key) throws Exception; - - protected abstract Map readClassBytes (Map indexMap, File cacheDir); - - @Override - public String toString() { - return getClass().getSimpleName() + "[" + String.valueOf(getCacheDirectory()) + "]"; - } - - protected static final T createBacking ( - File cacheDir, AsynchronousFileCacheBackingCreator creator) { - final Trace trace=TraceFactory.getTraceFactory().getTrace(AsynchronousFileCacheBacking.class); - if (!cacheDir.exists()) { - if (!cacheDir.mkdirs()) { - if ((trace != null) && trace.isTraceEnabled()) { - trace.error("Unable to create cache directory at " + cacheDir.getAbsolutePath()); - } - return null; - } - } - - if (!cacheDir.canWrite()) { - if ((trace != null) && trace.isTraceEnabled()) { - trace.error("Cache directory is not writable at " + cacheDir.getAbsolutePath()); - } - return null; - } - - // start the service (if needed) only if successfully create the backing instance - T backing=creator.create(cacheDir); - synchronized(execService) { - if (commandsRunner == null) { - commandsRunner = execService.submit(new Runnable() { - @SuppressWarnings("synthetic-access") - public void run() { - for ( ; ; ) { - try { - AsyncCommand cmd=commandsQ.take(); - try { - AsynchronousFileCacheBacking cache=cmd.getCache(); - cache.executeCommand(cmd); - } catch(Exception e) { - if ((trace != null) && trace.isTraceEnabled()) { - trace.error("Failed (" + e.getClass().getSimpleName() + ")" - + " to execute " + cmd + ": " + e.getMessage(), e); - } - } - } catch(InterruptedException e) { - if ((trace != null) && trace.isTraceEnabled()) { - trace.warn("Interrupted"); - } - Thread.currentThread().interrupt(); - break; - } - } - } - }); - } - } - - // fire-up an update-index command in case index was changed by the constructor - if (!postCacheCommand(new UpdateIndexCommand(backing))) { - if ((trace != null) && trace.isTraceEnabled()) { - trace.warn("Failed to offer update index command to " + cacheDir.getAbsolutePath()); - } - } - - return backing; - } - - public static final boolean postCacheCommand (AsyncCommand cmd) { - return commandsQ.offer(cmd); - } - - public static interface AsynchronousFileCacheBackingCreator { - T create (File cacheDir); - } - /** - * Represents an asynchronous command that can be sent to the - * {@link AsynchronousFileCacheBacking} instance to be executed - * on it asynchronously - */ - public static interface AsyncCommand { - /** - * @return The {@link AsynchronousFileCacheBacking} on which - * this command is supposed to be executed - * @see AsynchronousFileCacheBacking#executeCommand(AsyncCommand) - */ - AsynchronousFileCacheBacking getCache (); - } - - public static abstract class AbstractCommand implements AsyncCommand { - private final AsynchronousFileCacheBacking cache; - protected AbstractCommand (AsynchronousFileCacheBacking backing) { - if ((cache=backing) == null) { - throw new IllegalStateException("No backing cache specified"); - } - } - - public final AsynchronousFileCacheBacking getCache () { - return cache; - } - - @Override - public String toString() { - return getClass().getSimpleName() + "[" + getCache() + "]"; - } - } - - public static class ClearCommand extends AbstractCommand { - public ClearCommand (AsynchronousFileCacheBacking cache) { - super(cache); - } - } - - public static class UpdateIndexCommand extends AbstractCommand { - public UpdateIndexCommand (AsynchronousFileCacheBacking cache) { - super(cache); - } - } - - /** - * Base class for {@link AbstractCommand}s that refer to a cache key - */ - public static abstract class KeyedCommand extends AbstractCommand { - private final String key; - protected KeyedCommand (AsynchronousFileCacheBacking cache, String keyValue) { - super(cache); - - if (LangUtil.isEmpty(keyValue)) { - throw new IllegalStateException("No key value"); - } - - key = keyValue; - } - - public final String getKey () { - return key; - } - - @Override - public String toString() { - return super.toString() + "[" + getKey() + "]"; - } - } - - public static class RemoveCommand extends KeyedCommand { - public RemoveCommand (AsynchronousFileCacheBacking cache, String keyValue) { - super(cache, keyValue); - } - } - - public static class InsertCommand extends KeyedCommand { - private final byte[] bytes; - - public InsertCommand (AsynchronousFileCacheBacking cache, String keyValue, byte[] classBytes) { - super(cache, keyValue); - bytes = classBytes; - } - - public final byte[] getClassBytes () { - return bytes; - } - } -} diff --git a/weaver/src/org/aspectj/weaver/tools/cache/CacheBacking.java b/weaver/src/org/aspectj/weaver/tools/cache/CacheBacking.java deleted file mode 100644 index bcf7c0b60..000000000 --- a/weaver/src/org/aspectj/weaver/tools/cache/CacheBacking.java +++ /dev/null @@ -1,63 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * John Kew (vmware) initial implementation - * Lyor Goldstein (vmware) add support for weaved class being re-defined - *******************************************************************************/ -package org.aspectj.weaver.tools.cache; - -/** - * Interface for the backing to the cache; usually a file, - * but could be an in-memory backing for testing. - *

- * aspectj and jvmti provide no suitable guarantees - * on locking for class redefinitions, so every implementation - * must have a some locking mechanism to prevent invalid reads. - */ -public interface CacheBacking { - /** - * Return a list of keys which match the given - * regex. - * - * @param regex - * @return - */ - public String[] getKeys(String regex); - - /** - * Remove an entry from the cache - * - * @param ref - */ - public void remove(CachedClassReference ref); - - /** - * Clear the entire cache - */ - public void clear(); - - /** - * Get a cache entry - * - * @param ref entry to retrieve - * @param originalBytes Pre-weaving class bytes - required in order to - * ensure that the cached entry refers to the same original class - * @return the cached bytes or null, if the entry does not exist - */ - public CachedClassEntry get(CachedClassReference ref, byte[] originalBytes); - - /** - * Put an entry in the cache - * - * @param entry key of the entry - * @param originalBytes Pre-weaving class bytes - required in order to - * ensure that the cached entry refers to the same original class - */ - public void put(CachedClassEntry entry, byte[] originalBytes); -} diff --git a/weaver/src/org/aspectj/weaver/tools/cache/CacheFactory.java b/weaver/src/org/aspectj/weaver/tools/cache/CacheFactory.java deleted file mode 100644 index 042ef61bd..000000000 --- a/weaver/src/org/aspectj/weaver/tools/cache/CacheFactory.java +++ /dev/null @@ -1,25 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * John Kew (vmware) initial implementation - *******************************************************************************/ -package org.aspectj.weaver.tools.cache; - -/** - * Facility for overriding the default CacheKeyResolver - * and CacheBacking; an implementing factory must be set - * on the {@link WeavedClassCache} before the - * {@link org.aspectj.weaver.tools.WeavingAdaptor} is - * configured. - */ -public interface CacheFactory { - CacheKeyResolver createResolver(); - - CacheBacking createBacking(String scope); -} diff --git a/weaver/src/org/aspectj/weaver/tools/cache/CacheKeyResolver.java b/weaver/src/org/aspectj/weaver/tools/cache/CacheKeyResolver.java deleted file mode 100644 index 8c76ad878..000000000 --- a/weaver/src/org/aspectj/weaver/tools/cache/CacheKeyResolver.java +++ /dev/null @@ -1,88 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * John Kew (vmware) initial implementation - *******************************************************************************/ - -package org.aspectj.weaver.tools.cache; - -import java.util.List; - -/** - * Interface to allow alternate hashing schemes for weaved and - * generated classes. While the DefaultCacheKeyResolver may be - * a reasonable naive implementation, the management and invalidation - * of the cache may be more usefully accomplished at the Application - * or Container level. - *

- * The key is not a one-way hash; it must be convertible back to a - * className and must match the regex for the type of key it is - * (generated or weaved). - */ -public interface CacheKeyResolver { - /** - * Create a key for the given className from a class generated by - * the weaver such that: - *

-	 *    className == keyToClass(generatedKey(className)) holds
-	 * and
-	 *    generatedKey(className).matches(getGeneratedRegex()) == true
-	 * 
- * - * @param className class to create a key for - * @return key for the class, or null if no caching should be performed - */ - CachedClassReference generatedKey(String className); - - /** - * Create a key for the given class name and byte array from the pre-weaved - * class such that - *
-	 *    className == keyToClass(weavedKey(className, various_bytes)) holds
-	 * and
-	 *    weavedKey(className, various_bytes).matches(getWeavedRegex()) == true
-	 * 
- * - * @param className class to create a key for - * @param original_bytes bytes of the pre-weaved class - * @return key for the class, or null if no caching should be performed - */ - CachedClassReference weavedKey(String className, byte[] original_bytes); - - /** - * Convert a key back to a className - * - * @param key cache key - * @return className - */ - String keyToClass(String key); - - /** - * Create a unique string for the given classpath and aspect list - * - * @param loader Classloader for this adapter - * @param aspects list of aspects; either urls or class names handled by this adapter - * @return scope, or null, if no caching should be performed for this classloader - */ - String createClassLoaderScope(ClassLoader loader, List aspects); - - /** - * Return a regex which matches all generated keys - * - * @return string regex - */ - String getGeneratedRegex(); - - /** - * Return a regex which matches all weaved keys; - * - * @return string regex - */ - String getWeavedRegex(); -} diff --git a/weaver/src/org/aspectj/weaver/tools/cache/CacheStatistics.java b/weaver/src/org/aspectj/weaver/tools/cache/CacheStatistics.java deleted file mode 100644 index 1ccacfc18..000000000 --- a/weaver/src/org/aspectj/weaver/tools/cache/CacheStatistics.java +++ /dev/null @@ -1,107 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * John Kew (vmware) initial implementation - *******************************************************************************/ - -package org.aspectj.weaver.tools.cache; - -/** - * Maintains some basic statistics on the class cache. - */ -public class CacheStatistics { - private volatile int hits; - private volatile int misses; - private volatile int weaved; - private volatile int generated; - private volatile int ignored; - private volatile int puts; - private volatile int puts_ignored; - - public void hit() { - hits++; - } - - public void miss() { - misses++; - } - - public void weaved() { - weaved++; - } - - public void generated() { - generated++; - } - - public void ignored() { - ignored++; - } - - public void put() { - puts++; - } - - public void putIgnored() { - puts_ignored++; - } - - - public int getHits() { - return hits; - } - - public int getMisses() { - return misses; - } - - public int getWeaved() { - return weaved; - } - - public int getGenerated() { - return generated; - } - - public int getIgnored() { - return ignored; - } - - public int getPuts() { - return puts; - } - - public int getPutsIgnored() { - return puts_ignored; - } - - - public void reset() { - hits = 0; - misses = 0; - weaved = 0; - generated = 0; - ignored = 0; - puts = 0; - puts_ignored = 0; - } - - @Override - public String toString() { - return "CacheStatistics{" + - "hits=" + hits + - ", misses=" + misses + - ", weaved=" + weaved + - ", generated=" + generated + - ", ignored=" + ignored + - ", puts=" + puts + - ", puts_ignored=" + puts_ignored + - '}'; - } -} diff --git a/weaver/src/org/aspectj/weaver/tools/cache/CachedClassEntry.java b/weaver/src/org/aspectj/weaver/tools/cache/CachedClassEntry.java deleted file mode 100644 index db74e7f92..000000000 --- a/weaver/src/org/aspectj/weaver/tools/cache/CachedClassEntry.java +++ /dev/null @@ -1,90 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * John Kew (vmware) initial implementation - * Lyor Goldstein (vmware) add support for weaved class being re-defined - *******************************************************************************/ -package org.aspectj.weaver.tools.cache; - -/** - * Represents a class which has been cached - */ -public class CachedClassEntry { - static enum EntryType { - GENERATED, - WEAVED, - IGNORED, - } - - private final CachedClassReference ref; - private final byte[] weavedBytes; - private final EntryType type; - - public CachedClassEntry(CachedClassReference ref, byte[] weavedBytes, EntryType type) { - this.weavedBytes = weavedBytes; - this.ref = ref; - this.type = type; - } - - public String getClassName() { - return ref.getClassName(); - } - - public byte[] getBytes() { - return weavedBytes; - } - - public String getKey() { - return ref.getKey(); - } - - public boolean isGenerated() { - return type == EntryType.GENERATED; - } - - public boolean isWeaved() { - return type == EntryType.WEAVED; - } - - public boolean isIgnored() { - return type == EntryType.IGNORED; - } - - @Override - public int hashCode() { - return getClassName().hashCode() - + getKey().hashCode() - + type.hashCode() - ; - } - - @Override - public boolean equals(Object obj) { - if (obj == null) - return false; - if (this == obj) - return true; - if (getClass() != obj.getClass()) - return false; - - CachedClassEntry other=(CachedClassEntry) obj; - if (getClassName().equals(other.getClassName()) - && getKey().equals(other.getKey()) - && (type == other.type)) { - return true; - } else { - return false; - } - } - - @Override - public String toString() { - return getClassName() + "[" + type + "]"; - } -} diff --git a/weaver/src/org/aspectj/weaver/tools/cache/CachedClassReference.java b/weaver/src/org/aspectj/weaver/tools/cache/CachedClassReference.java deleted file mode 100644 index 7d48ff171..000000000 --- a/weaver/src/org/aspectj/weaver/tools/cache/CachedClassReference.java +++ /dev/null @@ -1,85 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * John Kew (vmware) initial implementation - * Lyor Goldstein (vmware) add support for weaved class being re-defined - *******************************************************************************/ - -package org.aspectj.weaver.tools.cache; - -/** - * A typed reference to a cached class entry. The key to any - * cache entry is a simple string, but that string may contain - * some specialized encoding. This class handles all of that - * encoding. - *

- * External users of the cache should not be able to create these - * objects manually. - */ -public class CachedClassReference { - static enum EntryType { - GENERATED, - WEAVED, - IGNORED, - } - - private final String key; - private final String className; - - protected CachedClassReference(String key, CacheKeyResolver resolver) { - this(key, resolver.keyToClass(key)); - } - - /** - * Protected to allow only the WeavedClassCache initialization rights - * - * @param key encoded key of the class - * @param className the classname - */ - protected CachedClassReference(String key, String className) { - this.key = key; - this.className = className; - } - - public String getKey() { - return key; - } - - public String getClassName() { - return className; - } - - @Override - public int hashCode() { - return getKey().hashCode() + getClassName().hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (obj == null) - return false; - if (this == obj) - return true; - if (getClass() != obj.getClass()) - return false; - - CachedClassReference other=(CachedClassReference) obj; - if (getKey().equals(other.getKey()) - && getClassName().equals(other.getClassName())) { - return true; - } else { - return false; - } - } - - @Override - public String toString() { - return getClassName() + "[" + getKey() + "]"; - } -} diff --git a/weaver/src/org/aspectj/weaver/tools/cache/DefaultCacheFactory.java b/weaver/src/org/aspectj/weaver/tools/cache/DefaultCacheFactory.java deleted file mode 100644 index 7eb796e4b..000000000 --- a/weaver/src/org/aspectj/weaver/tools/cache/DefaultCacheFactory.java +++ /dev/null @@ -1,26 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * John Kew (vmware) initial implementation - *******************************************************************************/ - -package org.aspectj.weaver.tools.cache; - -/** - * Default factory for creating the backing and resolving classes. - */ -public class DefaultCacheFactory implements CacheFactory { - public CacheKeyResolver createResolver() { - return new DefaultCacheKeyResolver(); - } - - public CacheBacking createBacking(String scope) { - return DefaultFileCacheBacking.createBacking(scope); - } -} diff --git a/weaver/src/org/aspectj/weaver/tools/cache/DefaultCacheKeyResolver.java b/weaver/src/org/aspectj/weaver/tools/cache/DefaultCacheKeyResolver.java deleted file mode 100644 index 4951923d3..000000000 --- a/weaver/src/org/aspectj/weaver/tools/cache/DefaultCacheKeyResolver.java +++ /dev/null @@ -1,117 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * John Kew (vmware) initial implementation - *******************************************************************************/ - -package org.aspectj.weaver.tools.cache; - -import java.net.URL; -import java.net.URLClassLoader; -import java.util.Collections; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.zip.CRC32; - -/** - * Naive default class and classloader hashing implementation useful - * for some multi-classloader environments. - *

- * This implementation creates classloader scopes of the form:
- * "ExampleClassLoaderName.[crc hash]" - *

- * And weaved class keys of the form:
- * "com.foo.BarClassName.[bytes len][crc].weaved" - *

- * And generated class keys of the form:
- * "com.foo.BarClassName$AjClosure.generated - */ -public class DefaultCacheKeyResolver implements CacheKeyResolver { - public static final String GENERATED_SUFFIX = ".generated"; - public static final String WEAVED_SUFFIX = ".weaved"; - - /** - * Create a scope from a set of urls and aspect urls. Creates scope - * of the form "ExampleClassLoaderName.[md5sum]" or - * "ExampleClassLoaderName.[crc]" - * - * @param cl the classloader which uses the cache, can be null - * @param aspects the aspects - * @return a unique string for URLClassloaders, otherwise a non-unique classname - */ - public String createClassLoaderScope(ClassLoader cl, List aspects) { - String name = cl != null ? cl.getClass().getSimpleName() : "unknown"; - - List hashableStrings = new LinkedList(); - StringBuilder hashable = new StringBuilder(256); - - // Add the list of loader urls to the hash list - if (cl != null && cl instanceof URLClassLoader) { - URL[] urls = ((URLClassLoader) cl).getURLs(); - for (int i = 0; i < urls.length; i++) { - hashableStrings.add(urls[i].toString()); - } - } - - hashableStrings.addAll(aspects); - Collections.sort(hashableStrings); - for (Iterator it = hashableStrings.iterator(); it.hasNext(); ) { - String url = it.next(); - hashable.append(url); - } - String hash = null; - byte[] bytes = hashable.toString().getBytes(); - hash = crc(bytes); - - return name + '.' + hash; - } - - private String crc(byte[] input) { - CRC32 crc32 = new CRC32(); - crc32.update(input); - return String.valueOf(crc32.getValue()); - } - - public String getGeneratedRegex() { - return ".*" + GENERATED_SUFFIX; - } - - public String getWeavedRegex() { - return ".*" + WEAVED_SUFFIX; - } - - - /** - * Converts a cache key back to a className - * - * @param key to convert - * @return className, e.g. "com.foo.Bar" - */ - public String keyToClass(String key) { - if (key.endsWith(GENERATED_SUFFIX)) { - return key.replaceAll(GENERATED_SUFFIX + "$", ""); - } - if (key.endsWith(WEAVED_SUFFIX)) { - return key.replaceAll("\\.[^.]+" + WEAVED_SUFFIX, ""); - } - return key; - } - - public CachedClassReference weavedKey(String className, byte[] original_bytes) { - String hash = crc(original_bytes); - return new CachedClassReference(className + "." + hash + WEAVED_SUFFIX, className); - - } - - public CachedClassReference generatedKey(String className) { - return new CachedClassReference(className + GENERATED_SUFFIX, className); - } - -} diff --git a/weaver/src/org/aspectj/weaver/tools/cache/DefaultFileCacheBacking.java b/weaver/src/org/aspectj/weaver/tools/cache/DefaultFileCacheBacking.java deleted file mode 100644 index 21543a80d..000000000 --- a/weaver/src/org/aspectj/weaver/tools/cache/DefaultFileCacheBacking.java +++ /dev/null @@ -1,289 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * John Kew (vmware) initial implementation - * Lyor Goldstein (vmware) add support for weaved class being re-defined - *******************************************************************************/ - -package org.aspectj.weaver.tools.cache; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FilenameFilter; -import java.io.OutputStream; -import java.util.Map; - -import org.aspectj.bridge.MessageUtil; -import org.aspectj.util.FileUtil; -import org.aspectj.util.LangUtil; - - -/** - * Naive File-Backed Class Cache with no expiry or application - * centric invalidation. - *

- * Enabled with the system property, "aj.weaving.cache.dir" - * If this system property is not set, no caching will be - * performed. - *

- * A CRC checksum is stored alongside the class file to verify - * the bytes on read. If for some reason there is an error - * reading either the class or crc file, or if the crc does not - * match the class data the cache entry is deleted. - *

- * An alternate implementation of this could store the class file - * as a jar/zip directly, which would have the required crc; as - * a first pass however it is somewhat useful to view these files - * in expanded form for debugging. - */ -public class DefaultFileCacheBacking extends AbstractIndexedFileCacheBacking { - private final Map index; - - private static final Object LOCK = new Object(); - - protected DefaultFileCacheBacking(File cacheDir) { - super(cacheDir); - index = readIndex(); - } - - public static final DefaultFileCacheBacking createBacking(File cacheDir) { - if (!cacheDir.exists()) { - if (!cacheDir.mkdirs()) { - MessageUtil.error("Unable to create cache directory at " + cacheDir.getName()); - return null; - } - } else if (!cacheDir.isDirectory()) { - MessageUtil.error("Not a cache directory at " + cacheDir.getName()); - return null; - } - - if (!cacheDir.canWrite()) { - MessageUtil.error("Cache directory is not writable at " + cacheDir.getName()); - return null; - } - return new DefaultFileCacheBacking(cacheDir); - } - - @Override - protected Map getIndex() { - return index; - } - - @Override - protected IndexEntry resolveIndexMapEntry (File cacheDir, IndexEntry ie) { - File cacheEntry = new File(cacheDir, ie.key); - if (ie.ignored || cacheEntry.canRead()) { - return ie; - } else { - return null; - } - } - - private void removeIndexEntry(String key) { - synchronized (LOCK) { - index.remove(key); - writeIndex(); - } - } - - private void addIndexEntry(IndexEntry ie) { - synchronized (LOCK) { - index.put(ie.key, ie); - writeIndex(); - } - } - - @Override - protected Map readIndex() { - synchronized (LOCK) { - return super.readIndex(); - } - } - - @Override - protected void writeIndex() { - synchronized (LOCK) { - super.writeIndex(); - } - } - - public void clear() { - File cacheDir=getCacheDirectory(); - int numDeleted=0; - synchronized (LOCK) { - numDeleted = FileUtil.deleteContents(cacheDir); - } - - if ((numDeleted > 0) && (logger != null) && logger.isTraceEnabled()) { - logger.info("clear(" + cacheDir + ") deleted"); - } - } - - public static CacheBacking createBacking(String scope) { - String cache = System.getProperty(WEAVED_CLASS_CACHE_DIR); - if (cache == null) { - return null; - } - - File cacheDir = new File(cache, scope); - return createBacking(cacheDir); - } - - @Override - public String[] getKeys(final String regex) { - File cacheDirectory = getCacheDirectory(); - File[] files = cacheDirectory.listFiles(new FilenameFilter() { - public boolean accept(File file, String s) { - if (s.matches(regex)) { - return true; - } - return false; - } - }); - if (LangUtil.isEmpty(files)) { - return EMPTY_KEYS; - } - String[] keys = new String[files.length]; - for (int i = 0; i < files.length; i++) { - keys[i] = files[i].getName(); - } - return keys; - } - - public CachedClassEntry get(CachedClassReference ref, byte[] originalBytes) { - File cacheDirectory = getCacheDirectory(); - String refKey=ref.getKey(); - File cacheFile = new File(cacheDirectory, refKey); - IndexEntry ie = index.get(refKey); - if (ie == null) { - // no index, delete - delete(cacheFile); - return null; - } - - // check if original file changed - if (crc(originalBytes) != ie.crcClass) { - delete(cacheFile); - return null; - } - - if (ie.ignored) { - return new CachedClassEntry(ref, WeavedClassCache.ZERO_BYTES, CachedClassEntry.EntryType.IGNORED); - } - - if (cacheFile.canRead()) { - byte[] bytes = read(cacheFile, ie.crcWeaved); - if (bytes != null) { - if (!ie.generated) { - return new CachedClassEntry(ref, bytes, CachedClassEntry.EntryType.WEAVED); - } else { - return new CachedClassEntry(ref, bytes, CachedClassEntry.EntryType.GENERATED); - } - } - } - - return null; - } - - public void put(CachedClassEntry entry, byte[] originalBytes) { - File cacheDirectory = getCacheDirectory(); - String refKey = entry.getKey(); - IndexEntry ie = index.get(refKey); - File cacheFile = new File(cacheDirectory, refKey); - final boolean writeEntryBytes; - // check if original bytes changed or the ignored/generated flags - if ((ie != null) - && ((ie.ignored != entry.isIgnored()) || (ie.generated != entry.isGenerated()) || (crc(originalBytes) != ie.crcClass))) { - delete(cacheFile); - writeEntryBytes = true; - } else { - writeEntryBytes = !cacheFile.exists(); - } - - if (writeEntryBytes) { - ie = createIndexEntry(entry, originalBytes); - if (!entry.isIgnored()) { - ie.crcWeaved = write(cacheFile, entry.getBytes()); - } - addIndexEntry(ie); - } - } - - public void remove(CachedClassReference ref) { - File cacheDirectory = getCacheDirectory(); - String refKey = ref.getKey(); - File cacheFile = new File(cacheDirectory, refKey); - synchronized (LOCK) { - removeIndexEntry(refKey); - delete(cacheFile); - } - } - - @Override - protected void delete(File file) { - synchronized (LOCK) { - super.delete(file); - } - } - - protected byte[] read(File file, long expectedCRC) { - byte[] bytes=null; - synchronized (LOCK) { - FileInputStream fis = null; - try { - fis = new FileInputStream(file); - bytes = FileUtil.readAsByteArray(fis); - } catch (Exception e) { - if ((logger != null) && logger.isTraceEnabled()) { - logger.warn("read(" + file.getAbsolutePath() + ")" - + " failed (" + e.getClass().getSimpleName() + ")" - + " to read contents: " + e.getMessage(), e); - } - } finally { - close(fis, file); - } - - // delete the file if there was an exception reading it or mismatched crc - if ((bytes == null) || (crc(bytes) != expectedCRC)) { - delete(file); - return null; - } - } - - return bytes; - } - - protected long write(File file, byte[] bytes) { - synchronized (LOCK) { - if (file.exists()) { - return -1L; - } - OutputStream out = null; - try { - out = new FileOutputStream(file); - out.write(bytes); - } catch (Exception e) { - if ((logger != null) && logger.isTraceEnabled()) { - logger.warn("write(" + file.getAbsolutePath() + ")" - + " failed (" + e.getClass().getSimpleName() + ")" - + " to write contents: " + e.getMessage(), e); - } - // delete the file if there was an exception writing it - delete(file); - return -1L; - } finally { - close(out, file); - } - - return crc(bytes); - } - } - -} diff --git a/weaver/src/org/aspectj/weaver/tools/cache/FlatFileCacheBacking.java b/weaver/src/org/aspectj/weaver/tools/cache/FlatFileCacheBacking.java deleted file mode 100644 index 6de416a80..000000000 --- a/weaver/src/org/aspectj/weaver/tools/cache/FlatFileCacheBacking.java +++ /dev/null @@ -1,139 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2012 VMware, Inc. custard - * - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Lyor Goldstein - * ******************************************************************/ - -package org.aspectj.weaver.tools.cache; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.StreamCorruptedException; -import java.util.Map; -import java.util.TreeMap; - -import org.aspectj.util.FileUtil; -import org.aspectj.util.LangUtil; - -/** - * Uses a "flat" files model to store the cached instrumented classes - * and aspects - i.e., each class/aspect is stored as a separate (binary) - * file. This is a good mechanism when the number of instrumented class is - * relatively small (a few 10's). The reason for it is that scanning a folder - * that has many files in it quickly becomes an I/O bottleneck. Also, some - * O/S-es may impose internal limits on the maximum number of "children" - * a folder node may have. On the other hand, it is much faster (again, for - * small number of instrumented classes) than the ZIP cache since each class/aspect - * is represented by a single file - thus adding/removing/modifying it is easier. - * - * @author Lyor Goldstein - */ -public class FlatFileCacheBacking extends AsynchronousFileCacheBacking { - private static final AsynchronousFileCacheBackingCreator defaultCreator= - new AsynchronousFileCacheBackingCreator() { - public FlatFileCacheBacking create(File cacheDir) { - return new FlatFileCacheBacking(cacheDir); - } - }; - public FlatFileCacheBacking(File cacheDir) { - super(cacheDir); - } - - public static final FlatFileCacheBacking createBacking (File cacheDir) { - return createBacking(cacheDir, defaultCreator); - } - - @Override - protected Map readClassBytes(Map indexMap, File cacheDir) { - return readClassBytes(indexMap, cacheDir.listFiles()); - } - - protected Map readClassBytes (Map indexMap, File[] files) { - Map result=new TreeMap(); - if (LangUtil.isEmpty(files)) { - return result; - } - - for (File file : files) { - if (!file.isFile()) { - continue; // skip sub-directories - we expect flat files - } - - String key=file.getName(); - if (INDEX_FILE.equalsIgnoreCase(key)) { - continue; // skip the index itself if found - } - - IndexEntry entry=indexMap.get(key); - if ((entry == null) || entry.ignored) { // if not in index or ignored then remove it - if ((logger != null) && logger.isTraceEnabled()) { - logger.info("readClassBytes(" + key + ") remove orphan/ignored: " + file.getAbsolutePath()); - } - FileUtil.deleteContents(file); - continue; - } - - try { - byte[] bytes=FileUtil.readAsByteArray(file); - long crc=crc(bytes); - if (crc != entry.crcWeaved) { - throw new StreamCorruptedException("Mismatched CRC - expected=" + entry.crcWeaved + "/got=" + crc); - } - - result.put(key, bytes); - if ((logger != null) && logger.isTraceEnabled()) { - logger.debug("readClassBytes(" + key + ") cached from " + file.getAbsolutePath()); - } - } catch(IOException e) { - if ((logger != null) && logger.isTraceEnabled()) { - logger.error("Failed (" + e.getClass().getSimpleName() + ")" - + " to read bytes from " + file.getAbsolutePath() - + ": " + e.getMessage()); - } - indexMap.remove(key); // no need for the entry if no file - force a re-write of its bytes - FileUtil.deleteContents(file); // assume some kind of corruption - continue; - } - } - - return result; - } - - @Override - protected IndexEntry resolveIndexMapEntry (File cacheDir, IndexEntry ie) { - File cacheEntry = new File(cacheDir, ie.key); - if (ie.ignored || cacheEntry.canRead()) { - return ie; - } else { - return null; - } - } - - @Override - protected void writeClassBytes (String key, byte[] bytes) throws Exception { - File dir=getCacheDirectory(), file=new File(dir, key); - FileOutputStream out=new FileOutputStream(file); - try { - out.write(bytes); - } finally { - out.close(); - } - } - - @Override - protected void removeClassBytes (String key) throws Exception { - File dir=getCacheDirectory(), file=new File(dir, key); - if (file.exists() && (!file.delete())) { - throw new StreamCorruptedException("Failed to delete " + file.getAbsolutePath()); - } - } - -} diff --git a/weaver/src/org/aspectj/weaver/tools/cache/GeneratedCachedClassHandler.java b/weaver/src/org/aspectj/weaver/tools/cache/GeneratedCachedClassHandler.java deleted file mode 100644 index 91c332b6b..000000000 --- a/weaver/src/org/aspectj/weaver/tools/cache/GeneratedCachedClassHandler.java +++ /dev/null @@ -1,39 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * John Kew (vmware) initial implementation - * Lyor Goldstein (vmware) add support for weaved class being re-defined - *******************************************************************************/ - -package org.aspectj.weaver.tools.cache; - -import org.aspectj.weaver.tools.GeneratedClassHandler; - -/** - * Handler for generated classes; such as Shadowed closures, etc. This wraps the normal - * generated class handler with caching - */ -public class GeneratedCachedClassHandler implements GeneratedClassHandler { - private final WeavedClassCache cache; - private final GeneratedClassHandler nextGeneratedClassHandler; - - public GeneratedCachedClassHandler(WeavedClassCache cache, GeneratedClassHandler nextHandler) { - this.cache = cache; - this.nextGeneratedClassHandler = nextHandler; - } - - public void acceptClass (String name, byte[] originalBytes, byte[] wovenBytes) { - // The cache expects classNames in dot form - CachedClassReference ref = cache.createGeneratedCacheKey(name.replace('/', '.')); - cache.put(ref, originalBytes, wovenBytes); - if (nextGeneratedClassHandler != null) { - nextGeneratedClassHandler.acceptClass(name, originalBytes, wovenBytes); - } - } -} diff --git a/weaver/src/org/aspectj/weaver/tools/cache/SimpleCache.java b/weaver/src/org/aspectj/weaver/tools/cache/SimpleCache.java deleted file mode 100644 index 45d718a14..000000000 --- a/weaver/src/org/aspectj/weaver/tools/cache/SimpleCache.java +++ /dev/null @@ -1,377 +0,0 @@ -package org.aspectj.weaver.tools.cache; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.security.ProtectionDomain; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.zip.CRC32; - -import org.aspectj.weaver.Dump; -import org.aspectj.weaver.tools.Trace; -import org.aspectj.weaver.tools.TraceFactory; - - -/******************************************************************************* - * Copyright (c) 2012 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * Abraham Nevado (lucierna) initial implementation - ********************************************************************************/ - -public class SimpleCache { - - private static final String SAME_BYTES_STRING = "IDEM"; - private static final byte[] SAME_BYTES = SAME_BYTES_STRING.getBytes(); - - private Map cacheMap; - private boolean enabled = false; - - // cache for generated classes - private Map generatedCache; - private static final String GENERATED_CACHE_SUBFOLDER = "panenka.cache"; - private static final String GENERATED_CACHE_SEPARATOR = ";"; - - public static final String IMPL_NAME = "shared"; - - protected SimpleCache(String folder, boolean enabled) { - this.enabled = enabled; - - cacheMap = Collections.synchronizedMap(StoreableCachingMap.init(folder)); - - if (enabled) { - String generatedCachePath = folder + File.separator + GENERATED_CACHE_SUBFOLDER; - File f = new File (generatedCachePath); - if (!f.exists()){ - f.mkdir(); - } - generatedCache = Collections.synchronizedMap(StoreableCachingMap.init(generatedCachePath,0)); - } - } - - public byte[] getAndInitialize(String classname, byte[] bytes, - ClassLoader loader, ProtectionDomain protectionDomain) { - if (!enabled) { - return null; - } - byte[] res = get(classname, bytes); - - if (Arrays.equals(SAME_BYTES, res)) { - return bytes; - } else { - if (res != null) { - initializeClass(classname, res, loader, protectionDomain); - } - return res; - } - - } - - private byte[] get(String classname, byte bytes[]) { - String key = generateKey(classname, bytes); - - byte[] res = cacheMap.get(key); - return res; - } - - public void put(String classname, byte[] origbytes, byte[] wovenbytes) { - if (!enabled) { - return; - } - - String key = generateKey(classname, origbytes); - - if (Arrays.equals(origbytes, wovenbytes)) { - cacheMap.put(key, SAME_BYTES); - return; - } - cacheMap.put(key, wovenbytes); - } - - private String generateKey(String classname, byte[] bytes) { - CRC32 checksum = new CRC32(); - checksum.update(bytes); - long crc = checksum.getValue(); - classname = classname.replace("/", "."); - return new String(classname + "-" + crc); - - } - - private static class StoreableCachingMap extends HashMap { - private String folder; - private static final String CACHENAMEIDX = "cache.idx"; - - private long lastStored = System.currentTimeMillis(); - private static int DEF_STORING_TIMER = 60000; //ms - private int storingTimer; - - private transient Trace trace; - private void initTrace(){ - trace = TraceFactory.getTraceFactory().getTrace(StoreableCachingMap.class); - } - -// private StoreableCachingMap(String folder) { -// this.folder = folder; -// initTrace(); -// } - - private StoreableCachingMap(String folder, int storingTimer){ - this.folder = folder; - initTrace(); - this.storingTimer = storingTimer; - } - - public static StoreableCachingMap init(String folder) { - return init(folder,DEF_STORING_TIMER); - - } - - public static StoreableCachingMap init(String folder, int storingTimer) { - File file = new File(folder + File.separator + CACHENAMEIDX); - if (file.exists()) { - try { - ObjectInputStream in = new ObjectInputStream( - new FileInputStream(file)); - // Deserialize the object - StoreableCachingMap sm = (StoreableCachingMap) in.readObject(); - sm.initTrace(); - in.close(); - return sm; - } catch (Exception e) { - Trace trace = TraceFactory.getTraceFactory().getTrace(StoreableCachingMap.class); - trace.error("Error reading Storable Cache", e); - } - } - - return new StoreableCachingMap(folder,storingTimer); - - } - - @Override - public Object get(Object obj) { - try { - if (super.containsKey(obj)) { - String path = (String) super.get(obj); - if (path.equals(SAME_BYTES_STRING)) { - return SAME_BYTES; - } - return readFromPath(path); - } else { - return null; - } - } catch (IOException e) { - trace.error("Error reading key:"+obj.toString(),e); - Dump.dumpWithException(e); - } - return null; - } - - @Override - public Object put(Object key, Object value) { - try { - String path = null; - byte[] valueBytes = (byte[]) value; - - if (Arrays.equals(valueBytes, SAME_BYTES)) { - path = SAME_BYTES_STRING; - } else { - path = writeToPath((String) key, valueBytes); - } - Object result = super.put(key, path); - storeMap(); - return result; - } catch (IOException e) { - trace.error("Error inserting in cache: key:"+key.toString() + "; value:"+value.toString(), e); - Dump.dumpWithException(e); - } - return null; - } - - - - public void storeMap() { - long now = System.currentTimeMillis(); - if ((now - lastStored ) < storingTimer){ - return; - } - File file = new File(folder + File.separator + CACHENAMEIDX);; - try { - ObjectOutputStream out = new ObjectOutputStream( - new FileOutputStream(file)); - // Deserialize the object - out.writeObject(this); - out.close(); - lastStored = now; - } catch (Exception e) { - trace.error("Error storing cache; cache file:"+file.getAbsolutePath(), e); - Dump.dumpWithException(e); - } - } - - private byte[] readFromPath(String fullPath) throws IOException { - FileInputStream is = null ; - try{ - is = new FileInputStream(fullPath); - } - catch (FileNotFoundException e){ - //may be caused by a generated class that has been stored in generated cache but not saved at cache folder - System.out.println("FileNotFoundExceptions: The aspectj cache is corrupt. Please clean it and reboot the server. Cache path:"+this.folder ); - e.printStackTrace(); - return null; - } - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - - int nRead; - byte[] data = new byte[16384]; - - while ((nRead = is.read(data, 0, data.length)) != -1) { - buffer.write(data, 0, nRead); - } - - buffer.flush(); - is.close(); - return buffer.toByteArray(); - - } - - private String writeToPath(String key, byte[] bytes) throws IOException { - String fullPath = folder + File.separator + key; - FileOutputStream fos = new FileOutputStream(fullPath); - fos.write(bytes); - fos.flush(); - fos.close(); - return fullPath; - } - - } - - private void initializeClass(String className, byte[] bytes, - ClassLoader loader, ProtectionDomain protectionDomain) { - String[] generatedClassesNames = getGeneratedClassesNames(className,bytes); - - if (generatedClassesNames == null) { - return; - } - for (String generatedClassName : generatedClassesNames) { - - byte[] generatedBytes = get(generatedClassName, bytes); - - if (protectionDomain == null) { - defineClass(loader, generatedClassName, generatedBytes); - } else { - defineClass(loader, generatedClassName, generatedBytes, - protectionDomain); - } - - } - - } - - private String[] getGeneratedClassesNames(String className, byte[] bytes) { - String key = generateKey(className, bytes); - - byte[] readBytes = generatedCache.get(key); - if (readBytes == null) { - return null; - } - String readString = new String(readBytes); - return readString.split(GENERATED_CACHE_SEPARATOR); - } - - public void addGeneratedClassesNames(String parentClassName, byte[] parentBytes, String generatedClassName) { - if (!enabled) { - return; - } - String key = generateKey(parentClassName, parentBytes); - - byte[] storedBytes = generatedCache.get(key); - if (storedBytes == null) { - generatedCache.put(key, generatedClassName.getBytes()); - } else { - String storedClasses = new String(storedBytes); - storedClasses += GENERATED_CACHE_SEPARATOR + generatedClassName; - generatedCache.put(key, storedClasses.getBytes()); - } - } - - private Method defineClassMethod = null; - private Method defineClassWithProtectionDomainMethod = null; - - private void defineClass(ClassLoader loader, String name, byte[] bytes) { - - Object clazz = null; - - try { - if (defineClassMethod == null) { - defineClassMethod = ClassLoader.class.getDeclaredMethod( - "defineClass", new Class[] { String.class, - bytes.getClass(), int.class, int.class }); - } - defineClassMethod.setAccessible(true); - clazz = defineClassMethod.invoke(loader, new Object[] { name, - bytes, new Integer(0), new Integer(bytes.length) }); - } catch (InvocationTargetException e) { - if (e.getTargetException() instanceof LinkageError) { - e.printStackTrace(); - } else { - System.out.println("define generated class failed" - + e.getTargetException()); - } - } catch (Exception e) { - e.printStackTrace(); - Dump.dumpWithException(e); - } - } - - private void defineClass(ClassLoader loader, String name, byte[] bytes, - ProtectionDomain protectionDomain) { - - Object clazz = null; - - try { - // System.out.println(">> Defining with protection domain " + name + - // " pd=" + protectionDomain); - if (defineClassWithProtectionDomainMethod == null) { - defineClassWithProtectionDomainMethod = ClassLoader.class - .getDeclaredMethod("defineClass", new Class[] { - String.class, bytes.getClass(), int.class, - int.class, ProtectionDomain.class }); - } - defineClassWithProtectionDomainMethod.setAccessible(true); - clazz = defineClassWithProtectionDomainMethod.invoke(loader, - new Object[] { name, bytes, Integer.valueOf(0), - new Integer(bytes.length), protectionDomain }); - } catch (InvocationTargetException e) { - if (e.getTargetException() instanceof LinkageError) { - e.printStackTrace(); - // is already defined (happens for X$ajcMightHaveAspect - // interfaces since aspects are reweaved) - // TODO maw I don't think this is OK and - } else { - e.printStackTrace(); - } - }catch (NullPointerException e) { - System.out.println("NullPointerException loading class:"+name+". Probabily caused by a corruput cache. Please clean it and reboot the server"); - } catch (Exception e) { - e.printStackTrace(); - Dump.dumpWithException(e); - } - - } - -} diff --git a/weaver/src/org/aspectj/weaver/tools/cache/SimpleCacheFactory.java b/weaver/src/org/aspectj/weaver/tools/cache/SimpleCacheFactory.java deleted file mode 100644 index 49569dd0e..000000000 --- a/weaver/src/org/aspectj/weaver/tools/cache/SimpleCacheFactory.java +++ /dev/null @@ -1,104 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * Abraham Nevado (lucierna) initial implementation - ********************************************************************************/ - -package org.aspectj.weaver.tools.cache; - -import java.io.File; - -import org.aspectj.weaver.Dump; - -public class SimpleCacheFactory { - - public static final String CACHE_ENABLED_PROPERTY = "aj.weaving.cache.enabled"; - public static final String CACHE_DIR = "aj.weaving.cache.dir"; - public static final String CACHE_IMPL = "aj.weaving.cache.impl"; - - public static final String PATH_DEFAULT= "/tmp/"; // TODO windows default...? - public static final boolean BYDEFAULT= false; - - - public static String path = PATH_DEFAULT; - public static Boolean enabled = false; - private static boolean determinedIfEnabled = false; - private static SimpleCache lacache=null; - - public static synchronized SimpleCache createSimpleCache(){ - if (lacache==null){ - if (!determinedIfEnabled) { - determineIfEnabled(); - } - - if (!enabled) { - return null; - } - - try { - path = System.getProperty(CACHE_DIR); - if (path == null){ - path = PATH_DEFAULT; - } - - } catch (Throwable t) { - path=PATH_DEFAULT; - t.printStackTrace(); - Dump.dumpWithException(t); - } - File f = new File(path); - if (!f.exists()){ - f.mkdir(); - } - lacache= new SimpleCache(path, enabled); - } - return lacache; - - } - - private static void determineIfEnabled() { - try { - String property = System.getProperty(CACHE_ENABLED_PROPERTY); - if (property == null ){ - enabled = BYDEFAULT; - } - else if (property.equalsIgnoreCase("true")){ - - String impl = System.getProperty(CACHE_IMPL); - if (SimpleCache.IMPL_NAME.equals(impl)){ - enabled = true; - } - else{ - enabled = BYDEFAULT; - } - } - else{ - enabled = BYDEFAULT; - } - - } catch (Throwable t) { - enabled=BYDEFAULT; - System.err.println("Error creating cache"); - t.printStackTrace(); - Dump.dumpWithException(t); - } - determinedIfEnabled = true; - } - - // Should behave ok with two threads going through here, well whoever gets there first will set determinedIfEnabled but only after - // it has set 'enabled' to the right value. - public static boolean isEnabled() { - if (!determinedIfEnabled) { - determineIfEnabled(); - } - return enabled; - } - - -} diff --git a/weaver/src/org/aspectj/weaver/tools/cache/WeavedClassCache.java b/weaver/src/org/aspectj/weaver/tools/cache/WeavedClassCache.java deleted file mode 100644 index b281d412f..000000000 --- a/weaver/src/org/aspectj/weaver/tools/cache/WeavedClassCache.java +++ /dev/null @@ -1,278 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * John Kew (vmware) initial implementation - * Lyor Goldstein (vmware) add support for weaved class being re-defined - *******************************************************************************/ - -package org.aspectj.weaver.tools.cache; - -import org.aspectj.bridge.IMessage; -import org.aspectj.bridge.IMessageHandler; -import org.aspectj.bridge.Message; -import org.aspectj.bridge.MessageUtil; -import org.aspectj.weaver.tools.GeneratedClassHandler; - -import java.util.LinkedList; -import java.util.List; - -/** - * Manages a cache of weaved and generated classes similar to Eclipse Equinox, - * except designed to operate across multiple restarts of the JVM and with one - * cache per classloader; allowing URLClassLoaders with the same set of URI - * paths to share the same cache (with the default configuration). - *

- * To enable the default configuration two system properties must be set: - *

- *    "-Daj.weaving.cache.enabled=true"
- *    "-Daj.weaving.cache.dir=/some/directory"
- * 
- *

- * The class cache is often something that application developers or - * containers would like to manage, so there are a few interfaces for overriding the - * default behavior and performing other management functions. - *

- * {@link CacheBacking}
- * Provides an interface for implementing a custom backing store - * for the cache; The default implementation in {@link DefaultFileCacheBacking} - * provides a naive file-backed cache. An alternate implementation may ignore - * caching until signaled explicitly by the application, or only cache files - * for a specific duration. This class delegates the locking and synchronization - * requirements to the CacheBacking implementation. - *

- * {@link CacheKeyResolver}
- * Provides methods for creating keys from classes to be cached and for - * creating the "scope" of the cache itself for a given classloader and aspect - * list. The default implementation is provided by {@link DefaultCacheKeyResolver} - * but an alternate implementation may want to associate a cache with a particular - * application running underneath a container. - *

- * This naive cache does not normally invalidate *any* classes; the interfaces above - * must be used to implement more intelligent behavior. Cache invalidation - * problems may occur in at least three scenarios: - *

- *    1. New aspects are added dynamically somewhere in the classloader hierarchy; affecting
- *       other classes elsewhere.
- *    2. Use of declare parent in aspects to change the type hierarchy; if the cache
- *       has not invalidated the right classes in the type hierarchy aspectj may not
- *       be reconstruct the class incorrectly.
- *    3. Similarly to (2), the addition of fields or methods on classes which have
- *       already been weaved and cached could have inter-type conflicts.
- * 
- */ -public class WeavedClassCache { - public static final String WEAVED_CLASS_CACHE_ENABLED = "aj.weaving.cache.enabled"; - public static final String CACHE_IMPL = SimpleCacheFactory.CACHE_IMPL; - private static CacheFactory DEFAULT_FACTORY = new DefaultCacheFactory(); - public static final byte[] ZERO_BYTES = new byte[0]; - private final IMessageHandler messageHandler; - private final GeneratedCachedClassHandler cachingClassHandler; - private final CacheBacking backing; - private final CacheStatistics stats; - private final CacheKeyResolver resolver; - private final String name; - - private static final List cacheRegistry = new LinkedList(); - - protected WeavedClassCache(GeneratedClassHandler existingClassHandler, - IMessageHandler messageHandler, - String name, - CacheBacking backing, - CacheKeyResolver resolver) { - this.resolver = resolver; - this.name = name; - this.backing = backing; - this.messageHandler = messageHandler; - // wrap the existing class handler with a caching version - cachingClassHandler = new GeneratedCachedClassHandler(this, existingClassHandler); - this.stats = new CacheStatistics(); - synchronized (cacheRegistry) { - cacheRegistry.add(this); - } - } - - /** - * Creates a new cache using the resolver and backing returned by the DefaultCacheFactory. - * - * @param loader classloader for this cache - * @param aspects list of aspects used by the WeavingAdapter - * @param existingClassHandler the existing GeneratedClassHandler used by the weaver - * @param messageHandler the existing messageHandler used by the weaver - * @return - */ - public static WeavedClassCache createCache(ClassLoader loader, List aspects, GeneratedClassHandler existingClassHandler, IMessageHandler messageHandler) { - CacheKeyResolver resolver = DEFAULT_FACTORY.createResolver(); - String name = resolver.createClassLoaderScope(loader, aspects); - if (name == null) { - return null; - } - CacheBacking backing = DEFAULT_FACTORY.createBacking(name); - if (backing != null) { - return new WeavedClassCache(existingClassHandler, messageHandler, name, backing, resolver); - } - return null; - } - - public String getName() { - return name; - } - - /** - * The Cache and be extended in two ways, through a specialized CacheKeyResolver and - * a specialized CacheBacking. The default factory used to create these classes can - * be set with this method. Since each weaver will create a cache, this method must be - * called before the weaver is first initialized. - * - * @param factory - */ - public static void setDefaultCacheFactory(CacheFactory factory) { - DEFAULT_FACTORY = factory; - } - - /** - * Created a key for a generated class - * - * @param className ClassName, e.g. "com.foo.Bar" - * @return the cache key, or null if no caching should be performed - */ - public CachedClassReference createGeneratedCacheKey(String className) { - return resolver.generatedKey(className); - } - - /** - * Create a key for a normal weaved class - * - * @param className ClassName, e.g. "com.foo.Bar" - * @param originalBytes Original byte array of the class - * @return a cache key, or null if no caching should be performed - */ - public CachedClassReference createCacheKey(String className, byte[] originalBytes) { - return resolver.weavedKey(className, originalBytes); - } - - /** - * Returns a generated class handler which wraps the handler this cache was initialized - * with; this handler should be used to make sure that generated classes are added - * to the cache - */ - public GeneratedClassHandler getCachingClassHandler() { - return cachingClassHandler; - } - - /** - * Has caching been enabled through the System property, - * WEAVED_CLASS_CACHE_ENABLED - * - * @return true if caching is enabled - */ - public static boolean isEnabled() { - String enabled = System.getProperty(WEAVED_CLASS_CACHE_ENABLED); - String impl = System.getProperty(CACHE_IMPL); - return (enabled != null && (impl == null || !SimpleCache.IMPL_NAME.equalsIgnoreCase(impl) ) ); - } - - /** - * Put a class in the cache - * - * @param ref reference to the entry, as created through createCacheKey - * @param classBytes pre-weaving class bytes - * @param weavedBytes bytes to cache - */ - public void put(CachedClassReference ref, byte[] classBytes, byte[] weavedBytes) { - CachedClassEntry.EntryType type = CachedClassEntry.EntryType.WEAVED; - if (ref.getKey().matches(resolver.getGeneratedRegex())) { - type = CachedClassEntry.EntryType.GENERATED; - } - backing.put(new CachedClassEntry(ref, weavedBytes, type), classBytes); - stats.put(); - } - - /** - * Get a cache value - * - * @param ref reference to the cache entry, created through createCacheKey - * @param classBytes Pre-weaving class bytes - required to ensure that - * cached aspects refer to an unchanged original class - * @return the CacheEntry, or null if no entry exists in the cache - */ - public CachedClassEntry get(CachedClassReference ref, byte[] classBytes) { - CachedClassEntry entry = backing.get(ref, classBytes); - if (entry == null) { - stats.miss(); - } else { - stats.hit(); - if (entry.isGenerated()) stats.generated(); - if (entry.isWeaved()) stats.weaved(); - if (entry.isIgnored()) stats.ignored(); - } - return entry; - } - - /** - * Put a cache entry to indicate that the class should not be - * weaved; the original bytes of the class should be used. - * - * @param ref The cache reference - * @param classBytes The un-weaved class bytes - */ - public void ignore(CachedClassReference ref, byte[] classBytes) { - stats.putIgnored(); - backing.put(new CachedClassEntry(ref, ZERO_BYTES, CachedClassEntry.EntryType.IGNORED), classBytes); - } - - /** - * Invalidate a cache entry - * - * @param ref - */ - public void remove(CachedClassReference ref) { - backing.remove(ref); - } - - /** - * Clear the entire cache - */ - public void clear() { - backing.clear(); - } - - /** - * Get the statistics associated with this cache, or - * null if statistics have not been enabled. - * - * @return - */ - public CacheStatistics getStats() { - return stats; - } - - /** - * Return a list of all WeavedClassCaches which have been initialized - * - * @return - */ - public static List getCaches() { - synchronized (cacheRegistry) { - return new LinkedList(cacheRegistry); - } - } - - protected void error(String message, Throwable th) { - messageHandler.handleMessage(new Message(message, IMessage.ERROR, th, null)); - } - - protected void error(String message) { - MessageUtil.error(messageHandler, message); - } - - protected void info(String message) { - MessageUtil.info(message); - } - -} diff --git a/weaver/src/org/aspectj/weaver/tools/cache/ZippedFileCacheBacking.java b/weaver/src/org/aspectj/weaver/tools/cache/ZippedFileCacheBacking.java deleted file mode 100644 index 6ee8b728c..000000000 --- a/weaver/src/org/aspectj/weaver/tools/cache/ZippedFileCacheBacking.java +++ /dev/null @@ -1,321 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * Lyor Goldstein (vmware) add support for weaved class being re-defined - *******************************************************************************/ - -package org.aspectj.weaver.tools.cache; - -import java.io.BufferedOutputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.StreamCorruptedException; -import java.util.Collection; -import java.util.Collections; -import java.util.Enumeration; -import java.util.Map; -import java.util.TreeMap; -import java.util.TreeSet; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; -import java.util.zip.ZipOutputStream; - -import org.aspectj.util.FileUtil; -import org.aspectj.util.LangUtil; - -/** - * Uses a ZIP file to store the instrumented classes/aspects - each one as a - * separate {@link ZipEntry}. This mechanism is suitable for relatively - * large numbers of instrumented classes/aspects (100's and more) since it - * holds all of them in a single (ZIP) file. The down side is that any - * modifications to the cache require re-writing the entire ZIP file. This - * can cause the ZIP file to become corrupted if interrupted in mid-update, - * thus requiring the re-population of the cache on next application activation - * (though the overhead in this case is not prohibitvely high...) - */ -public class ZippedFileCacheBacking extends AsynchronousFileCacheBacking { - public static final String ZIP_FILE = "cache.zip"; - private static final AsynchronousFileCacheBackingCreator defaultCreator= - new AsynchronousFileCacheBackingCreator() { - public ZippedFileCacheBacking create(File cacheDir) { - return new ZippedFileCacheBacking(cacheDir); - } - }; - - private final File zipFile; - public ZippedFileCacheBacking(File cacheDir) { - super(cacheDir); - zipFile = new File(cacheDir, ZIP_FILE); - } - - public File getZipFile () { - return zipFile; - } - - public static final ZippedFileCacheBacking createBacking (File cacheDir) { - return createBacking(cacheDir, defaultCreator); - } - - @Override - protected void writeClassBytes(String key, byte[] bytes) throws Exception { - File outFile=getZipFile(); - Map entriesMap; - try { - entriesMap = readZipClassBytes(outFile); - } catch(Exception e) { - if ((logger != null) && logger.isTraceEnabled()) { - logger.warn("writeClassBytes(" + outFile + ")[" + key + "]" - + " failed (" + e.getClass().getSimpleName() + ")" - + " to read current data: " + e.getMessage(), - e); - } - - FileUtil.deleteContents(outFile); - return; - } - - if (entriesMap.isEmpty()) { - entriesMap = Collections.singletonMap(key, bytes); - } else { - entriesMap.put(key, bytes); - } - - try { - writeZipClassBytes(outFile, entriesMap); - } catch(Exception e) { - if ((logger != null) && logger.isTraceEnabled()) { - logger.warn("writeClassBytes(" + outFile + ")[" + key + "]" - + " failed (" + e.getClass().getSimpleName() + ")" - + " to write updated data: " + e.getMessage(), - e); - } - - FileUtil.deleteContents(outFile); - } - } - - @Override - protected void removeClassBytes(String key) throws Exception { - File outFile=getZipFile(); - Map entriesMap; - try { - entriesMap = readZipClassBytes(outFile); - } catch(Exception e) { - if ((logger != null) && logger.isTraceEnabled()) { - logger.warn("removeClassBytes(" + outFile + ")[" + key + "]" - + " failed (" + e.getClass().getSimpleName() + ")" - + " to read current data: " + e.getMessage(), - e); - } - - FileUtil.deleteContents(outFile); - return; - } - - if (!entriesMap.isEmpty()) { - if (entriesMap.remove(key) == null) { - return; // not in the data file to begin with so nothing to update - } - } - - try { - writeZipClassBytes(outFile, entriesMap); - } catch(Exception e) { - if ((logger != null) && logger.isTraceEnabled()) { - logger.warn("removeClassBytes(" + outFile + ")[" + key + "]" - + " failed (" + e.getClass().getSimpleName() + ")" - + " to write updated data: " + e.getMessage(), - e); - } - - FileUtil.deleteContents(outFile); - } - } - - @Override - protected Map readClassBytes(Map indexMap, File cacheDir) { - File dataFile=new File(cacheDir, ZIP_FILE); - Map entriesMap; - boolean okEntries=true; - try { - entriesMap = readZipClassBytes(dataFile); - } catch(Exception e) { - if ((logger != null) && logger.isTraceEnabled()) { - logger.warn("Failed (" + e.getClass().getSimpleName() + ")" - + " to read zip entries from " + dataFile - + ": " + e.getMessage(), - e); - } - - entriesMap = new TreeMap(); - okEntries = false; - } - - if (!syncClassBytesEntries(dataFile, indexMap, entriesMap)) { - okEntries = false; - } - - if (!okEntries) { - FileUtil.deleteContents(dataFile); - - if (!entriesMap.isEmpty()) { - entriesMap.clear(); - } - } - - syncIndexEntries(dataFile, indexMap, entriesMap); - - return entriesMap; - } - - // remove all non-ignored entries that have no class bytes - protected Collection syncIndexEntries (File dataFile, Map indexMap, Map entriesMap) { - Collection toDelete=null; - for (Map.Entry ie : indexMap.entrySet()) { - String key=ie.getKey(); - IndexEntry indexEntry=ie.getValue(); - if (indexEntry.ignored) { - continue; // ignored entries have no class bytes - } - - if (entriesMap.containsKey(key)) { - continue; - } - - if ((logger != null) && logger.isTraceEnabled()) { - logger.debug("syncIndexEntries(" + dataFile + ")[" + key + "] no class bytes"); - } - - if (toDelete == null) { - toDelete = new TreeSet(); - } - toDelete.add(key); - } - - if (toDelete == null) { - return Collections.emptySet(); - } - - for (String key : toDelete) { - indexMap.remove(key); - } - - return toDelete; - } - - // check if all class bytes entries are valid - protected boolean syncClassBytesEntries (File dataFile, Map indexMap, Map entriesMap) { - boolean okEntries=true; - - for (Map.Entry bytesEntry : entriesMap.entrySet()) { - String key=bytesEntry.getKey(); - IndexEntry indexEntry=indexMap.get(key); - // ignored entries should have no bytes - if ((indexEntry == null) || indexEntry.ignored) { - if ((logger != null) && logger.isTraceEnabled()) { - logger.debug("syncClassBytesEntries(" + dataFile + ")[" + key + "] bad index entry"); - } - - okEntries = false; - continue; - } - - long crc=crc(bytesEntry.getValue()); - if (crc != indexEntry.crcWeaved) { - if ((logger != null) && logger.isTraceEnabled()) { - logger.debug("syncClassBytesEntries(" + dataFile + ")[" + key + "]" - + " mismatched CRC - expected=" + indexEntry.crcWeaved + "/got=" + crc); - } - - indexMap.remove(key); - okEntries = false; - continue; - } - } - - return okEntries; - } - - @Override - protected IndexEntry resolveIndexMapEntry(File cacheDir, IndexEntry ie) { - if (cacheDir.exists()) { - return ie; // we will take care of non-existing index entries in the readClassBytes method - } else { - return null; - } - } - - public static final Map readZipClassBytes (File file) throws IOException { - if (!file.canRead()) { - return Collections.emptyMap(); - } - - Map result=new TreeMap(); - byte[] copyBuf=new byte[4096]; - ByteArrayOutputStream out=new ByteArrayOutputStream(copyBuf.length); - ZipFile zipFile=new ZipFile(file); - try { - for (Enumeration entries=zipFile.entries(); (entries != null) && entries.hasMoreElements(); ) { - ZipEntry e=entries.nextElement(); - String name=e.getName(); - if (LangUtil.isEmpty(name)) { - continue; - } - - out.reset(); - - InputStream zipStream=zipFile.getInputStream(e); - try { - for (int nRead=zipStream.read(copyBuf); nRead != (-1); nRead=zipStream.read(copyBuf)) { - out.write(copyBuf, 0, nRead); - } - } finally { - zipStream.close(); - } - - byte[] data=out.toByteArray(), prev=result.put(name, data); - if (prev != null) { - throw new StreamCorruptedException("Multiple entries for " + name); - } - } - } finally { - zipFile.close(); - } - - return result; - } - - public static final void writeZipClassBytes (File file, Map entriesMap) throws IOException { - if (entriesMap.isEmpty()) { - FileUtil.deleteContents(file); - return; - } - - File zipDir=file.getParentFile(); - if ((!zipDir.exists()) && (!zipDir.mkdirs())) { - throw new IOException("Failed to create path to " + zipDir.getAbsolutePath()); - } - - ZipOutputStream zipOut=new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(file), 4096)); - try { - for (Map.Entry bytesEntry : entriesMap.entrySet()) { - String key=bytesEntry.getKey(); - byte[] bytes=bytesEntry.getValue(); - zipOut.putNextEntry(new ZipEntry(key)); - zipOut.write(bytes); - zipOut.closeEntry(); - } - } finally { - zipOut.close(); - } - } -} diff --git a/weaver/src/test/java/$Proxy1.java b/weaver/src/test/java/$Proxy1.java new file mode 100644 index 000000000..51cae8b05 --- /dev/null +++ b/weaver/src/test/java/$Proxy1.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2009 Contributors + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement - initial API and implementation + *******************************************************************************/ +import java.io.Serializable; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; + +public class $Proxy1 extends Proxy implements MessageService { + + protected $Proxy1(InvocationHandler arg0) { + super(arg0); + } + + public Object get1(Long t) { + return null; + } + + public Object get2(Serializable s) { + return null; + } +} diff --git a/weaver/src/test/java/CounterAspect.java b/weaver/src/test/java/CounterAspect.java new file mode 100644 index 000000000..1ca126d02 --- /dev/null +++ b/weaver/src/test/java/CounterAspect.java @@ -0,0 +1,52 @@ +/* ******************************************************************* + * Copyright (c) 2008 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement initial implementation + * ******************************************************************/ +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; + +/** + * Created to enable PointcutDesignatorHandlerTests.testParsingBeanInReferencePointcut01 and 02 to run + * + * @author Andy Clement + */ +@Aspect +public class CounterAspect { + + int count; + + @Before("execution(* set*(..)) && bean(testBean1)") + public void increment1ForAnonymousPointcut() { + count++; + } + + @Pointcut("execution(* toString(..)) && bean(testBean1)") + public void testBean1toString() { + } + + @Pointcut("execution(* setAge(..)) && bean(testBean1)") + public void testBean1SetAge() { + } + + @Pointcut("execution(* setAge(..)) && bean(testBean2)") + public void testBean2SetAge() { + } + + @Before("testBean1SetAge()") + public void increment1() { + count++; + } + + @Before("testBean2SetAge()") + public void increment2() { + count++; + } +} \ No newline at end of file diff --git a/weaver/src/test/java/GenericService.java b/weaver/src/test/java/GenericService.java new file mode 100644 index 000000000..87f5c0419 --- /dev/null +++ b/weaver/src/test/java/GenericService.java @@ -0,0 +1,17 @@ +/******************************************************************************* + * Copyright (c) 2009 Contributors + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement - initial API and implementation + *******************************************************************************/ +import java.io.Serializable; + +public interface GenericService { + Object get1(T t); + + Object get2(Serializable s); +} diff --git a/weaver/src/test/java/MA.java b/weaver/src/test/java/MA.java new file mode 100644 index 000000000..270882586 --- /dev/null +++ b/weaver/src/test/java/MA.java @@ -0,0 +1,15 @@ +/* ******************************************************************* + * Copyright (c) 2008 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors + * Andy Clement + * ******************************************************************/ + +public @interface MA { + +} diff --git a/weaver/src/test/java/MB.java b/weaver/src/test/java/MB.java new file mode 100644 index 000000000..3ff3eede4 --- /dev/null +++ b/weaver/src/test/java/MB.java @@ -0,0 +1,15 @@ +/* ******************************************************************* + * Copyright (c) 2008 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors + * Andy Clement + * ******************************************************************/ + +public @interface MB { + +} diff --git a/weaver/src/test/java/MC.java b/weaver/src/test/java/MC.java new file mode 100644 index 000000000..d48133baf --- /dev/null +++ b/weaver/src/test/java/MC.java @@ -0,0 +1,15 @@ +/* ******************************************************************* + * Copyright (c) 2008 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors + * Andy Clement + * ******************************************************************/ + +public @interface MC { + +} diff --git a/weaver/src/test/java/MD.java b/weaver/src/test/java/MD.java new file mode 100644 index 000000000..47a61cba0 --- /dev/null +++ b/weaver/src/test/java/MD.java @@ -0,0 +1,15 @@ +/* ******************************************************************* + * Copyright (c) 2008 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors + * Andy Clement + * ******************************************************************/ + +public @interface MD { + +} diff --git a/weaver/src/test/java/MessageService.java b/weaver/src/test/java/MessageService.java new file mode 100644 index 000000000..160aecc21 --- /dev/null +++ b/weaver/src/test/java/MessageService.java @@ -0,0 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2009 Contributors + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement - initial API and implementation + *******************************************************************************/ +public interface MessageService extends GenericService { + +} diff --git a/weaver/src/test/java/fluffy/Aspect.java b/weaver/src/test/java/fluffy/Aspect.java new file mode 100644 index 000000000..401ce9ba8 --- /dev/null +++ b/weaver/src/test/java/fluffy/Aspect.java @@ -0,0 +1,29 @@ +package fluffy; +import org.aspectj.runtime.internal.AroundClosure; + +public class Aspect { + + public static void ignoreMe() {} + + public static void before_method_call() { + System.out.println("before"); + } + + public static void afterReturning_method_call() { + System.out.println("afterReturning"); + } + + public static void afterThrowing_method_execution(Throwable t) { + System.out.println("afterThrowing " + t); + t.printStackTrace(); + } + + public static Object aroundFun(AroundClosure c) { + System.out.println("around"); + try { + return c.run(new Object[0]); + } catch (Throwable t) { + return null; + } + } +} diff --git a/weaver/src/test/java/fluffy/Base.java b/weaver/src/test/java/fluffy/Base.java new file mode 100644 index 000000000..4cdb1f772 --- /dev/null +++ b/weaver/src/test/java/fluffy/Base.java @@ -0,0 +1,18 @@ +package fluffy; + +public class Base { + + public static void onlyBase() {} + public static void both() {} + + public void onlyBaseNonStatic() {} + public void bothNonStatic() {} + + public int onlyBase; + public int both; + + public Base() {} + public Base(int i) {} + + public void m() throws CloneNotSupportedException {} +} diff --git a/weaver/src/test/java/fluffy/Derived.java b/weaver/src/test/java/fluffy/Derived.java new file mode 100644 index 000000000..ad0842c18 --- /dev/null +++ b/weaver/src/test/java/fluffy/Derived.java @@ -0,0 +1,20 @@ +package fluffy; + +import java.io.IOException; + +public class Derived extends Base { + + public static void onlyDerived() throws IOException, CloneNotSupportedException {} + public static void both() {} + + public void onlyDerivedNonStatic() {} + public void bothNonStatic() {} + + public int onlyDerived; + public int both; + + public Derived() {} + + public void m() {} + +} diff --git a/weaver/src/test/java/org/aspectj/matcher/tools/ReflectionWorldAdvancedPointcutExpressionTest.java b/weaver/src/test/java/org/aspectj/matcher/tools/ReflectionWorldAdvancedPointcutExpressionTest.java new file mode 100644 index 000000000..9e16e65fb --- /dev/null +++ b/weaver/src/test/java/org/aspectj/matcher/tools/ReflectionWorldAdvancedPointcutExpressionTest.java @@ -0,0 +1,30 @@ +/* ******************************************************************* + * Copyright (c) 2008 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement + * ******************************************************************/ +package org.aspectj.matcher.tools; + +import org.aspectj.weaver.World; +import org.aspectj.weaver.reflect.ReflectionWorld; + +/** + * Run all the pointcut parsing/matching tests against a ReflectionWorld. + * + * @author Andy Clement + */ +public class ReflectionWorldAdvancedPointcutExpressionTest extends CommonAdvancedPointcutExpressionTests { + + protected World getWorld() { + World w = new ReflectionWorld(false, getClass().getClassLoader()); + w.setBehaveInJava5Way(true); + return w; + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/AbstractTraceTest.java b/weaver/src/test/java/org/aspectj/weaver/AbstractTraceTest.java new file mode 100644 index 000000000..334690b62 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/AbstractTraceTest.java @@ -0,0 +1,153 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Matthew Webster - initial implementation + *******************************************************************************/ +package org.aspectj.weaver; + +import java.util.ArrayList; + +import junit.framework.TestCase; + +import org.aspectj.weaver.tools.AbstractTrace; +import org.aspectj.weaver.tools.DefaultTrace; +import org.aspectj.weaver.tools.Traceable; + +public abstract class AbstractTraceTest extends TestCase { + + protected AbstractTrace trace; + + public void testIsTraceEnabled() { + DefaultTrace trace = new DefaultTrace(getClass()); + assertFalse(trace.isTraceEnabled()); + } + + public void testEnterWithThisAndArgs() { + trace.enter("testEnterWithThisAndArgs",this,new Object[] { "arg1", "arg2" }); + } + + public void testEnterWithThisAndArray() { + Object arg1 = new String[] { "s1", "s2" }; + Object arg2 = new char[] { 'a', 'b', 'c' }; + trace.enter(getName(),this,new Object[] { arg1, arg2 }); + } + + public void testEnterWithThisAndCollection() { + Object arg1 = new ArrayList(); + trace.enter(getName(),this,new Object[] { arg1 }); + } + + public void testEnterWithThisAndTraceable () { + Object arg1 = new Traceable() { + + public String toTraceString() { + return getClass().getName() + "[Traceable]"; + } + + }; + trace.enter(getName(),this,new Object[] { arg1 }); + } + + public void testEnterWithThisAndToStringException () { + Object arg1 = new Object() { + + public String toString() { + throw new RuntimeException("toString() can throw an Exception"); + } + + }; + trace.enter(getName(),this,new Object[] { arg1 }); + } + + public void testEnterWithThisAndHashCodeException () { + Object arg1 = new Object() { + + public int hashCode() { + throw new RuntimeException("hashCode can throw an Exception"); + } + + }; + trace.enter(getName(),this,new Object[] { arg1 }); + } + + public void testEnterWithThisAndClassLoader () { + Object arg1 = new ClassLoader() { + + public String toString() { + throw new Error("Don't call ClassLoader.toString()"); + } + + }; + trace.enter(getName(),this,new Object[] { arg1 }); + } + + public void testEnterWithThis() { + trace.enter("testEnterWithThis",this); + } + + public void testEnter() { + trace.enter("testEnter"); + } + + public void testExitWithReturn() { + trace.exit("testExitWithReturn","ret"); + } + + public void testExitWithThrowable() { + trace.exit("testExitWithThrowable",new RuntimeException()); + } + + public void testExit() { + trace.exit("testExit"); + } + + public void testEvent() { + trace.event("testEvent"); + } + + public void testEventWithThisAndArgs() { + trace.event("testEventWithThisAndArgs",this,new Object[] { "arg1", "arg2" }); + } + + public void testEventWithThisAndArg() { + trace.event("testEventWithThisAndArg",this,"arg1"); + } + + public void testDebug() { + trace.debug("debug"); + } + + public void testInfo() { + trace.info("information"); + } + + public void testWarn() { + trace.warn("warning"); + } + + public void testWarnWithException() { + trace.warn("warning",new RuntimeException("warning")); + } + + public void testError() { + trace.error("error"); + } + + public void testErrorWithException() { + trace.error("error",new RuntimeException("error")); + } + + public void testFatal() { + trace.fatal("fatal"); + } + + public void testFatalWithException() { + trace.fatal("fatal",new RuntimeException("fatal")); + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/AllWeaver5Tests.java b/weaver/src/test/java/org/aspectj/weaver/AllWeaver5Tests.java new file mode 100644 index 000000000..6ce2e0354 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/AllWeaver5Tests.java @@ -0,0 +1,40 @@ +///* ******************************************************************* +// * Copyright (c) 2005-2006 Contributors. +// * All rights reserved. +// * This program and the accompanying materials are made available +// * under the terms of the Eclipse Public License v1.0 +// * which accompanies this distribution and is available at +// * http://eclipse.org/legal/epl-v10.html +// * +// * Contributors: +// * Adrian Colyer Initial implementation +// * Matthew Webster Move from default package +// * ******************************************************************/ +//package org.aspectj.weaver; +// +//import junit.framework.Test; +//import junit.framework.TestSuite; +// +//import org.aspectj.weaver.reflect.ReflectionWorldReferenceTypeTest; +//import org.aspectj.weaver.reflect.ReflectionWorldSpecificTest; +//import org.aspectj.weaver.tools.PointcutExpressionTest; +// +//public class AllWeaver5Tests { +// +// public static Test suite() { +// TestSuite suite = new TestSuite(AllWeaver5Tests.class.getName()); +// // $JUnit-BEGIN$ +// suite.addTest(AllTracing5Tests.suite()); +// suite.addTest(BcweaverModule15Test.suite()); +// suite.addTestSuite(ReflectionWorldReferenceTypeTest.class); +// suite.addTestSuite(PointcutExpressionTest.class); +// suite.addTestSuite(JoinPointSignatureIteratorTest.class); +// // gives incompatible class version error... +// // suite.addTestSuite(ReflectionWorldAdvancedPointcutExpressionTests.class +// // ); +// suite.addTestSuite(ReflectionWorldSpecificTest.class); +// // $JUnit-END$ +// return suite; +// } +// +//} diff --git a/weaver/src/test/java/org/aspectj/weaver/BcweaverModule15Test.java b/weaver/src/test/java/org/aspectj/weaver/BcweaverModule15Test.java new file mode 100644 index 000000000..8455315ff --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/BcweaverModule15Test.java @@ -0,0 +1,41 @@ +//package org.aspectj.weaver; +// +///* ******************************************************************* +// * Copyright (c) 2005 Contributors. +// * All rights reserved. +// * This program and the accompanying materials are made available +// * under the terms of the Eclipse Public License v1.0 +// * which accompanies this distribution and is available at +// * http://eclipse.org/legal/epl-v10.html +// * +// * Contributors: +// * Adrian Colyer Initial implementation +// * ******************************************************************/ +//import junit.framework.Test; +//import junit.framework.TestCase; +//import junit.framework.TestSuite; +// +//import org.aspectj.weaver.bcel.BcelGenericSignatureToTypeXTestCase; +//import org.aspectj.weaver.bcel.BcelWorldReferenceTypeTest; +//import org.aspectj.weaver.patterns.WildTypePatternResolutionTestCase; +//import org.aspectj.weaver.tools.Java15PointcutExpressionTest; +// +//public class BcweaverModule15Test extends TestCase { +// public static Test suite() { +// TestSuite suite = new TestSuite(BcweaverModule15Test.class.getName()); +// suite.addTestSuite(BcelGenericSignatureToTypeXTestCase.class); +// suite.addTestSuite(BoundedReferenceTypeTestCase.class); +// suite.addTest(Java15PointcutExpressionTest.suite()); +// suite.addTestSuite(Member15Test.class); +// suite.addTestSuite(BcelWorldReferenceTypeTest.class); +// suite.addTest(Java5ReflectionBasedReferenceTypeDelegateTest.suite()); +// suite.addTestSuite(TypeVariableTestCase.class); +// suite.addTestSuite(TypeVariableReferenceTypeTestCase.class); +// suite.addTestSuite(WildTypePatternResolutionTestCase.class); +// return suite; +// } +// +// public BcweaverModule15Test(String name) { +// super(name); +// } +//} diff --git a/weaver/src/test/java/org/aspectj/weaver/BoundedReferenceTypeTestCase.java b/weaver/src/test/java/org/aspectj/weaver/BoundedReferenceTypeTestCase.java new file mode 100644 index 000000000..403b2ecb1 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/BoundedReferenceTypeTestCase.java @@ -0,0 +1,106 @@ +/* ******************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver; + +import junit.framework.TestCase; + +import org.aspectj.weaver.bcel.BcelWorld; + +public class BoundedReferenceTypeTestCase extends TestCase { + + ReferenceType javaLangClass; + ReferenceType javaLangObject; + BoundedReferenceType extendsClass; + BoundedReferenceType superClass; + BoundedReferenceType extendsWithExtras; + + public void testSignature() { + String extendsSig = extendsClass.getSignature(); + assertEquals("+Ljava/lang/Class;", extendsSig); + assertEquals("-Ljava/lang/Class;", superClass.getSignature()); + } + + public void testExtendsBounds() { + assertFalse("has no lower bound", extendsClass.hasLowerBound()); + assertNull("no lower bound", extendsClass.getLowerBound()); + assertEquals(javaLangClass, extendsClass.getUpperBound()); + assertEquals("no interface bounds", 0, extendsClass.getAdditionalBounds().length); + } + + public void testSuperBounds() { + assertTrue("has lower bound", superClass.hasLowerBound()); + assertEquals(javaLangClass, superClass.getLowerBound()); + assertEquals("Ljava/lang/Object;", superClass.getUpperBound().getSignature()); + assertEquals("no interface bounds", 0, superClass.getAdditionalBounds().length); + } + + public void testIsExtends() { + assertTrue(extendsClass.kind == BoundedReferenceType.EXTENDS); + assertFalse(superClass.kind == BoundedReferenceType.EXTENDS); + } + + public void testIsSuper() { + assertTrue(superClass.kind == BoundedReferenceType.SUPER); + assertFalse(extendsClass.kind == BoundedReferenceType.SUPER); + } + + public void testGetDeclaredInterfacesNoAdditions() { + ResolvedType[] rt1 = extendsClass.getDeclaredInterfaces(); + ResolvedType[] rt2 = javaLangClass.getDeclaredInterfaces(); + assertEquals("same length", rt1.length, rt2.length); + for (int i = 0; i < rt2.length; i++) { + assertEquals("same methods", rt1[i], rt2[i]); + } + } + + public void testGetDeclaredInterfacesWithInterfaceBounds() { + ResolvedType[] rt1 = extendsWithExtras.getDeclaredInterfaces(); + ResolvedType[] rt2 = javaLangClass.getDeclaredInterfaces(); + assertEquals("one extra interface", rt1.length, rt2.length + 1); + for (int i = 0; i < rt2.length; i++) { + assertEquals("same methods", rt1[i], rt2[i]); + } + assertEquals("Ljava/util/List;", rt1[rt1.length - 1].getSignature()); + } + + // all other methods in signature are delegated to upper bound... + // representative test + public void testGetDeclaredMethodsExtends() { + ResolvedMember[] rm1 = extendsClass.getDeclaredMethods(); + ResolvedMember[] rm2 = javaLangClass.getDeclaredMethods(); + assertEquals("same length", rm1.length, rm2.length); + for (int i = 0; i < rm2.length; i++) { + assertEquals("same methods", rm1[i], rm2[i]); + } + } + + public void testGetDeclaredMethodsSuper() { + ResolvedMember[] rm1 = superClass.getDeclaredMethods(); + ResolvedMember[] rm2 = javaLangObject.getDeclaredMethods(); + assertEquals("same length", rm1.length, rm2.length); + for (int i = 0; i < rm2.length; i++) { + assertEquals("same methods", rm1[i], rm2[i]); + } + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + BcelWorld world = new BcelWorld(); + javaLangClass = (ReferenceType) world.resolve(UnresolvedType.forName("java/lang/Class")); + javaLangObject = (ReferenceType) world.resolve(UnresolvedType.OBJECT); + extendsClass = new BoundedReferenceType(javaLangClass, true, world); + superClass = new BoundedReferenceType(javaLangClass, false, world); + extendsWithExtras = new BoundedReferenceType(javaLangClass, true, world, new ReferenceType[] { (ReferenceType) world + .resolve(UnresolvedType.forName("java/util/List")) }); + } +} diff --git a/weaver/src/test/java/org/aspectj/weaver/CommonReferenceTypeTests.java b/weaver/src/test/java/org/aspectj/weaver/CommonReferenceTypeTests.java new file mode 100644 index 000000000..8d683e8af --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/CommonReferenceTypeTests.java @@ -0,0 +1,91 @@ +/* ******************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver; + +import junit.framework.TestCase; + +// test cases for Adrian's additions to ReferenceType +// XXX - couldn't find any unit test cases for the rest of the ReferenceType class +public abstract class CommonReferenceTypeTests extends TestCase { + + private World world; + + public abstract World getWorld(); + + public void setUp() { + world = getWorld(); + } + + public void testUnresolvedTypeSignatureProcessing() { + world.setBehaveInJava5Way(true); + UnresolvedType ut = null; + ut = UnresolvedType.forName("java.util.List>[]").resolve(world); + ut = UnresolvedType.forSignature("[Pjava/util/List;>;").resolve(world); + assertEquals("Signatures not equal ", "[Pjava/util/List;>;", ut.getSignature()); + assertEquals("Names not equal ", "java.util.List>[]", ut.getName()); + } + + public void testArrays() { + world.setBehaveInJava5Way(true); + UnresolvedType ut = null; + ut = UnresolvedType.forName("[Ljava.lang.String;"); + assertEquals("[Ljava/lang/String;",ut.getSignature()); + UnresolvedType reified = UnresolvedType.forSignature(ut.getSignature()); + ResolvedType rt = world.resolve(reified); + assertEquals("[Ljava/lang/String;",rt.getSignature()); + assertEquals("java.lang.String[]",rt.getName()); + assertFalse(rt.isMissing()); + + ut = UnresolvedType.forName("[[[[Ljava.lang.String;"); + assertEquals("[[[[Ljava/lang/String;",ut.getSignature()); + reified = UnresolvedType.forSignature(ut.getSignature()); + rt = world.resolve(reified); + assertEquals("[[[[Ljava/lang/String;",rt.getSignature()); + assertEquals("java.lang.String[][][][]",rt.getName()); + assertTrue(rt.isArray()); + assertTrue(rt.getComponentType().isArray()); + assertFalse(rt.isMissing()); + } + + public void testIsRawTrue() { + world.setBehaveInJava5Way(true); + UnresolvedType javaLangClass = UnresolvedType.forName("java.lang.Class"); + ResolvedType rtx = world.resolve(javaLangClass); + assertTrue("Resolves to reference type", (rtx instanceof ReferenceType)); + ReferenceType rt = (ReferenceType) rtx; + assertTrue("java.lang.Class is raw", rt.isRawType()); + } + + public void testIsRawFalse() { + world.setBehaveInJava5Way(true); + UnresolvedType javaLangObject = UnresolvedType.forName("java.lang.Object"); + ResolvedType rtx = world.resolve(javaLangObject); + assertTrue("Resolves to reference type", (rtx instanceof ReferenceType)); + ReferenceType rt = (ReferenceType) rtx; + assertFalse("java.lang.Object is not raw", rt.isRawType()); + } + + public void testIsGenericTrue() { + world.setBehaveInJava5Way(true); + UnresolvedType javaLangClass = UnresolvedType.forName("java.lang.Class"); + ResolvedType rtx = world.resolve(javaLangClass); + assertTrue("java.lang.Class has underpinning generic type", rtx.getGenericType().isGenericType()); + } + + public void testIsGenericFalse() { + world.setBehaveInJava5Way(true); + UnresolvedType javaLangObject = UnresolvedType.forName("java.lang.Object"); + ResolvedType rtx = world.resolve(javaLangObject); + assertFalse(rtx.isGenericType()); + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/CommonsTraceFactoryTest.java b/weaver/src/test/java/org/aspectj/weaver/CommonsTraceFactoryTest.java new file mode 100644 index 000000000..ecdaf63de --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/CommonsTraceFactoryTest.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Matthew Webster - initial implementation + *******************************************************************************/ +package org.aspectj.weaver; + +import junit.framework.TestCase; + +import org.aspectj.weaver.tools.CommonsTraceFactory; +import org.aspectj.weaver.tools.Trace; + +public class CommonsTraceFactoryTest extends TestCase { + + public void testGetTraceFactory() { + CommonsTraceFactory factory = new CommonsTraceFactory(); + Trace trace = factory.getTrace(getClass()); + assertFalse("Tracing should be disbled by default",trace.isTraceEnabled()); + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/CommonsTraceTest.java b/weaver/src/test/java/org/aspectj/weaver/CommonsTraceTest.java new file mode 100644 index 000000000..ac6a8cec3 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/CommonsTraceTest.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Matthew Webster - initial implementation + *******************************************************************************/ +package org.aspectj.weaver; + +import org.aspectj.weaver.tools.CommonsTrace; + +public class CommonsTraceTest extends AbstractTraceTest { + + protected void setUp() throws Exception { + super.setUp(); + trace = new CommonsTrace(getClass()); + trace.setTraceEnabled(true); + } + + public void testCommonsTrace() { +// CommonsTrace trace = + new CommonsTrace(getClass()); + } + + public void testSetTraceEnabled() { + CommonsTrace trace = new CommonsTrace(getClass()); + trace.setTraceEnabled(true); + /* XXX Need to find out how to turn tracing on */ +// assertTrue(trace.isTraceEnabled()); + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/DefaultTraceFactoryTest.java b/weaver/src/test/java/org/aspectj/weaver/DefaultTraceFactoryTest.java new file mode 100644 index 000000000..16d14bfb6 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/DefaultTraceFactoryTest.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Matthew Webster - initial implementation + *******************************************************************************/ +package org.aspectj.weaver; + +import org.aspectj.weaver.tools.DefaultTraceFactory; +import org.aspectj.weaver.tools.Trace; + +import junit.framework.TestCase; + +public class DefaultTraceFactoryTest extends TestCase { + + public void testGetTrace() { + DefaultTraceFactory factory = new DefaultTraceFactory(); + Trace trace = factory.getTrace(getClass()); + assertFalse("Tracing should be disbled by default",trace.isTraceEnabled()); + } + +// public void testIsEnabled() { +// fail("Not yet implemented"); +// } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/DefaultTraceTest.java b/weaver/src/test/java/org/aspectj/weaver/DefaultTraceTest.java new file mode 100644 index 000000000..a8313c4fb --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/DefaultTraceTest.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Matthew Webster - initial implementation + *******************************************************************************/ +package org.aspectj.weaver; + +import org.aspectj.weaver.tools.DefaultTrace; + +public class DefaultTraceTest extends AbstractTraceTest { + + protected void setUp() throws Exception { + super.setUp(); + trace = new DefaultTrace(getClass()); + trace.setTraceEnabled(true); + } + + public void testDefaultTrace() { +// DefaultTrace trace = + new DefaultTrace(getClass()); + } + + public void testSetTraceEnabled() { + DefaultTrace trace = new DefaultTrace(getClass()); + trace.setTraceEnabled(true); + assertTrue(trace.isTraceEnabled()); + } + + public void testSetPrintStream () { + DefaultTrace trace = new DefaultTrace(getClass()); + trace.setPrintStream(System.out); + } +} diff --git a/weaver/src/test/java/org/aspectj/weaver/DumpTestCase.java b/weaver/src/test/java/org/aspectj/weaver/DumpTestCase.java new file mode 100644 index 000000000..affd39c2c --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/DumpTestCase.java @@ -0,0 +1,147 @@ +/******************************************************************************* + * Copyright (c) 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Matthew Webster + *******************************************************************************/ +package org.aspectj.weaver; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; + +import junit.framework.TestCase; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.IMessageHolder; +import org.aspectj.bridge.Message; +import org.aspectj.bridge.MessageHandler; + +/** + * @author websterm + * + * Test Dump facility. Ensure it can be configured and files contain expected contents. Testcase + * returns Dump configuration to orginal state. + */ +public class DumpTestCase extends TestCase { + + private File dumpFile; + private IMessage.Kind savedDumpCondition; + + public DumpTestCase(String name) { + super(name); + } + + protected void setUp() throws Exception { + super.setUp(); + + dumpFile = null; + savedDumpCondition = Dump.getDumpOnExit(); + } + + protected void tearDown() throws Exception { + super.tearDown(); + + if (dumpFile != null && dumpFile.exists()) { + boolean deleted = dumpFile.delete(); + assertTrue("Dump file '" + dumpFile.getPath() + "' could not be deleted",deleted); + } + Dump.setDumpOnExit(savedDumpCondition); + } + + public void testSetDumpOnException () { + Dump.setDumpOnException(true); + assertTrue("DumpOnException should be true",Dump.getDumpOnException()); + } + + public void testSetDumpOnExit () { + assertTrue("Should be able to set condition 'error'",Dump.setDumpOnExit("error")); + assertTrue("Should be able to set condition 'warning'",Dump.setDumpOnExit("warning")); + assertFalse("Should not be able to set condition 'junk'",Dump.setDumpOnExit("junk")); + } + + public void testDump () { + String fileName = Dump.dump("testDump()"); + dumpFile = new File(fileName); + assertTrue("Dump file '" + fileName + "' should exist",dumpFile.exists()); + } + + public void testDumpWithException () { + String message = "testDumpWithException()"; + String fileName = recursiveCall(message,100); + dumpFile = new File(fileName); + assertContents(dumpFile,"Exception Information",message); + } + + public void testDumpOnExit () { + Dump.setDumpOnExit("abort"); + Dump.saveMessageHolder(null); + String fileName = Dump.dumpOnExit(); + dumpFile = new File(fileName); + assertTrue("Dump file '" + fileName + "' should exist",dumpFile.exists()); + } + + public void testDumpOnExitExcluded () { + Dump.setDumpOnExit("abort"); + IMessageHolder holder = new MessageHandler(); + Dump.saveMessageHolder(holder); + holder.handleMessage(new Message("testDumpOnExitExcluded()",IMessage.ERROR,null,null)); + String fileName = Dump.dumpOnExit(); + dumpFile = new File(fileName); + assertEquals("Dump '" + fileName + "' should be excluded",Dump.DUMP_EXCLUDED,fileName); + } + + public void testDumpOnExitIncluded () { + Dump.setDumpOnExit("error"); + IMessageHolder holder = new MessageHandler(); + Dump.saveMessageHolder(holder); + IMessage error = new Message("testDumpOnExitIncluded()",IMessage.ERROR,null,null); + holder.handleMessage(error); + String fileName = Dump.dumpOnExit(); + dumpFile = new File(fileName); + assertContents(dumpFile,"Compiler Messages",error.getMessage()); + } + + /* Ensure dump file exists and contains certain contents under a given heading */ + public static void assertContents (File dumpFile, String heading, String contents) { + assertTrue("Dump file '" + dumpFile.getPath() + "' should exist",dumpFile.exists()); + assertTrue("Dump file '" + dumpFile.getPath()+ "' should contain '" + contents + "'",fileContains(dumpFile,heading,contents)); + } + + private static boolean fileContains (File dumpFile, String heading, String contents) { + boolean result = false; + + try { + BufferedReader reader = new BufferedReader(new FileReader(dumpFile)); + String currentHeading = ""; + String record; + while ((null != (record = reader.readLine())) && (result == false)) { + if (record.startsWith("----")) currentHeading = record; + else if ((record.indexOf(contents) != -1) && currentHeading.indexOf(heading) != -1) result = true; + } + reader.close(); + } + catch (IOException ex) { + fail(ex.toString()); + } + + return result; + } + + /* Generate a big stack trace */ + private String recursiveCall (String message, int depth) { + if (depth == 0) { + Throwable th = new RuntimeException(message); + return Dump.dumpWithException(th); + } + else { + return recursiveCall(message,--depth); + } + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/GenericSignatureParserTest.java b/weaver/src/test/java/org/aspectj/weaver/GenericSignatureParserTest.java new file mode 100644 index 000000000..e5948eb40 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/GenericSignatureParserTest.java @@ -0,0 +1,63 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * ******************************************************************/ + package org.aspectj.weaver; + +import junit.framework.TestCase; + +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.classfile.Method; +import org.aspectj.apache.bcel.util.SyntheticRepository; +import org.aspectj.util.GenericSignatureParser; + +/** + * @author Adrian Colyer + * @author Andy Clement + */ +public class GenericSignatureParserTest extends TestCase { + + GenericSignatureParser parser; + + protected void setUp() throws Exception { + super.setUp(); + parser = new GenericSignatureParser(); + } + + public void testClassSignatureParsingInJDK() throws Exception { + SyntheticRepository repository = SyntheticRepository.getInstance(); + String[] testClasses = new String[] { "java.lang.Comparable", "java.lang.Iterable", "java.lang.Class", "java.lang.Enum", + "java.lang.InheritableThreadLocal", "java.lang.ThreadLocal", "java.util.Collection", "java.util.Comparator", + "java.util.Enumeration", "java.util.Iterator", "java.util.List", "java.util.ListIterator", "java.util.Map", + "java.util.Map$Entry", "java.util.Queue", "java.util.Set", "java.util.SortedMap", "java.util.SortedSet" }; + for (int i = 0; i < testClasses.length; i++) { + JavaClass jc = repository.loadClass(testClasses[i]); + String sig = jc.getGenericSignature(); + parser.parseAsClassSignature(sig); + } + } + + public void testMethodSignatureParsingInJDK() throws Exception { + SyntheticRepository repository = SyntheticRepository.getInstance(); + String[] testClasses = new String[] { "java.lang.Comparable", "java.lang.Iterable", "java.lang.Class", "java.lang.Enum", + "java.lang.InheritableThreadLocal", "java.lang.ThreadLocal", "java.util.Collection", "java.util.Comparator", + "java.util.Enumeration", "java.util.Iterator", "java.util.List", "java.util.ListIterator", "java.util.Map", + "java.util.Map$Entry", "java.util.Queue", "java.util.Set", "java.util.SortedMap", "java.util.SortedSet" }; + for (int i = 0; i < testClasses.length; i++) { + JavaClass jc = repository.loadClass(testClasses[i]); + Method[] methods = jc.getMethods(); + for (int j = 0; j < methods.length; j++) { + String sig = methods[j].getGenericSignature(); + if (sig != null) + parser.parseAsMethodSignature(sig); + } + } + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/Java5ReflectionBasedReferenceTypeDelegateTest.java b/weaver/src/test/java/org/aspectj/weaver/Java5ReflectionBasedReferenceTypeDelegateTest.java new file mode 100644 index 000000000..9f6a2965e --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/Java5ReflectionBasedReferenceTypeDelegateTest.java @@ -0,0 +1,154 @@ +/* ******************************************************************* + * Copyright (c) 2005-2017 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Clement Initial implementation + * ******************************************************************/ + +package org.aspectj.weaver; + +import junit.framework.Test; +import junit.framework.TestSuite; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.Field; + +import org.aspectj.weaver.reflect.ReflectionBasedReferenceTypeDelegateTest; + +public class Java5ReflectionBasedReferenceTypeDelegateTest extends ReflectionBasedReferenceTypeDelegateTest { + + public static Test suite() { + TestSuite suite = new TestSuite("TestJava5ReflectionBasedReferenceTypeDelegate"); + suite.addTestSuite(Java5ReflectionBasedReferenceTypeDelegateTest.class); + return suite; + } + + /** + * Let's play about with a generic type and ensure we can work with it in a reflective world. + */ + public void testResolveGeneric() { + UnresolvedType collectionType = UnresolvedType.forName("java.util.Collection"); + world.resolve(collectionType).getRawType().resolve(world); + ResolvedMember[] methods = world.resolve(collectionType).getDeclaredMethods(); + int i = -1; + for (int j=0;j 0); + TypeVariable tv = theGenericEnumType.getTypeVariables()[0]; + String expected = "TypeVar E extends java.lang.Enum"; + assertTrue("Type variable should be '" + expected + "' but is '" + tv + "'", tv.toString().equals(expected)); + } + + public void testResolveClass() { + world.resolve("java.lang.Class").getGenericType(); + } + + public void testGenericInterfaceSuperclass_ReflectionWorldResolution() { + + UnresolvedType javaUtilMap = UnresolvedType.forName("java.util.Map"); + + ReferenceType rawType = (ReferenceType) world.resolve(javaUtilMap); + assertTrue("Should be the raw type ?!? " + rawType.getTypekind(), rawType.isRawType()); + + ReferenceType genericType = (ReferenceType) rawType.getGenericType(); + assertTrue("Should be the generic type ?!? " + genericType.getTypekind(), genericType.isGenericType()); + + ResolvedType rt = rawType.getSuperclass(); + assertTrue("Superclass for Map raw type should be Object but was " + rt, rt.equals(UnresolvedType.OBJECT)); + + ResolvedType rt2 = genericType.getSuperclass(); + assertTrue("Superclass for Map generic type should be Object but was " + rt2, rt2.equals(UnresolvedType.OBJECT)); + } + + /** + * This is testing the optimization in the reflective annotation finder to verify that if you only want runtime + * annotation info then we use reflection and don't go digging through the classfile bytes. + */ + public void testAnnotationFinderClassRetention() throws Exception { + ResolvedType type = world.resolve(AnnoTesting.class.getName()); + ResolvedMember[] ms = type.getDeclaredMethods(); + int findMethod = findMethod("a", ms); + + ResolvedMember methodWithOnlyClassLevelAnnotation = ms[findMethod("a", ms)]; + ResolvedMember methodWithOnlyRuntimeLevelAnnotation = ms[findMethod("b", ms)]; + ResolvedMember methodWithClassAndRuntimeLevelAnnotations = ms[findMethod("c", ms)]; + ResolvedMember methodWithClassAndRuntimeLevelAnnotations2 = ms[findMethod("d", ms)]; + + assertTrue(methodWithOnlyClassLevelAnnotation.hasAnnotation(world.resolve(AnnoClass.class.getName()))); + assertTrue(methodWithOnlyRuntimeLevelAnnotation.hasAnnotation(world.resolve(AnnoRuntime.class.getName()))); + + // This is the tricky scenario. + + // When asking about the runtime level annotations it should not go digging into bcel + assertTrue(methodWithClassAndRuntimeLevelAnnotations.hasAnnotation(world.resolve(AnnoRuntime.class.getName()))); + + Field annotationsField = ResolvedMemberImpl.class.getDeclaredField("annotationTypes"); + annotationsField.setAccessible(true); + ResolvedType[] annoTypes = (ResolvedType[])annotationsField.get(methodWithClassAndRuntimeLevelAnnotations); + + // Should only be the runtime one here + assertEquals(1, annoTypes.length); + + // But when you do ask again and this time for class level, it should redo the unpack and pull both runtime and class out + assertTrue(methodWithClassAndRuntimeLevelAnnotations.hasAnnotation(world.resolve(AnnoClass.class.getName()))); + + annotationsField.setAccessible(true); + annoTypes = (ResolvedType[])annotationsField.get(methodWithClassAndRuntimeLevelAnnotations); + + // Now both should be there + assertEquals(2, annoTypes.length); + + assertTrue(methodWithClassAndRuntimeLevelAnnotations2.hasAnnotation(world.resolve(AnnoRuntime.class.getName()))); + // now ask for 'all annotations' via another route, this should reunpack and get them all + ResolvedType[] annotations = methodWithClassAndRuntimeLevelAnnotations2.getAnnotationTypes(); + assertEquals(2,annotations.length); + } + + @Retention(RetentionPolicy.CLASS) + @interface AnnoClass {} + + @Retention(RetentionPolicy.RUNTIME) + @interface AnnoRuntime {} + + class AnnoTesting { + + @AnnoClass + public void a() {} + + @AnnoRuntime + public void b() {} + + @AnnoClass @AnnoRuntime + public void c() {} + + @AnnoClass @AnnoRuntime + public void d() {} + + } + +} \ No newline at end of file diff --git a/weaver/src/test/java/org/aspectj/weaver/Jdk14TraceFactoryTest.java b/weaver/src/test/java/org/aspectj/weaver/Jdk14TraceFactoryTest.java new file mode 100644 index 000000000..cec4d6a3d --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/Jdk14TraceFactoryTest.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Matthew Webster - initial implementation + *******************************************************************************/ +package org.aspectj.weaver; + +import junit.framework.TestCase; + +import org.aspectj.weaver.tools.Jdk14TraceFactory; +import org.aspectj.weaver.tools.Trace; + +public class Jdk14TraceFactoryTest extends TestCase { + + public void testJdk14TraceFactory() { + new Jdk14TraceFactory(); + } + + public void testGetTrace() { + Jdk14TraceFactory factory = new Jdk14TraceFactory(); + Trace trace = factory.getTrace(getClass()); + assertFalse("Tracing should be disbled by default",trace.isTraceEnabled()); + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/Jdk14TraceTest.java b/weaver/src/test/java/org/aspectj/weaver/Jdk14TraceTest.java new file mode 100644 index 000000000..0c694cc9d --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/Jdk14TraceTest.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Matthew Webster - initial implementation + *******************************************************************************/ +package org.aspectj.weaver; + +import org.aspectj.weaver.tools.DefaultTrace; +import org.aspectj.weaver.tools.Jdk14Trace; + +public class Jdk14TraceTest extends AbstractTraceTest { + + protected void setUp() throws Exception { + super.setUp(); + trace = new Jdk14Trace(getClass()); + trace.setTraceEnabled(true); + } + + public void testJdk14Trace() { + new Jdk14Trace(getClass()); + } + + public void testSetTraceEnabled() { + DefaultTrace trace = new DefaultTrace(getClass()); + trace.setTraceEnabled(true); + assertTrue(trace.isTraceEnabled()); + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/JoinPointSignatureIteratorTest.java b/weaver/src/test/java/org/aspectj/weaver/JoinPointSignatureIteratorTest.java new file mode 100644 index 000000000..143de7333 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/JoinPointSignatureIteratorTest.java @@ -0,0 +1,107 @@ +/* ******************************************************************* + * Copyright (c) 2009 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement + * ******************************************************************/ +package org.aspectj.weaver; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import junit.framework.TestCase; + +import org.aspectj.weaver.reflect.ReflectionWorld; + +/** + * @author Andy Clement + */ +public class JoinPointSignatureIteratorTest extends TestCase { + + private World getWorld() { + return new ReflectionWorld(getClass().getClassLoader()); + } + + /** + * Checking the signatures for a dynamic proxy - which is created using erased information and so is completely generics unaware + */ + public void testPr268419() { + World w = getWorld(); + w.setBehaveInJava5Way(true); + + // The proxy class here is not generated, it is simply loaded up. $Proxy1 is in the java5-testsrc folder, but it + // obeys the rules of a generated proxy in that it extends java.lang.reflect.Proxy + ResolvedType proxy = UnresolvedType.forName("$Proxy1").resolve(w); + assertNotNull(proxy); + + // The test hierarchy here (messageservice and genericservice) contains 2 methods. One is generic + // and one is not. The aim of the test here is that the join point signatures generated for both + // should be the same because of the use of a proxy. + + List l = proxy.getMethodsWithoutIterator(false, false, false); + for (Object object : l) { + ResolvedMember rm = (ResolvedMember) object; + if (expectedResults.containsKey(rm.toString())) { + System.out.println("\nChecking: " + rm); + int i = 0; + List/* String */sigs = (List) expectedResults.get(rm.toString()); + Iterator jpsi = rm.getJoinPointSignatures(w); + while (jpsi.hasNext()) { + ResolvedMember sig = (ResolvedMember) jpsi.next(); + assertEquals(sigs.get(i).toString(), sig.toString()); + i++; + } + if (i != sigs.size()) { + fail("Expected " + sigs.size() + " signatures but got " + i); + } + } else { + if (rm.getName().equals("get1") || rm.getName().equals("get2")) { + fail("\nFound this unchecked get method " + rm); + // Iterator jpsi = rm.getJoinPointSignatures(w); + // while (jpsi.hasNext()) { + // ResolvedMember sig = (ResolvedMember) jpsi.next(); + // System.out.println(sig); + // } + } + } + } + } + + public static Map expectedResults = new HashMap(); + + static { + List sigs = new ArrayList(); + sigs.add("java.lang.Object $Proxy1.get1(java.io.Serializable)"); + sigs.add("java.lang.Object MessageService.get1(java.io.Serializable)"); + sigs.add("java.lang.Object GenericService.get1(java.io.Serializable)"); + sigs.add("java.lang.Object GenericService.get1(java.io.Serializable)"); + expectedResults.put("java.lang.Object $Proxy1.get1(java.io.Serializable)", sigs); + + sigs = new ArrayList(); + sigs.add("java.lang.Object $Proxy1.get2(java.io.Serializable)"); + sigs.add("java.lang.Object MessageService.get2(java.io.Serializable)"); + sigs.add("java.lang.Object GenericService.get2(java.io.Serializable)"); + sigs.add("java.lang.Object GenericService.get2(java.io.Serializable)"); + expectedResults.put("java.lang.Object $Proxy1.get2(java.io.Serializable)", sigs); + + sigs = new ArrayList(); + sigs.add("java.lang.Object $Proxy1.get1(java.lang.Long)"); + expectedResults.put("java.lang.Object $Proxy1.get1(java.lang.Long)", sigs); + + sigs = new ArrayList(); + sigs.add("java.lang.Object GenericService.get1(java.io.Serializable)"); + expectedResults.put("java.lang.Object GenericService.get1(java.io.Serializable)", sigs); + + sigs = new ArrayList(); + sigs.add("java.lang.Object GenericService.get2(java.io.Serializable)"); + expectedResults.put("java.lang.Object GenericService.get2(java.io.Serializable)", sigs); + } +} diff --git a/weaver/src/test/java/org/aspectj/weaver/LocaleTest.java b/weaver/src/test/java/org/aspectj/weaver/LocaleTest.java new file mode 100644 index 000000000..e69a4df93 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/LocaleTest.java @@ -0,0 +1,53 @@ +/* Copyright (c) 2002 Contributors. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + */ +package org.aspectj.weaver; + +import java.io.IOException; +import java.util.Locale; + +import junit.framework.TestCase; + +import org.aspectj.apache.bcel.generic.Instruction; +import org.aspectj.apache.bcel.util.ByteSequence; + +public class LocaleTest extends TestCase { + + public LocaleTest(String name) { + super(name); + } + + public void testNormalLocale() { + doBipush(); + } + + public void testTurkishLocale() { + Locale def = Locale.getDefault(); + Locale.setDefault(new Locale("tr", "")); + try { + doBipush(); + } finally { + Locale.setDefault(def); + } + } + + private static void doBipush() { + try { + Instruction.readInstruction( + new ByteSequence(new byte[] { + (byte)16, // bipush + (byte) 3 // data for bipush + })); + } catch (IOException e) { + throw new RuntimeException(e.getMessage()); + } + } +} + diff --git a/weaver/src/test/java/org/aspectj/weaver/Member15Test.java b/weaver/src/test/java/org/aspectj/weaver/Member15Test.java new file mode 100644 index 000000000..6cb0ce83f --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/Member15Test.java @@ -0,0 +1,155 @@ +/* ******************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver; + +import org.aspectj.weaver.bcel.BcelWorld; + +import junit.framework.TestCase; + +/** + * @author colyer + * @author clement + */ +public class Member15Test extends TestCase { + + public void testCanBeParameterizedRegularMethod() { + BcelWorld world = new BcelWorld(); + ResolvedType javaLangClass = world.resolve(UnresolvedType.forName("java/lang/Class")); + ResolvedMember[] methods = javaLangClass.getDeclaredMethods(); + ResolvedMember getAnnotations = null; + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equals("getAnnotations")) { + getAnnotations = methods[i]; + break; + } + } + if (getAnnotations != null) { // so can run on non-Java 5 +// System.out.println("got it"); + assertFalse(getAnnotations.canBeParameterized()); + } + } + + public void testCanBeParameterizedGenericMethod() { + BcelWorld world = new BcelWorld(); + world.setBehaveInJava5Way(true); + ResolvedType javaLangClass = world.resolve(UnresolvedType.forName("java.lang.Class")); + javaLangClass = javaLangClass.getGenericType(); + if (javaLangClass == null) return; // for < 1.5 + ResolvedMember[] methods = javaLangClass.getDeclaredMethods(); + ResolvedMember asSubclass = null; + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equals("asSubclass")) { + asSubclass = methods[i]; + break; + } + } + if (asSubclass != null) { // so can run on non-Java 5 +// System.out.println("got it"); + assertTrue(asSubclass.canBeParameterized()); + } + } + + public void testCanBeParameterizedMethodInGenericType() { + BcelWorld world = new BcelWorld(); + world.setBehaveInJava5Way(true); + ResolvedType javaUtilList = world.resolve(UnresolvedType.forName("java.util.List")); + javaUtilList = javaUtilList.getGenericType(); + if (javaUtilList == null) return; // for < 1.5 + ResolvedMember[] methods = javaUtilList.getDeclaredMethods(); + ResolvedMember add = null; + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equals("add")) { + add = methods[i]; + break; + } + } + if (add != null) { // so can run on non-Java 5 +// System.out.println("got it"); + assertTrue(add.canBeParameterized()); + } + } + /* + public void testGenericReferenceTypeCreation() { + UnresolvedType genericType = UnresolvedType.forGenericTypeSignature("Lorg/aspectj/weaver/MemberTestCase15$One;","Ljava/lang/Object;"); + assertEquals("Porg/aspectj/weaver/MemberTestCase15$One;",genericType.getSignature()); + assertEquals("Lorg/aspectj/weaver/MemberTestCase15$One;",genericType.getErasureSignature()); + } + + public void testMemberSignatureCreation() { + World world = new BcelWorld("../weaver5/bin/"); + //new ReflectionWorld(false, getClass().getClassLoader()); + world.setBehaveInJava5Way(true); + ResolvedType one = world.resolve("org.aspectj.weaver.MemberTestCase15$One"); + assertNotNull(one); + assertFalse(one.isMissing()); + + // Look at the methods on the parameterized type One + ResolvedMember member = findMethod("getter",one); + String erasedSignature = MemberImpl.typesToSignature(member.getReturnType(),member.getParameterTypes(),true); + assertEquals("()Ljava/lang/String;",erasedSignature); + String nonErasedSignature = MemberImpl.typesToSignature(member.getReturnType(),member.getParameterTypes(),false); + assertEquals("()Ljava/lang/String;",nonErasedSignature); + erasedSignature = MemberImpl.typesToSignature(member.getReturnType(),member.getParameterTypes(),true); + assertEquals("()Ljava/lang/String;",erasedSignature); + nonErasedSignature = MemberImpl.typesToSignature(member.getReturnType(),member.getParameterTypes(),false); + assertEquals("()Ljava/lang/String;",nonErasedSignature); + + member = findMethod("getterTwo",one); + erasedSignature = MemberImpl.typesToSignature(member.getReturnType(),member.getParameterTypes(),true); + assertEquals("()Ljava/util/List;",erasedSignature); + nonErasedSignature = MemberImpl.typesToSignature(member.getReturnType(),member.getParameterTypes(),false); + assertEquals("()Pjava/util/List;",nonErasedSignature); + nonErasedSignature = MemberImpl.typesToSignature(member.getGenericReturnType(),member.getGenericParameterTypes(),true); + assertEquals("()Ljava/util/List;",nonErasedSignature); + nonErasedSignature = MemberImpl.typesToSignature(member.getGenericReturnType(),member.getGenericParameterTypes(),false); + assertEquals("()Pjava/util/List;",nonErasedSignature); + + // Grab the generic type backing the parameterized type + ResolvedType oneGeneric = one.getGenericType(); + assertTrue(oneGeneric.isGenericType()); + member = findMethod("getterTwo",oneGeneric); + erasedSignature = MemberImpl.typesToSignature(member.getReturnType(),member.getParameterTypes(),true); + assertEquals("()Ljava/util/List;",erasedSignature); + erasedSignature = MemberImpl.typesToSignature(member.getReturnType(),member.getParameterTypes(),false); + assertEquals("()Ljava/util/List;",erasedSignature); + nonErasedSignature = MemberImpl.typesToSignature(member.getReturnType(),member.getParameterTypes(),false); + assertEquals("()Pjava/util/List;",nonErasedSignature); + nonErasedSignature = MemberImpl.typesToSignature(member.getGenericReturnType(),member.getGenericParameterTypes(),false); + assertEquals("()Ljava/util/List;",nonErasedSignature); + + + ResolvedType oneRaw = oneGeneric.getRawType(); + member = findMethod("getterTwo",oneRaw); + } + + private ResolvedMember findMethod(String name, ResolvedType type) { + ResolvedMember[] members = type.getDeclaredMethods(); + for (ResolvedMember member: members) { + if (member.getName().equals(name)) { + return member; + } + } + return null; + } + + // testcode + class One { + T t; + T getter() { + return null; + } + List getterTwo() { + return null; + } + } + */ +} diff --git a/weaver/src/test/java/org/aspectj/weaver/Member15TestCase.java b/weaver/src/test/java/org/aspectj/weaver/Member15TestCase.java new file mode 100644 index 000000000..3b37d5ba8 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/Member15TestCase.java @@ -0,0 +1,81 @@ +/* ******************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver; + +import org.aspectj.weaver.bcel.BcelWorld; + +import junit.framework.TestCase; + +/** + * @author colyer + * + */ +public class Member15TestCase extends TestCase { + + public void testCanBeParameterizedRegularMethod() { + BcelWorld world = new BcelWorld(); + ResolvedType javaLangClass = world.resolve(UnresolvedType.forName("java/lang/Class")); + ResolvedMember[] methods = javaLangClass.getDeclaredMethods(); + ResolvedMember getAnnotations = null; + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equals("getAnnotations")) { + getAnnotations = methods[i]; + break; + } + } + if (getAnnotations != null) { // so can run on non-Java 5 +// System.out.println("got it"); + assertFalse(getAnnotations.canBeParameterized()); + } + } + + public void testCanBeParameterizedGenericMethod() { + BcelWorld world = new BcelWorld(); + world.setBehaveInJava5Way(true); + ResolvedType javaLangClass = world.resolve(UnresolvedType.forName("java.lang.Class")); + javaLangClass = javaLangClass.getGenericType(); + if (javaLangClass == null) return; // for < 1.5 + ResolvedMember[] methods = javaLangClass.getDeclaredMethods(); + ResolvedMember asSubclass = null; + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equals("asSubclass")) { + asSubclass = methods[i]; + break; + } + } + if (asSubclass != null) { // so can run on non-Java 5 +// System.out.println("got it"); + assertTrue(asSubclass.canBeParameterized()); + } + } + + public void testCanBeParameterizedMethodInGenericType() { + BcelWorld world = new BcelWorld(); + world.setBehaveInJava5Way(true); + ResolvedType javaUtilList = world.resolve(UnresolvedType.forName("java.util.List")); + javaUtilList = javaUtilList.getGenericType(); + if (javaUtilList == null) return; // for < 1.5 + ResolvedMember[] methods = javaUtilList.getDeclaredMethods(); + ResolvedMember add = null; + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equals("add")) { + add = methods[i]; + break; + } + } + if (add != null) { // so can run on non-Java 5 +// System.out.println("got it"); + assertTrue(add.canBeParameterized()); + } + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/MemberTestCase.java b/weaver/src/test/java/org/aspectj/weaver/MemberTestCase.java new file mode 100644 index 000000000..c4755a3db --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/MemberTestCase.java @@ -0,0 +1,183 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * 2005 contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * Adrian Colyer, canBeParameterized tests + * ******************************************************************/ + +package org.aspectj.weaver; + +import java.lang.reflect.Modifier; + +import junit.framework.TestCase; + +import org.aspectj.testing.util.TestUtil; + +/** + * This is a test case for all the portions of Member that don't require a world. + */ +public class MemberTestCase extends TestCase { + + public MemberTestCase(String name) { + super(name); + } + + public void testMethodConstruction() { + Member s = TestUtils.methodFromString("void Foo.goo(int)"); + Member t = MemberImpl.method(UnresolvedType.forName("Foo"), 0, "goo", "(I)V"); + Member u = TestUtils.methodFromString("void Foo1.goo(int)"); + Member v = TestUtils.methodFromString("int Foo.goo(int)"); + + TestUtil.assertCommutativeEquals(s, s, true); + TestUtil.assertCommutativeEquals(t, t, true); + TestUtil.assertCommutativeEquals(u, u, true); + TestUtil.assertCommutativeEquals(v, v, true); + TestUtil.assertCommutativeEquals(s, t, true); + TestUtil.assertCommutativeEquals(s, u, false); + TestUtil.assertCommutativeEquals(s, v, false); + TestUtil.assertCommutativeEquals(t, u, false); + TestUtil.assertCommutativeEquals(t, v, false); + TestUtil.assertCommutativeEquals(u, v, false); + + s = TestUtils.fieldFromString("int Foo.goo"); + t = MemberImpl.field("Foo", 0, "goo", "I"); + u = TestUtils.fieldFromString("int Foo.goo1"); + v = TestUtils.fieldFromString("long Foo.goo"); + + TestUtil.assertCommutativeEquals(s, s, true); + TestUtil.assertCommutativeEquals(t, t, true); + TestUtil.assertCommutativeEquals(u, u, true); + TestUtil.assertCommutativeEquals(v, v, true); + TestUtil.assertCommutativeEquals(s, t, true); + TestUtil.assertCommutativeEquals(s, u, false); + TestUtil.assertCommutativeEquals(s, v, false); + TestUtil.assertCommutativeEquals(t, u, false); + TestUtil.assertCommutativeEquals(t, v, false); + TestUtil.assertCommutativeEquals(u, v, false); + } + + public void testMethodContents() { + Member m = TestUtils.methodFromString("void Foo.goo(int)"); + kindTest(m, Member.METHOD); + declaringTypeTest(m, "Foo"); + nameTest(m, "goo"); + parameterTypesTest(m, new UnresolvedType[] { UnresolvedType.INT }); + returnTypeTest(m, UnresolvedType.VOID); + isInterfaceTest(m, false); + isPrivateTest(m, false); + isConstructorTest(m, false); + isStaticTest(m, false); + + m = TestUtils.methodFromString("interface java.lang.Object java.util.Iterator.next()"); + kindTest(m, Member.METHOD); + declaringTypeTest(m, "java.util.Iterator"); + nameTest(m, "next"); + parameterTypesTest(m, UnresolvedType.NONE); + returnTypeTest(m, UnresolvedType.OBJECT); + isInterfaceTest(m, true); + isPrivateTest(m, false); + isConstructorTest(m, false); + isStaticTest(m, false); + + m = TestUtils.methodFromString("void Foo.(int, java.lang.Object)"); + kindTest(m, Member.CONSTRUCTOR); + declaringTypeTest(m, "Foo"); + nameTest(m, ""); + parameterTypesTest(m, new UnresolvedType[] { UnresolvedType.INT, UnresolvedType.OBJECT }); + returnTypeTest(m, UnresolvedType.VOID); + isInterfaceTest(m, false); + isPrivateTest(m, false); + isConstructorTest(m, true); + isStaticTest(m, false); + + m = TestUtils.methodFromString("private double Foo.sqrt(double)"); + kindTest(m, Member.METHOD); + declaringTypeTest(m, "Foo"); + nameTest(m, "sqrt"); + parameterTypesTest(m, new UnresolvedType[] { UnresolvedType.DOUBLE }); + returnTypeTest(m, UnresolvedType.DOUBLE); + isInterfaceTest(m, false); + isPrivateTest(m, true); + isConstructorTest(m, false); + isStaticTest(m, false); + + m = TestUtils.methodFromString("static int java.lang.Math.max(int, int)"); + kindTest(m, Member.METHOD); + declaringTypeTest(m, "java.lang.Math"); + nameTest(m, "max"); + parameterTypesTest(m, new UnresolvedType[] { UnresolvedType.INT, UnresolvedType.INT }); + returnTypeTest(m, UnresolvedType.INT); + isInterfaceTest(m, false); + isPrivateTest(m, false); + isConstructorTest(m, false); + isStaticTest(m, true); + } + + public void testFieldContents() { + Member m = TestUtils.fieldFromString("int Foo.goo"); + kindTest(m, Member.FIELD); + declaringTypeTest(m, "Foo"); + nameTest(m, "goo"); + parameterTypesTest(m, UnresolvedType.NONE); + returnTypeTest(m, UnresolvedType.INT); + isInterfaceTest(m, false); + isPrivateTest(m, false); + isConstructorTest(m, false); + isStaticTest(m, false); + + m = TestUtils.fieldFromString("static java.util.Iterator goo.Bar.i"); + kindTest(m, Member.FIELD); + declaringTypeTest(m, "goo.Bar"); + nameTest(m, "i"); + parameterTypesTest(m, UnresolvedType.NONE); + returnTypeTest(m, UnresolvedType.forName("java.util.Iterator")); + isInterfaceTest(m, false); + isPrivateTest(m, false); + isConstructorTest(m, false); + isStaticTest(m, true); + } + + private void isStaticTest(Member m, boolean b) { + assertEquals(m + " is static", b, Modifier.isStatic(m.getModifiers())); + } + + private void isConstructorTest(Member m, boolean b) { + assertEquals(m + " is constructor", b, m.getKind() == Member.CONSTRUCTOR); + } + + private void isPrivateTest(Member m, boolean b) { + assertEquals(m + " is private", b, Modifier.isPrivate(m.getModifiers())); + } + + private void isInterfaceTest(Member m, boolean b) { + assertEquals(m + " is interface", b, Modifier.isInterface(m.getModifiers())); + } + + private void returnTypeTest(Member m, UnresolvedType returnType) { + assertEquals(m + " return type", returnType, m.getReturnType()); + } + + private void parameterTypesTest(Member m, UnresolvedType[] paramTypes) { + TestUtil.assertArrayEquals(m + " parameters", paramTypes, m.getParameterTypes()); + } + + private void nameTest(Member m, String name) { + assertEquals(m + " name", name, m.getName()); + } + + private void declaringTypeTest(Member m, String declaringName) { + assertEquals(m + " declared in", UnresolvedType.forName(declaringName), m.getDeclaringType()); + } + + private void kindTest(Member m, MemberKind kind) { + assertEquals(m + " kind", kind, m.getKind()); + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/ParameterizedReferenceTypeTestCase.java b/weaver/src/test/java/org/aspectj/weaver/ParameterizedReferenceTypeTestCase.java new file mode 100644 index 000000000..8e04d8c53 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/ParameterizedReferenceTypeTestCase.java @@ -0,0 +1,84 @@ +/* ******************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver; + +import org.aspectj.weaver.bcel.BcelWorld; + +import junit.framework.TestCase; + +/** + * @author colyer + * For a parameterized reference type, the methods that return members + * - getDeclaredFields + * - getDeclaredMethods + * - getDeclaredInterfaces + * - getDeclaredPointcuts + * should have any type variables substituted by the given type parameter before + * being returned. + */ +public class ParameterizedReferenceTypeTestCase extends TestCase { + + BcelWorld world; + ReferenceType listOfString; + + public void testDeclaredMethodWithParameter() { + ResolvedMember[] methods = listOfString.getDeclaredMethods(); + ResolvedMember add = null; + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equals("add")) { + if (methods[i].getParameterTypes().length == 1) { + add = methods[i]; + System.out.println(add); +// j8: boolean java.util.List.add(java.lang.Object) +// break; + } + } + } + UnresolvedType parameterType = add.getParameterTypes()[0]; + assertEquals("Ljava/lang/String;",parameterType.getSignature()); + + ResolvedMember get = null; + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equals("get")) { + if (methods[i].getParameterTypes().length == 1) { + get = methods[i]; + break; + } + } + } + UnresolvedType returnType = get.getReturnType(); + assertEquals("Ljava/lang/String;",returnType.getSignature()); + + } + + public void testDeclaredMethodWithParameterizedReturnType() { + ResolvedMember[] methods = listOfString.getDeclaredMethods(); + ResolvedMember iterator = null; + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equals("iterator")) { + iterator = methods[i]; + break; + } + } + UnresolvedType returnType = iterator.getReturnType(); + assertEquals("Pjava/util/Iterator;",returnType.getSignature()); + + } + + protected void setUp() throws Exception { + super.setUp(); + world = new BcelWorld(); + world.setBehaveInJava5Way(true); + listOfString = (ReferenceType) + TypeFactory.createTypeFromSignature("Pjava/util/List;").resolve(world); + } +} diff --git a/weaver/src/test/java/org/aspectj/weaver/ReferenceTypeTestCase.java b/weaver/src/test/java/org/aspectj/weaver/ReferenceTypeTestCase.java new file mode 100644 index 000000000..5fbc530e7 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/ReferenceTypeTestCase.java @@ -0,0 +1,854 @@ +/* ******************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver; + +import java.rmi.RemoteException; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import junit.framework.TestCase; + +import org.aspectj.util.PartialOrder; +import org.aspectj.weaver.bcel.BcelWorld; + +// test cases for Adrian's additions to ReferenceType +// XXX - couldn't find any unit test cases for the rest of the ReferenceType class +public class ReferenceTypeTestCase extends TestCase { + + public void testIsRawTrue() { + BcelWorld world = new BcelWorld(); + world.setBehaveInJava5Way(true); + UnresolvedType javaLangClass = UnresolvedType.forName("java.lang.Class"); + ResolvedType rtx = world.resolve(javaLangClass); + assertTrue("Resolves to reference type", (rtx instanceof ReferenceType)); + ReferenceType rt = (ReferenceType) rtx; + assertTrue("java.lang.Class is raw", rt.isRawType()); + } + + public void testIsRawFalse() { + BcelWorld world = new BcelWorld(); + world.setBehaveInJava5Way(true); + UnresolvedType javaLangObject = UnresolvedType.forName("java.lang.Object"); + ResolvedType rtx = world.resolve(javaLangObject); + assertTrue("Resolves to reference type", (rtx instanceof ReferenceType)); + ReferenceType rt = (ReferenceType) rtx; + assertFalse("java.lang.Object is not raw", rt.isRawType()); + } + + public void testIsGenericTrue() { + BcelWorld world = new BcelWorld(); + world.setBehaveInJava5Way(true); + UnresolvedType javaLangClass = UnresolvedType.forName("java.lang.Class"); + ResolvedType rtx = world.resolve(javaLangClass); + assertTrue("java.lang.Class has underpinning generic type", rtx.getGenericType().isGenericType()); + } + + public void testIsGenericFalse() { + BcelWorld world = new BcelWorld(); + world.setBehaveInJava5Way(true); + UnresolvedType javaLangObject = UnresolvedType.forName("java.lang.Object"); + ResolvedType rtx = world.resolve(javaLangObject); + assertFalse(rtx.isGenericType()); + } + + BcelWorld world; + + @Override + public void setUp() throws Exception { + super.setUp(); + world = new BcelWorld(); + world.setBehaveInJava5Way(true); + } + + public void testCoercion01() { + ReferenceType listOfString = (ReferenceType) world.resolve(UnresolvedType + .forSignature("Pjava/util/List;")); + ReferenceType listOfInteger = (ReferenceType) world.resolve(UnresolvedType + .forSignature("Pjava/util/List;")); + assertFalse(listOfInteger.isAssignableFrom(listOfString)); + assertFalse(listOfString.isAssignableFrom(listOfInteger)); + assertFalse(listOfInteger.isCoerceableFrom(listOfString)); + assertFalse(listOfString.isCoerceableFrom(listOfInteger)); + } + + public void testAssignable01() { + List list = new ArrayList(); + List listOfString = new ArrayList(); + List listOfSomething = new ArrayList(); + List listOfSomethingNumberish = new ArrayList(); + List listOfSomethingSuperDouble = new ArrayList(); + // interfaces too List + + ReferenceType ajList = resolve("Ljava/util/List;"); + ReferenceType ajListOfString = resolve("Pjava/util/List;"); + ReferenceType ajListOfSomething = resolve("Pjava/util/List<*>;"); + ReferenceType ajListOfSomethingNumberish = resolve("Pjava/util/List<+Ljava/lang/Number;>;"); + ReferenceType ajListOfSomethingSuperDouble = resolve("Pjava/util/List<-Ljava/lang/Double;>;"); + + // try and write the java equivalent, if it succeeds then check isAssignableFrom() is true + // if the java is only correct with a cast, check isCoerceableFrom() + list = listOfString; + assertTrue(ajList.isAssignableFrom(ajListOfString)); + list = listOfSomething; + assertTrue(ajList.isAssignableFrom(ajListOfSomething)); + list = listOfSomethingNumberish; + assertTrue(ajList.isAssignableFrom(ajListOfSomething)); + list = listOfSomethingSuperDouble; + assertTrue(ajList.isAssignableFrom(ajListOfSomethingSuperDouble)); + + listOfString = list; // unchecked conversion to List + assertFalse(ajListOfString.isAssignableFrom(ajList)); + assertTrue(ajListOfString.isCoerceableFrom(ajListOfSomething)); + // error: listOfString = listOfSomething; + assertFalse(ajListOfString.isAssignableFrom(ajListOfSomething)); + // error: listOfString = listOfSomethingNumberish; + assertFalse(ajListOfString.isAssignableFrom(ajListOfSomethingNumberish)); + // error: listOfString = listOfSomethingSuperDouble; + assertFalse(ajListOfString.isAssignableFrom(ajListOfSomethingSuperDouble)); + // error: listOfString = (List) listOfSomethingSuperDouble; + assertFalse(ajListOfString.isCoerceableFrom(ajListOfSomethingSuperDouble)); + + listOfSomething = list; + assertTrue(ajListOfSomething.isAssignableFrom(ajList)); + listOfSomething = listOfString; + assertTrue(ajListOfSomething.isAssignableFrom(ajListOfString)); + listOfSomething = listOfSomethingNumberish; + assertTrue(ajListOfSomething.isAssignableFrom(ajListOfSomething)); + listOfSomething = listOfSomethingSuperDouble; + assertTrue(ajListOfSomething.isAssignableFrom(ajListOfSomethingSuperDouble)); + + listOfSomethingNumberish = list; // unchecked conversion + assertFalse(ajListOfSomethingNumberish.isAssignableFrom(ajList)); + assertTrue(ajListOfSomethingNumberish.isCoerceableFrom(ajList)); + // error: listOfSomethingNumberish = listOfString; + assertFalse(ajListOfSomethingNumberish.isAssignableFrom(ajListOfString)); + assertFalse(ajListOfSomethingNumberish.isCoerceableFrom(ajListOfString)); + // error: listOfSomethingNumberish = listOfSomething; + assertFalse(ajListOfSomethingNumberish.isAssignableFrom(ajListOfSomething)); + listOfSomethingNumberish = (List) listOfSomething; + assertTrue(ajListOfSomethingNumberish.isCoerceableFrom(ajListOfSomething)); + // error: listOfSomethingNumberish = listOfSomethingSuperDouble; + assertFalse(ajListOfSomethingNumberish.isAssignableFrom(ajListOfSomethingSuperDouble)); + // listOfSomethingNumberish = (List) listOfSomethingSuperDouble; + // assertTrue(ajListOfSomethingNumberish.isCoerceableFrom(ajListOfSomethingSuperDouble)); + } + + class C { + void m1(List e) { + } + + void m2(List e) { + } + + void m3(List e) { + } + + void m4(List e) { + } + + void m5(List e) { + } + + void m6(List e) { + } + + void m7(List> e) { + } + + void m8(List e) { + } + + void m9(E e) { + } + } + + class A1 { + } + + class B1 extends A1 { + } + + class C1 extends B1 { + } + + class D1 extends C1 { + } + + class D2 { + void m5(List e) { + } + } + + public void testAssignable02() { + List list = new ArrayList(); + ArrayList arraylist = null; + List listOfString = new ArrayList(); + List listOfSomething = new ArrayList(); + ArrayList arrayListOfSomething = null; + List listOfNumber = null; + ArrayList arrayListOfNumber = null; + ArrayList arrayListOfSomethingNumberish = null; + List listOfSomethingNumberish = new ArrayList(); + List listOfSomethingSuperDouble = new ArrayList(); + List listOfInteger = new ArrayList(); + ArrayList arrayListOfString; + ArrayList arraylistOfInteger; + // interfaces too List + + ReferenceType ajArrayListOfString = resolve("Pjava/util/ArrayList;"); + ReferenceType ajArrayListOfInteger = resolve("Pjava/util/ArrayList;"); + ReferenceType ajArrayListOfNumber = resolve("Pjava/util/ArrayList;"); + ReferenceType ajArrayListOfSomethingNumberish = resolve("Pjava/util/ArrayList<+Ljava/lang/Number;>;"); + ReferenceType ajList = resolve("Ljava/util/List;"); + ReferenceType ajArrayList = resolve("Ljava/util/ArrayList;"); + ReferenceType ajListOfString = resolve("Pjava/util/List;"); + ReferenceType ajListOfSomething = resolve("Pjava/util/List<*>;"); + ReferenceType ajArrayListOfSomething = resolve("Pjava/util/ArrayList<*>;"); + ReferenceType ajListOfSomethingNumberish = resolve("Pjava/util/List<+Ljava/lang/Number;>;"); + ReferenceType ajListOfSomethingSuperDouble = resolve("Pjava/util/List<-Ljava/lang/Double;>;"); + ReferenceType ajListOfInteger = resolve("Pjava/util/List;"); + ReferenceType ajListOfNumber = resolve("Pjava/util/List;"); + // Effectively, whether the advice matches is based on whether what we pass at the joinpoint could + // be bound to the specification in the args() pointcut + + // void around(): execution(* C.m1(..)) && args(List){} //: Should match (it does) + assertTrue(ajListOfInteger.isAssignableFrom(ajListOfInteger)); + + // void around(): execution(* C.m1(..)) && args(ArrayList){}//: Should runtime check (it does!) + ArrayList x = (ArrayList) listOfInteger; + assertFalse(ajArrayListOfInteger.isAssignableFrom(ajListOfInteger)); + assertTrue(ajArrayListOfInteger.isCoerceableFrom(ajListOfInteger)); + + // void around(): execution(* C.m1(..)) && args(List){} // Should not match (it does not!) + // error: listOfNumber = listOfInteger; + assertFalse(ajListOfNumber.isAssignableFrom(ajListOfInteger)); + assertFalse(ajListOfNumber.isCoerceableFrom(ajListOfInteger)); + + // void around(): execution(* C.m1(..)) && args(ArrayList){} // Should not match (it does not) + // error: arrayListOfNumber = listOfInteger; + assertFalse(ajArrayListOfNumber.isAssignableFrom(ajListOfInteger)); + assertFalse(ajArrayListOfNumber.isCoerceableFrom(ajListOfInteger)); + + // void around(): execution(* C.m1(..)) && args(List){} // Should match (it does) + listOfSomethingNumberish = listOfInteger; + assertTrue(ajListOfSomethingNumberish.isAssignableFrom(ajListOfInteger)); + + // void around(): execution(* C.m1(..)) && args(ArrayList){}// Should runtime check (it does!) + arrayListOfSomethingNumberish = (ArrayList) listOfInteger; + assertFalse(ajArrayListOfSomethingNumberish.isAssignableFrom(ajListOfInteger)); + assertTrue(ajArrayListOfSomethingNumberish.isCoerceableFrom(ajListOfInteger)); + + // void around(): execution(* C.m1(..)) && args(List){}// Should match (it does) + list = listOfInteger; + assertTrue(ajList.isAssignableFrom(ajListOfInteger)); + + // void around(): execution(* C.m1(..)) && args(ArrayList){}//: Should runtime check (it does not match!) + arraylist = (ArrayList) listOfInteger; + assertFalse(ajArrayList.isAssignableFrom(ajListOfInteger)); + assertTrue(ajArrayList.isCoerceableFrom(ajListOfInteger)); + + // void around(): execution(* C.m1(..)) && args(List){}// Should match (it does) + listOfSomething = listOfInteger; + assertTrue(ajListOfSomething.isAssignableFrom(ajListOfInteger)); + + // void around(): execution(* C.m1(..)) && args(ArrayList){}// Should runtime check (it does not match!) + arrayListOfSomething = (ArrayList) listOfInteger; + assertFalse(ajArrayListOfSomething.isAssignableFrom(ajListOfInteger)); + assertTrue(ajArrayListOfSomething.isCoerceableFrom(ajListOfInteger)); + + // void around(): execution(* C.m1(..)) && args(ArrayList){}// Should not match (it does not match!) + // error: arrayListOfString = listOfInteger; + assertFalse(ajArrayListOfString.isAssignableFrom(ajListOfInteger)); + assertFalse(ajArrayListOfString.isCoerceableFrom(ajListOfInteger)); + } + + public void testAssignable03_method_m2() { + List list = new ArrayList(); + ArrayList arraylist = null; + List listOfString = new ArrayList(); + List listOfSomething = new ArrayList(); + ArrayList arrayListOfSomething = null; + List listOfNumber = null; + ArrayList arrayListOfNumber = null; + ArrayList arrayListOfInteger = null; + ArrayList arrayListOfSomethingNumberish = null; + List listOfSomethingNumberish = new ArrayList(); + List listOfSomethingSuperDouble = new ArrayList(); + List listOfInteger = new ArrayList(); + ArrayList arrayListOfString; + ArrayList arraylistOfInteger; + // interfaces too List + + ReferenceType ajArrayListOfString = resolve("Pjava/util/ArrayList;"); + ReferenceType ajArrayListOfInteger = resolve("Pjava/util/ArrayList;"); + ReferenceType ajArrayListOfNumber = resolve("Pjava/util/ArrayList;"); + ReferenceType ajArrayListOfSomethingNumberish = resolve("Pjava/util/ArrayList<+Ljava/lang/Number;>;"); + ReferenceType ajList = resolve("Ljava/util/List;"); + ReferenceType ajArrayList = resolve("Ljava/util/ArrayList;"); + ReferenceType ajListOfString = resolve("Pjava/util/List;"); + ReferenceType ajListOfSomething = resolve("Pjava/util/List<*>;"); + ReferenceType ajArrayListOfSomething = resolve("Pjava/util/ArrayList<*>;"); + ReferenceType ajListOfSomethingNumberish = resolve("Pjava/util/List<+Ljava/lang/Number;>;"); + ReferenceType ajListOfSomethingSuperDouble = resolve("Pjava/util/List<-Ljava/lang/Double;>;"); + ReferenceType ajListOfInteger = resolve("Pjava/util/List;"); + ReferenceType ajListOfNumber = resolve("Pjava/util/List;"); + + // void m2(List e) {} + + // comment 11 + // void around(): execution(* C.m2(..)) && args(List){} //: Should not match (but it does) ERROR + listOfInteger = (List) listOfSomethingNumberish; + assertFalse(ajListOfInteger.isAssignableFrom(ajListOfSomethingNumberish)); + assertTrue(ajListOfInteger.isCoerceableFrom(ajListOfSomethingNumberish)); + + // void around(): execution(* C.m2(..)) && args(ArrayList){}//: Should not match (but it does!) ERROR + arrayListOfInteger = (ArrayList) listOfSomethingNumberish; + assertFalse(ajArrayListOfInteger.isAssignableFrom(ajListOfSomethingNumberish)); + assertTrue(ajArrayListOfInteger.isCoerceableFrom(ajListOfSomethingNumberish)); + + // void around(): execution(* C.m2(..)) && args(List){} //: Should not match (but it does) ERROR + listOfNumber = (List) listOfSomethingNumberish; + assertFalse(ajListOfNumber.isAssignableFrom(ajListOfSomethingNumberish)); + assertTrue(ajListOfNumber.isCoerceableFrom(ajListOfSomethingNumberish)); + + // void around(): execution(* C.m2(..)) && args(ArrayList){}//: Should not runtime check (but it does!) ERROR + arrayListOfNumber = (ArrayList) listOfSomethingNumberish; + assertFalse(ajArrayListOfNumber.isAssignableFrom(ajListOfSomethingNumberish)); + assertTrue(ajArrayListOfNumber.isCoerceableFrom(ajListOfSomethingNumberish)); + + // void around(): execution(* C.m2(..)) && args(List){}//: Should match (it does) + listOfSomethingNumberish = listOfSomethingNumberish; + assertTrue(ajListOfSomethingNumberish.isAssignableFrom(ajListOfSomethingNumberish)); + + // void around(): execution(* C.m2(..)) && args(ArrayList){}//: Should runtime check (it does!) + arrayListOfSomethingNumberish = (ArrayList) listOfSomethingNumberish; + assertFalse(ajArrayListOfSomethingNumberish.isAssignableFrom(ajListOfSomethingNumberish)); + assertTrue(ajArrayListOfSomethingNumberish.isCoerceableFrom(ajListOfSomethingNumberish)); + + // void around(): execution(* C.m2(..)) && args(List){}//: Should match (it does) + list = listOfSomethingNumberish; + assertTrue(ajList.isAssignableFrom(ajListOfSomethingNumberish)); + + // void around(): execution(* C.m2(..)) && args(ArrayList){}//: Should runtime check (it does not match!) ERROR + arraylist = (ArrayList) listOfSomethingNumberish; + assertFalse(ajArrayList.isAssignableFrom(ajListOfSomethingNumberish)); + assertTrue(ajArrayList.isCoerceableFrom(ajListOfSomethingNumberish)); + + // void around(): execution(* C.m2(..)) && args(List){}//: Should match (it does) + listOfSomething = listOfSomethingNumberish; + assertTrue(ajListOfSomething.isAssignableFrom(ajListOfSomethingNumberish)); + + // void around(): execution(* C.m2(..)) && args(ArrayList){}//: Should runtime check (it does!) + arrayListOfSomething = (ArrayList) listOfSomethingNumberish; + assertFalse(ajArrayListOfSomething.isAssignableFrom(ajListOfSomethingNumberish)); + assertTrue(ajArrayListOfSomething.isCoerceableFrom(ajListOfSomethingNumberish)); + + // void around(): execution(* C.m2(..)) && args(ArrayList){}//: Should not match (it does not match!) + // error: arrayListOfString = listOfSomethingNumberish; + assertFalse(ajArrayListOfString.isAssignableFrom(ajListOfSomethingNumberish)); + assertFalse(ajArrayListOfString.isCoerceableFrom(ajListOfSomethingNumberish)); + } + + public void testAssignable04_method_m3() { + List list = new ArrayList(); + ArrayList arraylist = null; + List listOfString = new ArrayList(); + List listOfSomething = new ArrayList(); + ArrayList arrayListOfSomething = null; + List listOfNumber = null; + ArrayList arrayListOfNumber = null; + ArrayList arrayListOfInteger = null; + ArrayList arrayListOfSomethingNumberish = null; + List listOfSomethingNumberish = new ArrayList(); + List listOfSomethingSuperDouble = new ArrayList(); + List listOfInteger = new ArrayList(); + ArrayList arrayList = null; + ArrayList arrayListOfString; + ArrayList arraylistOfInteger; + // interfaces too List + + ReferenceType ajArrayListOfString = resolve("Pjava/util/ArrayList;"); + ReferenceType ajArrayListOfInteger = resolve("Pjava/util/ArrayList;"); + ReferenceType ajArrayListOfNumber = resolve("Pjava/util/ArrayList;"); + ReferenceType ajArrayListOfSomethingNumberish = resolve("Pjava/util/ArrayList<+Ljava/lang/Number;>;"); + ReferenceType ajList = resolve("Ljava/util/List;"); + ReferenceType ajArrayList = resolve("Ljava/util/ArrayList;"); + ReferenceType ajListOfString = resolve("Pjava/util/List;"); + ReferenceType ajListOfSomething = resolve("Pjava/util/List<*>;"); + ReferenceType ajArrayListOfSomething = resolve("Pjava/util/ArrayList<*>;"); + ReferenceType ajListOfSomethingNumberish = resolve("Pjava/util/List<+Ljava/lang/Number;>;"); + ReferenceType ajListOfSomethingSuperDouble = resolve("Pjava/util/List<-Ljava/lang/Double;>;"); + ReferenceType ajListOfInteger = resolve("Pjava/util/List;"); + ReferenceType ajListOfNumber = resolve("Pjava/util/List;"); + + // void m3(List e) { } + + // void around(): execution(* C.m3(..)) && args(List){} //: Should not match (it does not) + // error: listOfInteger = listOfNumber; + assertFalse(ajListOfInteger.isAssignableFrom(ajListOfNumber)); + assertFalse(ajListOfInteger.isCoerceableFrom(ajListOfNumber)); + + // void around(): execution(* C.m3(..)) && args(ArrayList){}//: Should not match (it does not) + // error: arrayListOfInteger = listOfNumber; + assertFalse(ajArrayListOfInteger.isAssignableFrom(ajListOfNumber)); + assertFalse(ajArrayListOfInteger.isCoerceableFrom(ajListOfNumber)); + + // void around(): execution(* C.m3(..)) && args(List){}//: Should match (it does) + listOfNumber = listOfNumber; + assertTrue(ajListOfNumber.isAssignableFrom(ajListOfNumber)); + + // void around(): execution(* C.m3(..)) && args(ArrayList){}//: Should runtime match (it does) + arrayListOfNumber = (ArrayList) listOfNumber; + assertFalse(ajArrayListOfNumber.isAssignableFrom(ajListOfNumber)); + assertTrue(ajArrayListOfNumber.isCoerceableFrom(ajListOfNumber)); + + // void around(): execution(* C.m3(..)) && args(List){}//: Should match (it does) + listOfSomethingNumberish = listOfNumber; + assertTrue(ajListOfSomethingNumberish.isAssignableFrom(ajListOfNumber)); + + // void around(): execution(* C.m3(..)) && args(ArrayList){}//: Should runtime check (it does!) + arrayListOfSomethingNumberish = (ArrayList) listOfNumber; + assertFalse(ajArrayListOfSomethingNumberish.isAssignableFrom(ajListOfNumber)); + assertTrue(ajArrayListOfSomethingNumberish.isCoerceableFrom(ajListOfNumber)); + + // void around(): execution(* C.m3(..)) && args(List){}//: Should match (it does) + list = listOfNumber; + assertTrue(ajList.isAssignableFrom(ajListOfNumber)); + + // void around(): execution(* C.m3(..)) && args(ArrayList){}//: Should runtime check (it does not match!) ERROR + arrayList = (ArrayList) listOfNumber; + assertFalse(ajArrayList.isAssignableFrom(ajListOfNumber)); + assertTrue(ajArrayList.isCoerceableFrom(ajListOfNumber)); + + // void around(): execution(* C.m3(..)) && args(List){}//: Should match (it does) + listOfSomething = listOfNumber; + assertTrue(ajListOfSomething.isAssignableFrom(ajListOfNumber)); + + // void around(): execution(* C.m3(..)) && args(ArrayList){}//: Should runtime check (it does!) + arrayListOfSomething = (ArrayList) listOfNumber; + assertFalse(ajArrayListOfSomething.isAssignableFrom(ajListOfNumber)); + assertTrue(ajArrayListOfSomething.isCoerceableFrom(ajListOfNumber)); + + // void around(): execution(* C.m3(..)) && args(ArrayList){}//: Should not match (it does not match!) + // error: arrayListOfString = listOfNumber; + assertFalse(ajArrayListOfString.isAssignableFrom(ajListOfNumber)); + assertFalse(ajArrayListOfString.isCoerceableFrom(ajListOfNumber)); + + } + + static class ClassA { + } + + static interface IMarker { + } + + static class ClassB implements IMarker> { + } + + static class ClassC implements IMarker { + } + + public void testAssignability_pr267559() { + ClassB cb = new ClassB(); + ClassB cb2 = new ClassB(); + + ReferenceType rcb = resolve("Lorg/aspectj/weaver/ReferenceTypeTestCase$ClassB;"); + ReferenceType rcb2 = resolve("Lorg/aspectj/weaver/ReferenceTypeTestCase$ClassB;"); + boolean b = rcb.isAssignableFrom(rcb2); + assertTrue(b); + b = rcb2.isAssignableFrom(rcb); + assertTrue(b); + + rcb = resolve("Porg/aspectj/weaver/ReferenceTypeTestCase$IMarker;>;"); + rcb2 = resolve("Lorg/aspectj/weaver/ReferenceTypeTestCase$ClassB;"); + b = rcb.isAssignableFrom(rcb2); + assertTrue(b); + } + + public void testAssignable03_method_m4() { + List list = new ArrayList(); + ArrayList arraylist = null; + List listOfString = new ArrayList(); + List listOfSomething = new ArrayList(); + ArrayList arrayListOfSomething = null; + List listOfNumber = null; + ArrayList arrayListOfNumber = null; + ArrayList arrayListOfSomethingNumberish = null; + List listOfSomethingNumberish = new ArrayList(); + List listOfSomethingSuperDouble = new ArrayList(); + List listOfInteger = new ArrayList(); + ArrayList arrayListOfString; + ArrayList arraylistOfInteger; + // interfaces too List + + ReferenceType ajArrayListOfString = resolve("Pjava/util/ArrayList;"); + ReferenceType ajArrayListOfInteger = resolve("Pjava/util/ArrayList;"); + ReferenceType ajArrayListOfNumber = resolve("Pjava/util/ArrayList;"); + ReferenceType ajArrayListOfSomethingNumberish = resolve("Pjava/util/ArrayList<+Ljava/lang/Number;>;"); + ReferenceType ajList = resolve("Ljava/util/List;"); + ReferenceType ajArrayList = resolve("Ljava/util/ArrayList;"); + ReferenceType ajListOfString = resolve("Pjava/util/List;"); + ReferenceType ajListOfSomething = resolve("Pjava/util/List<*>;"); + ReferenceType ajArrayListOfSomething = resolve("Pjava/util/ArrayList<*>;"); + ReferenceType ajListOfSomethingNumberish = resolve("Pjava/util/List<+Ljava/lang/Number;>;"); + ReferenceType ajListOfSomethingSuperDouble = resolve("Pjava/util/List<-Ljava/lang/Double;>;"); + ReferenceType ajListOfInteger = resolve("Pjava/util/List;"); + ReferenceType ajListOfNumber = resolve("Pjava/util/List;"); + + // void m4(List e) {} + + // void around(): execution(* C.m4(..)) && args(List){} //: Should match with unchecked warning + listOfInteger = (List) listOfSomething; + assertFalse(ajListOfInteger.isAssignableFrom(ajListOfSomething)); + assertTrue(ajListOfInteger.isCoerceableFrom(ajListOfSomething)); + + // void around(): execution(* C.m4(..)) && args(ArrayList){} // Should match with unchecked warning + arraylistOfInteger = (ArrayList) listOfSomething; + assertFalse(ajArrayListOfInteger.isAssignableFrom(ajListOfSomething)); + assertTrue(ajArrayListOfInteger.isCoerceableFrom(ajListOfSomething)); + + // void around(): execution(* C.m4(..)) && args(List){} // Should match with unchecked warning + listOfNumber = (List) listOfSomething; + assertFalse(ajListOfNumber.isAssignableFrom(ajListOfSomething)); + assertTrue(ajListOfNumber.isCoerceableFrom(ajListOfSomething)); + + // void around(): execution(* C.m4(..)) && args(ArrayList){} // Should match with unchecked warning + arrayListOfNumber = (ArrayList) listOfSomething; + assertFalse(ajArrayListOfNumber.isAssignableFrom(ajListOfSomething)); + assertTrue(ajArrayListOfNumber.isCoerceableFrom(ajListOfSomething)); + + // void around(): execution(* C.m4(..)) && args(List){} // Should match with unchecked warning + listOfSomethingNumberish = (List) listOfSomething; + assertFalse(ajListOfSomethingNumberish.isAssignableFrom(ajListOfSomething)); + assertTrue(ajListOfSomethingNumberish.isCoerceableFrom(ajListOfSomething)); + + // void around(): execution(* C.m4(..)) && args(ArrayList){} // Should match with unchecked warning + arrayListOfSomethingNumberish = (ArrayList) listOfSomething; + assertFalse(ajArrayListOfSomethingNumberish.isAssignableFrom(ajListOfSomething)); + assertTrue(ajArrayListOfSomethingNumberish.isCoerceableFrom(ajListOfSomething)); + + // void around(): execution(* C.m4(..)) && args(List){} // Should match + list = listOfSomething; + assertTrue(ajList.isAssignableFrom(ajListOfSomething)); + + // void around(): execution(* C.m4(..)) && args(ArrayList){} // Should runtime check + arraylist = (ArrayList) listOfSomething; + assertFalse(ajArrayList.isAssignableFrom(ajListOfSomething)); + assertTrue(ajArrayList.isCoerceableFrom(ajListOfSomething)); + + // void around(): execution(* C.m4(..)) && args(List){}//: Should match + list = listOfSomething; + assertTrue(ajList.isAssignableFrom(ajListOfSomething)); + + // void around(): execution(* C.m4(..)) && args(ArrayList){} // Should runtime check + arrayListOfSomething = (ArrayList) listOfSomething; + assertFalse(ajArrayListOfSomething.isAssignableFrom(ajListOfSomething)); + assertTrue(ajArrayListOfSomething.isCoerceableFrom(ajListOfSomething)); + + // void around(): execution(* C.m4(..)) && args(ArrayList){} // Should match with unchecked warning + arrayListOfString = (ArrayList) listOfSomething; + assertFalse(ajArrayListOfString.isAssignableFrom(ajListOfSomething)); + assertTrue(ajArrayListOfString.isCoerceableFrom(ajListOfSomething)); + } + + // copy of the real one in BcelClassWeaver + public static class IfaceInitList implements PartialOrder.PartialComparable { + final ResolvedType onType; + List list = new ArrayList(); + + IfaceInitList(ResolvedType onType) { + this.onType = onType; + } + + public int compareTo(Object other) { + IfaceInitList o = (IfaceInitList) other; + if (onType.isAssignableFrom(o.onType)) { + return +1; + } else if (o.onType.isAssignableFrom(onType)) { + return -1; + } else { + return 0; + } + } + + public int fallbackCompareTo(Object other) { + return 0; + } + } + + public void testExpensiveAssignableChecks_309336() { + List objects = new ArrayList(); + ReferenceType rcb = resolve("Lorg/aspectj/weaver/ReferenceTypeTestCase$Foo;"); + ReferenceType i = (ReferenceType) rcb.getDeclaredInterfaces()[0]; + while (i != null && i.isInterface()) { + objects.add(Math.abs(new Random(12).nextInt(objects.size() + 1)), new IfaceInitList(i)); + ResolvedType[] rt = i.getDeclaredInterfaces(); + i = rt == null || rt.length == 0 ? null : (ReferenceType) rt[0]; + } + for (int loop = 0; loop < 10; loop++) { + // ReferenceType.r = 0; + long stime = System.nanoTime(); + for (int j = 0; j < 10; j++) { + List objects2 = new ArrayList(); + objects2.addAll(objects); + PartialOrder.sort(objects2); + } + long etime = System.nanoTime(); + System.err.println("Took " + ((etime - stime) / 1000000) + "ms: calls ");// + ReferenceType.r); + } + // could do with asserting something... basically we are just checking we didn't run out of memory doing the sorts above! + } + + public interface Operator14 { + T execute(String aArg) throws E1, E2, E3, E4, E5, E6, E7, E8, E9, E10, E11, E12, E13, E14, RemoteException; + } + + public interface Operator13 + extends Operator14 { + } + + public interface Operator12 + extends Operator13 { + } + + public interface Operator11 + extends Operator12 { + } + + public interface Operator10 + extends Operator11 { + + } + + public interface Operator9 + extends Operator10 { + } + + public interface Operator8 + extends Operator9 { + } + + public interface Operator7 + extends Operator8 { + } + + public interface Operator6 + extends Operator7 { + + } + + public interface Operator5 + extends Operator6 { + } + + public interface Operator4 extends + Operator5 { + } + + public interface Operator3 extends + Operator4 { + } + + public interface Operator2 extends Operator3 { + + } + + public interface Operator1 extends Operator2 { + } + + public interface Operator extends Operator1 { + } + + class Foo implements Operator { + public String execute(String aArg) throws NullPointerException, RemoteException { + System.out.println("Doh!"); + return aArg; + } + } + + // public void testAssignable_method_m5() { + // List list = new ArrayList(); + // ArrayList arraylist = null; + // List listOfString = new ArrayList(); + // List listOfSomething = new ArrayList(); + // ArrayList arrayListOfSomething = null; + // List listOfNumber = null; + // ArrayList arrayListOfNumber = null; + // ArrayList arrayListOfSomethingNumberish = null; + // List listOfSomethingNumberish = new ArrayList(); + // List listOfSomethingSuperDouble = new ArrayList(); + // List listOfInteger = new ArrayList(); + // ArrayList arrayListOfString; + // ArrayList arraylistOfInteger; + // // interfaces too List + // + // ReferenceType ajArrayListOfString = resolve("Pjava/util/ArrayList;"); + // ReferenceType ajArrayListOfInteger = resolve("Pjava/util/ArrayList;"); + // ReferenceType ajArrayListOfNumber = resolve("Pjava/util/ArrayList;"); + // ReferenceType ajArrayListOfSomethingNumberish = resolve("Pjava/util/ArrayList<+Ljava/lang/Number;>;"); + // ReferenceType ajList = resolve("Ljava/util/List;"); + // ReferenceType ajArrayList = resolve("Ljava/util/ArrayList;"); + // ReferenceType ajListOfString = resolve("Pjava/util/List;"); + // ReferenceType ajListOfSomething = resolve("Pjava/util/List<*>;"); + // ReferenceType ajArrayListOfSomething = resolve("Pjava/util/ArrayList<*>;"); + // ReferenceType ajListOfSomethingNumberish = resolve("Pjava/util/List<+Ljava/lang/Number;>;"); + // ReferenceType ajListOfSomethingSuperDouble = resolve("Pjava/util/List<-Ljava/lang/Double;>;"); + // ReferenceType ajListOfInteger = resolve("Pjava/util/List;"); + // ReferenceType ajListOfNumber = resolve("Pjava/util/List;"); + // ReferenceType ajListOfEextendsNumber = resolve("Pjava/util/List<+TE") + // + // // class C { + // // void m5(List e) { } + // // + // // void around(): execution(* C.m5(..)) && args(List){} Should not match (but it does) ERROR + // + // // void around(): execution(* C.m5(..)) && args(ArrayList){}//: Should not match (but it does!) ERROR + // // void around(): execution(* C.m5(..)) && args(List){}//: Should not match (but it does!) ERROR + // // void around(): execution(* C.m5(..)) && args(ArrayList){}//: Should not match (it does) ERROR + // // void around(): execution(* C.m5(..)) && args(List){}//: Should match (it does) + // // void around(): execution(* C.m5(..)) && args(ArrayList){}//: Should runtime check (it does!) + // // void around(): execution(* C.m5(..)) && args(List){}//: Should match (it does) + // // void around(): execution(* C.m5(..)) && args(ArrayList){}//: Should runtime check (it does not match!) ERROR + // // void around(): execution(* C.m5(..)) && args(List){}//: Should match (it does) + // // void around(): execution(* C.m5(..)) && args(ArrayList){}//: Should runtime check (it does not match!) + // // void around(): execution(* C.m5(..)) && args(ArrayList){}//: Should not match (it does not match!) + // // + // // // void around(): execution(* D2.m5(..)) && args(List){} //: Should + // // not match (but it does) ERROR + // // // void around(): execution(* D2.m5(..)) && args(ArrayList){}//: + // // Should not match (but it does!) ERROR + // // // void around(): execution(* D2.m5(..)) && args(List){}//: Should + // // not match (but it does!) ERROR + // // // void around(): execution(* D2.m5(..)) && args(ArrayList){}//: + // // Should not match (it does) ERROR + // // // void around(): execution(* D2.m5(..)) && args(List){}//: + // // Should match (it does) + // // // void around(): execution(* D2.m5(..)) && args(ArrayList){}//: Should runtime check (it does!) + // // // void around(): execution(* D2.m5(..)) && args(List){}//: + // // Should match (it does) + // // // void around(): execution(* D2.m5(..)) && args(ArrayList){}//: Should runtime check (it does!) + // // // void around(): execution(* D2.m5(..)) && args(List){}//: Should match + // // (it does) + // // // void around(): execution(* D2.m5(..)) && args(ArrayList){}//: Should + // // runtime check (it does not match!) ERROR + // // // void around(): execution(* D2.m5(..)) && args(List){}//: Should + // // match (it does) + // // // void around(): execution(* D2.m5(..)) && args(ArrayList){}//: + // // Should runtime check (it does not match!) + // // // void around(): execution(* D2.m5(..)) && args(ArrayList){}//: + // // Should not match (it does not match!) + // // + // // // void around(): execution(* C.m6(..)) && args(List){} //: + // // Should not match (but it does) ERROR + // // // void around(): execution(* C.m6(..)) && args(ArrayList){}//: + // // Should not match (but it does!) ERROR + // // // void around(): execution(* C.m6(..)) && args(List){}//: Should + // // not match (but it does!) ERROR + // // // void around(): execution(* C.m6(..)) && args(ArrayList){}//: + // // Should not match (it does) ERROR + // // // void around(): execution(* C.m6(..)) && args(List){}//: Should match (it does) + // // // void around(): execution(* C.m6(..)) && args(ArrayList){}//: Should runtime check (it does!) + // // // void around(): execution(* C.m6(..)) && args(List){}//: Should match + // // (it does) + // // // void around(): execution(* C.m6(..)) && args(ArrayList){}//: Should + // // runtime check (it does not match!) + // // // void around(): execution(* C.m6(..)) && args(List){}//: Should + // // match (it does) + // // // void around(): execution(* C.m6(..)) && args(ArrayList){}//: Should + // // runtime check (it does not match!) + // // // void around(): execution(* C.m6(..)) && args(ArrayList){}//: + // // Should not match (it does not match!) + // // + // // // void around(): execution(* C.m7(..)) && args(List>){} + // // //: Should not match (but it does) ERROR + // // // void around(): execution(* C.m7(..)) && + // // args(ArrayList>){}//: Should not match (but it does!) ERROR + // // // void around(): execution(* C.m7(..)) && args(List>){}//: + // // Should not match (but it does!) ERROR + // // // void around(): execution(* C.m7(..)) && + // // args(ArrayList>){}//: Should not match (but it does) ERROR + // // // void around(): execution(* C.m7(..)) && args(List>){}//: Should not match (but it does) ERROR + // // // void around(): execution(* C.m7(..)) && args(ArrayList< ? extends + // // List>){}//: Should not match (but it does!) ERROR + // // // void around(): execution(* C.m7(..)) && args(List< ? extends List>){}//: Should match (it does!) + // // // void around(): execution(* C.m7(..)) && args(ArrayList< ? extends + // // List>){}//: Should match (it does!) + // // // void around(): execution(* C.m7(..)) && args(List){}//: Should match + // // (it does) + // // // void around(): execution(* C.m7(..)) && args(ArrayList){}//: Should + // // runtime check (it does not match!) + // // // void around(): execution(* C.m7(..)) && args(List){}//: Should + // // match (it does) + // // // void around(): execution(* C.m7(..)) && args(ArrayList){}//: Should + // // runtime check (it does!) + // // // void around(): execution(* C.m7(..)) && + // // args(ArrayList>){}//: Should not match (it does not match!) + // // + // // // void around(): execution(* C.m8(..)) && args(List){} //: + // // Should match with unchecked conversion (it does) + // // // void around(): execution(* C.m8(..)) && args(ArrayList){}//: + // // Should runtime check with unchecked conversion (it does!) + // // // void around(): execution(* C.m8(..)) && args(List){}//: Should + // // match with unchecked conversion (it does!) + // // // void around(): execution(* C.m8(..)) && args(ArrayList){}//: + // // Should runtime check with unchecked conversion (it does) + // // // void around(): execution(* C.m8(..)) && args(List){}//: Should match with unchecked conversion (it does!) + // // // void around(): execution(* C.m8(..)) && args(ArrayList){}//: Should runtime check with unchecked conversion (it does) + // // // void around(): execution(* C.m8(..)) && args(List){}//: Should match + // // (it does) + // // // void around(): execution(* C.m8(..)) && args(ArrayList){}//: Should + // // runtime check (it does!) + // // // void around(): execution(* C.m8(..)) && args(List){}//: Should + // // match (it does) + // // // void around(): execution(* C.m8(..)) && args(ArrayList){}//: Should + // // runtime check (it does!) + // // // void around(): execution(* C.m8(..)) && args(ArrayList){}//: + // // Should not match (it does not match!) + // // + // // // void around(): execution(* C.m9(..)) && args(List){} //: + // // Should not match (but it does) ERROR + // // // void around(): execution(* C.m9(..)) && args(ArrayList){}//: + // // Should not match (it does not match!) + // // // void around(): execution(* C.m9(..)) && args(Number){}//: Should match + // // (it does!) + // // // void around(): execution(* C.m9(..)) && args(Integer){}//: Should + // // runtime check (it does) + // // // void around(): execution(* C.m9(..)) && args(List){}//: Should not match (but it does) ERROR + // // // void around(): execution(* C.m9(..)) && args(ArrayList){}//: Should not match (it does not match!) + // // // void around(): execution(* C.m9(..)) && args(List){}//: Should not + // // match (but it does) ERROR + // // // void around(): execution(* C.m9(..)) && args(ArrayList){}//: Should + // // not match (it does not match!) + // // // void around(): execution(* C.m9(..)) && args(List){}//: Should not + // // match (but it does) ERROR + // // // void around(): execution(* C.m9(..)) && args(ArrayList){}//: Should + // // not match (it does not match!) + // // // void around(): execution(* C.m9(..)) && args(String){}//: Should not + // // match (it does not match!) + // + // } + + private ReferenceType resolve(String sig) { + return (ReferenceType) world.resolve(UnresolvedType.forSignature(sig)); + } +} diff --git a/weaver/src/test/java/org/aspectj/weaver/ResolvedMemberSignatures15TestCase.java b/weaver/src/test/java/org/aspectj/weaver/ResolvedMemberSignatures15TestCase.java new file mode 100644 index 000000000..04697b884 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/ResolvedMemberSignatures15TestCase.java @@ -0,0 +1,281 @@ +/* ******************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver; + +import java.lang.reflect.Modifier; + +import org.aspectj.weaver.bcel.BcelWorld; + +import junit.framework.TestCase; + +public class ResolvedMemberSignatures15TestCase extends TestCase { + + World world; + UnresolvedType baseType; + UnresolvedType derivedType; + + // STATIC METHODS + + public void testBaseOnlyStaticMethod() { + Member toFind = new MemberImpl(Member.METHOD,baseType, + (Modifier.PUBLIC | Modifier.STATIC), + UnresolvedType.forSignature("V"), + "onlyBase", UnresolvedType.NONE); + ResolvedMember[] foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); + assertEquals("found 1 member",1,foundMembers.length); + assertEquals("Lfluffy/Base;",foundMembers[0].getDeclaringType().getSignature()); + + toFind = new MemberImpl(Member.METHOD, derivedType, + (Modifier.PUBLIC | Modifier.STATIC), + UnresolvedType.forSignature("V"), + "onlyBase", UnresolvedType.NONE); + foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); + // this looks odd but we need both because of the way calls to inherited static methods + // are rendered in bytecode when written as obj.foo(); - the bytecode says it is a call + // to obj.getClass().foo() even if the static method is defined in a super type. + assertEquals("found 2 members",2,foundMembers.length); + StringBuilder s= new StringBuilder(); + for (ResolvedMember rm: foundMembers) { + s.append(rm.toString()+" "); + } + assertEquals("Expected derived but was "+foundMembers[0]+". All="+s,"Lfluffy/Derived;",foundMembers[0].getDeclaringType().getSignature()); + assertEquals("Expected base but was "+foundMembers[1]+". All="+s,"Lfluffy/Base;",foundMembers[1].getDeclaringType().getSignature()); + } + + public void testBothStaticMethod() { + Member toFind = new MemberImpl(Member.METHOD,baseType, + (Modifier.PUBLIC | Modifier.STATIC), + UnresolvedType.forSignature("V"), + "both", + new UnresolvedType[0] + ); + ResolvedMember[] foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); + assertEquals("found 1 member",1,foundMembers.length); + assertEquals("Lfluffy/Base;",foundMembers[0].getDeclaringType().getSignature()); + + toFind = new MemberImpl(Member.METHOD,derivedType, + (Modifier.PUBLIC | Modifier.STATIC), + UnresolvedType.forSignature("V"), + "both", + new UnresolvedType[0] + ); + foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); + assertEquals("found 1 members",1,foundMembers.length); + assertEquals("Lfluffy/Derived;",foundMembers[0].getDeclaringType().getSignature()); + } + + public void testDerivedStaticMethod() { + Member toFind = new MemberImpl(Member.METHOD,baseType, + (Modifier.PUBLIC | Modifier.STATIC), + UnresolvedType.forSignature("V"), + "onlyDerived", + new UnresolvedType[0] + ); + + ResolvedMember[] foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); + assertEquals("found nothing",0,foundMembers.length); + + toFind = new MemberImpl(Member.METHOD,derivedType, + (Modifier.PUBLIC | Modifier.STATIC), + UnresolvedType.forSignature("V"), + "onlyDerived", + new UnresolvedType[0] + ); + foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); + assertEquals("found 1 members",1,foundMembers.length); + assertEquals("Lfluffy/Derived;",foundMembers[0].getDeclaringType().getSignature()); + } + + // NON-STATIC METHODS + + public void testBaseOnlyMethod() { + Member toFind = new MemberImpl(Member.METHOD,baseType, + Modifier.PUBLIC, + UnresolvedType.forSignature("V"), + "onlyBaseNonStatic", + new UnresolvedType[0] + ); + ResolvedMember[] foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); + assertEquals("found 1 member",1,foundMembers.length); + assertEquals("Lfluffy/Base;",foundMembers[0].getDeclaringType().getSignature()); + + toFind = new MemberImpl(Member.METHOD,derivedType, + Modifier.PUBLIC, + UnresolvedType.forSignature("V"), + "onlyBaseNonStatic", + new UnresolvedType[0] + ); + foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); + assertEquals("found 2 members",2,foundMembers.length); + assertEquals("Lfluffy/Derived;",foundMembers[0].getDeclaringType().getSignature()); + assertEquals("Lfluffy/Base;",foundMembers[1].getDeclaringType().getSignature()); + + } + + public void testBothMethod() { + Member toFind = new MemberImpl(Member.METHOD,baseType, + Modifier.PUBLIC, UnresolvedType.forSignature("V"), + "bothNonStatic", UnresolvedType.NONE); + ResolvedMember[] foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); + assertEquals("found 1 member",1,foundMembers.length); + assertEquals("Lfluffy/Base;",foundMembers[0].getDeclaringType().getSignature()); + + toFind = new MemberImpl(Member.METHOD,derivedType, + Modifier.PUBLIC, UnresolvedType.forSignature("V"), + "bothNonStatic", UnresolvedType.NONE); + foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); + assertEquals("found 2 members",2,foundMembers.length); + assertEquals("Lfluffy/Derived;",foundMembers[0].getDeclaringType().getSignature()); + assertEquals("Lfluffy/Base;",foundMembers[1].getDeclaringType().getSignature()); + } + + public void testDerivedMethod() { + Member toFind = new MemberImpl(Member.METHOD,baseType, + Modifier.PUBLIC, + UnresolvedType.forSignature("V"), + "onlyDerivedNonStatic", + new UnresolvedType[0] + ); + + ResolvedMember[] foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); + assertEquals("found nothing",0,foundMembers.length); + + toFind = new MemberImpl(Member.METHOD,derivedType, + Modifier.PUBLIC, + UnresolvedType.forSignature("V"), + "onlyDerivedNonStatic", + new UnresolvedType[0] + ); + foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); + assertEquals("found 1 members",1,foundMembers.length); + assertEquals("Lfluffy/Derived;",foundMembers[0].getDeclaringType().getSignature()); + } + + public void testChangingThrowsClause() { + Member toFind = new MemberImpl(Member.METHOD,derivedType, + Modifier.PUBLIC, + UnresolvedType.forSignature("V"), + "m", + new UnresolvedType[0] + ); + + ResolvedMember[] foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); + assertEquals("found 2 members",2,foundMembers.length); + assertEquals("Lfluffy/Derived;",foundMembers[0].getDeclaringType().getSignature()); + assertEquals("Lfluffy/Base;",foundMembers[1].getDeclaringType().getSignature()); + + assertEquals("throws CloneNotSupported",1,foundMembers[1].getExceptions().length); + assertEquals("doesn't throw anything",0,foundMembers[0].getExceptions().length); + } + + // CONSTRUCTORS + + public void testNoWalkUpMatchingConstructor() { + Member toFind = new MemberImpl(Member.CONSTRUCTOR,derivedType, + Modifier.PUBLIC, + UnresolvedType.forSignature("V"), + "", + new UnresolvedType[0] + ); + ResolvedMember[] foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); + assertEquals("found 1 members",1,foundMembers.length); + assertEquals("Lfluffy/Derived;",foundMembers[0].getDeclaringType().getSignature()); + } + + public void testNoWalkUpNoMatchingConstructor() { + Member toFind = new MemberImpl(Member.CONSTRUCTOR,derivedType, + Modifier.PUBLIC, + UnresolvedType.forSignature("V"), + "", + new UnresolvedType[] {UnresolvedType.forSignature("I")} + ); + ResolvedMember[] foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); + assertEquals("No matches",0,foundMembers.length); + } + + // FIELDS + + public void testBaseOnlyField() { + Member toFind = new MemberImpl(Member.FIELD,baseType, + Modifier.PUBLIC, + UnresolvedType.forSignature("I"), + "onlyBase", + new UnresolvedType[0] + ); + ResolvedMember[] foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); + assertEquals("found 1 member",1,foundMembers.length); + assertEquals("Lfluffy/Base;",foundMembers[0].getDeclaringType().getSignature()); + + toFind = new MemberImpl(Member.FIELD,derivedType, + Modifier.PUBLIC, + UnresolvedType.forSignature("I"), + "onlyBase", + new UnresolvedType[0] + ); + foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); + assertEquals("found 2 members",2,foundMembers.length); + assertEquals("Lfluffy/Derived;",foundMembers[0].getDeclaringType().getSignature()); + assertEquals("Lfluffy/Base;",foundMembers[1].getDeclaringType().getSignature()); + } + + public void testBothField() { + Member toFind = new MemberImpl(Member.FIELD,baseType, + Modifier.PUBLIC, + UnresolvedType.forSignature("I"), + "both", + new UnresolvedType[0] + ); + ResolvedMember[] foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); + assertEquals("found 1 member",1,foundMembers.length); + assertEquals("Lfluffy/Base;",foundMembers[0].getDeclaringType().getSignature()); + + toFind = new MemberImpl(Member.FIELD,derivedType, + Modifier.PUBLIC, + UnresolvedType.forSignature("I"), + "both", + new UnresolvedType[0] + ); + foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); + assertEquals("found 1 members",1,foundMembers.length); + assertEquals("Lfluffy/Derived;",foundMembers[0].getDeclaringType().getSignature()); + } + + public void testDerivedField() { + Member toFind = new MemberImpl(Member.FIELD,baseType, + Modifier.PUBLIC, + UnresolvedType.forSignature("I"), + "onlyDerived", + new UnresolvedType[0] + ); + + ResolvedMember[] foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); + assertEquals("found nothing",0,foundMembers.length); + + toFind = new MemberImpl(Member.FIELD,derivedType, + Modifier.PUBLIC, + UnresolvedType.forSignature("I"), + "onlyDerived", + new UnresolvedType[0] + ); + foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); + assertEquals("found 1 members",1,foundMembers.length); + assertEquals("Lfluffy/Derived;",foundMembers[0].getDeclaringType().getSignature()); + } + + protected void setUp() throws Exception { + world = new BcelWorld(); + world.setBehaveInJava5Way(true); + baseType = UnresolvedType.forSignature("Lfluffy/Base;"); + derivedType = UnresolvedType.forSignature("Lfluffy/Derived;"); + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/TestShadow.java b/weaver/src/test/java/org/aspectj/weaver/TestShadow.java new file mode 100644 index 000000000..2cdfd84bf --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/TestShadow.java @@ -0,0 +1,131 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.weaver.ast.Var; + +public class TestShadow extends Shadow { + + private final World world; + private final UnresolvedType thisType; + + public TestShadow(Kind kind, Member signature, UnresolvedType thisType, World world) { + super(kind, signature, null); + this.world = world; + this.thisType = thisType; + } + + public World getIWorld() { + return world; + } + + /** this is subtly wrong. ha ha */ + public UnresolvedType getEnclosingType() { + return thisType; + } + + public Var getThisVar() { + // we should thorw if we don't have a this + return new Var(getThisType().resolve(world)); + } + + public Var getTargetVar() { + if (!hasTarget()) + throw new RuntimeException("bad"); + return new Var(getTargetType().resolve(world)); + } + + public Var getArgVar(int i) { + return new Var(getArgType(i).resolve(world)); + } + + public Var getThisEnclosingJoinPointStaticPartVar() { + throw new RuntimeException("unimplemented"); + } + + public Var getThisJoinPointStaticPartVar() { + throw new RuntimeException("unimplemented"); + } + + public Var getThisAspectInstanceVar(ResolvedType aspectType) { + throw new RuntimeException("unimplemented"); + } + + public Var getThisJoinPointVar() { + throw new RuntimeException("unimplemented"); + } + + public ISourceLocation getSourceLocation() { + throw new RuntimeException("unimplemented"); + } + + public Member getEnclosingCodeSignature() { + throw new RuntimeException("unimplemented"); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.Shadow#getKindedAnnotationVar() + */ + public Var getKindedAnnotationVar(UnresolvedType annotationType) { + throw new RuntimeException("unimplemented"); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.Shadow#getWithinAnnotationVar() + */ + public Var getWithinAnnotationVar(UnresolvedType annotationType) { + throw new RuntimeException("unimplemented"); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.Shadow#getWithinCodeAnnotationVar() + */ + public Var getWithinCodeAnnotationVar(UnresolvedType annotationType) { + throw new RuntimeException("unimplemented"); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.Shadow#getThisAnnotationVar() + */ + public Var getThisAnnotationVar(UnresolvedType annotationType) { + throw new RuntimeException("unimplemented"); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.Shadow#getTargetAnnotationVar() + */ + public Var getTargetAnnotationVar(UnresolvedType annotationType) { + throw new RuntimeException("unimplemented"); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.Shadow#getArgAnnotationVar(int) + */ + public Var getArgAnnotationVar(int i, UnresolvedType annotationType) { + throw new RuntimeException("unimplemented"); + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/TraceFactoryTest.java b/weaver/src/test/java/org/aspectj/weaver/TraceFactoryTest.java new file mode 100644 index 000000000..24ec997fe --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/TraceFactoryTest.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Matthew Webster - initial implementation + *******************************************************************************/ +package org.aspectj.weaver; + +import org.aspectj.weaver.tools.Trace; +import org.aspectj.weaver.tools.TraceFactory; + +import junit.framework.TestCase; + +public class TraceFactoryTest extends TestCase { + + public void testGetTraceFactory() { + TraceFactory traceFactory = TraceFactory.getTraceFactory(); + assertNotNull(traceFactory); + } + + public void testGetTrace() { + TraceFactory traceFactory = TraceFactory.getTraceFactory(); + Trace trace = traceFactory.getTrace(getClass()); + assertNotNull(trace); + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/TypeVariableReferenceTypeTestCase.java b/weaver/src/test/java/org/aspectj/weaver/TypeVariableReferenceTypeTestCase.java new file mode 100644 index 000000000..367a4fc93 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/TypeVariableReferenceTypeTestCase.java @@ -0,0 +1,81 @@ +/* ******************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver; + +import junit.framework.TestCase; + +import org.aspectj.weaver.bcel.BcelWorld; + +/** + * @author colyer + * + */ +public class TypeVariableReferenceTypeTestCase extends TestCase { + + ReferenceType javaLangClass; + ReferenceType jlNumber; + ReferenceType javaLangObject; + BoundedReferenceType extendsClass; + BoundedReferenceType superClass; + BoundedReferenceType extendsWithExtras; + BcelWorld world; + + @Override + protected void setUp() throws Exception { + super.setUp(); + world = new BcelWorld("../bin"); + world.setBehaveInJava5Way(true); + javaLangClass = (ReferenceType) world.resolve(UnresolvedType.forName("java/lang/Class")); + jlNumber = (ReferenceType) world.resolve(UnresolvedType.forSignature("Ljava/lang/Number;")); + javaLangObject = (ReferenceType) world.resolve(UnresolvedType.OBJECT); + extendsClass = new BoundedReferenceType(javaLangClass, true, world); + superClass = new BoundedReferenceType(javaLangClass, false, world); + extendsWithExtras = new BoundedReferenceType(javaLangClass, true, world, new ReferenceType[] { (ReferenceType) world + .resolve(UnresolvedType.forName("java/util/List")) }); + } + + public void testConstructionByNameAndVariable() { + TypeVariable tv = new TypeVariable("T", javaLangClass); + TypeVariableReferenceType tvrt = new TypeVariableReferenceType(tv, world); + assertEquals("T", tvrt.getTypeVariable().getName()); + assertEquals(javaLangClass, tvrt.getTypeVariable().getUpperBound()); + } + + public void testBounds() { + // Load up the testclass from below + ResolvedType testerClass = world.resolve(Tester1.class.getName()); + ResolvedType genericTesterClass = testerClass.getGenericType(); + + // Check the declaration type variable + TypeVariable[] typevars = genericTesterClass.getTypeVariables(); + TypeVariable typevar = typevars[0]; + assertEquals(jlNumber, typevar.getUpperBound()); + assertEquals("T", typevar.getName()); + ResolvedMember member = genericTesterClass.getDeclaredMethods()[1]; + + // getParameterTypes() returning the erased parameter + UnresolvedType param = member.getParameterTypes()[0]; + assertEquals(jlNumber, param); + + // Check the type variable reference + TypeVariableReferenceType tvReference = (TypeVariableReferenceType) member.getGenericParameterTypes()[0]; + assertEquals("T", tvReference.getTypeVariableName()); + assertEquals(jlNumber, tvReference.getUpperBound()); + assertEquals(jlNumber, tvReference.getDelegate().getResolvedTypeX()); + } + + class Tester1 { + public void method(T t) { + } + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/TypeVariableTestCase.java b/weaver/src/test/java/org/aspectj/weaver/TypeVariableTestCase.java new file mode 100644 index 000000000..8ef8f70c1 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/TypeVariableTestCase.java @@ -0,0 +1,110 @@ +/* ******************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver; + +import junit.framework.TestCase; + +import org.aspectj.weaver.bcel.BcelWorld; + +public class TypeVariableTestCase extends TestCase { + + private UnresolvedType javaLangNumber; + private UnresolvedType javaLangDouble; + private UnresolvedType javaUtilList; + private UnresolvedType javaIoSerializable; + private World world; + + public void testName() { + TypeVariable tv = new TypeVariable("T"); + assertEquals("T", tv.getName()); + } + + public void testDefaultBounds() { + TypeVariable tv = new TypeVariable("T"); + assertEquals("Object", UnresolvedType.OBJECT, tv.getFirstBound()); + assertNull(tv.getUpperBound()); + assertEquals("no additional bounds", 0, tv.getSuperInterfaces().length); + } + + public void testUpperBound() { + TypeVariable tv = new TypeVariable("N", javaLangNumber); + assertEquals("java.lang.Number", javaLangNumber, tv.getUpperBound()); + } + + public void testAdditionalUpperBounds() { + TypeVariable tv = new TypeVariable("E", UnresolvedType.OBJECT, new UnresolvedType[] { javaUtilList }); + assertEquals("1 additional bound", 1, tv.getSuperInterfaces().length); + assertEquals("java.util.List", javaUtilList, tv.getSuperInterfaces()[0]); + + tv = new TypeVariable("E", null, new UnresolvedType[] { javaUtilList }); + assertEquals("1 additional bound", 1, tv.getSuperInterfaces().length); + assertEquals("java.util.List", javaUtilList, tv.getSuperInterfaces()[0]); + } + + public void testResolution() { + TypeVariable tv = new TypeVariable("T", javaLangNumber, new UnresolvedType[] { javaUtilList }); + tv.resolve(world); + assertEquals("resolved number", javaLangNumber.resolve(world), tv.getUpperBound()); + assertEquals("resolved list", javaUtilList.resolve(world), tv.getSuperInterfaces()[0]); + } + + public void testBindWithoutResolve() { + TypeVariable tv = new TypeVariable("X"); + try { + tv.canBeBoundTo(null); + fail("Should throw illegal state exception"); + } catch (IllegalStateException ex) { + } + } + + public void testCanBindToUpperMatch() { + TypeVariable tv = new TypeVariable("X", javaLangNumber); + tv.resolve(world); + assertTrue(tv.canBeBoundTo(javaLangDouble.resolve(world))); + } + + public void testCanBindToUpperFail() { + TypeVariable tv = new TypeVariable("X", javaLangNumber); + tv.resolve(world); + assertFalse(tv.canBeBoundTo(UnresolvedType.OBJECT.resolve(world))); + } + + public void testCanBindToInterfaceMatch() { + TypeVariable tv = new TypeVariable("T", javaLangNumber, new UnresolvedType[] { javaIoSerializable }); + tv.resolve(world); + assertTrue(tv.canBeBoundTo(javaLangDouble.resolve(world))); + } + + public void testCanBindToInterfaceFail() { + TypeVariable tv = new TypeVariable("T", javaLangNumber, new UnresolvedType[] { javaUtilList }); + tv.resolve(world); + assertFalse(tv.canBeBoundTo(javaLangDouble.resolve(world))); + } + + // --- + + @Override + protected void setUp() throws Exception { + super.setUp(); + javaLangNumber = UnresolvedType.forSignature("Ljava/lang/Number;"); + javaLangDouble = UnresolvedType.forSignature("Ljava/lang/Double;"); + javaIoSerializable = UnresolvedType.forSignature("Ljava/io/Serializable;"); + javaUtilList = UnresolvedType.forSignature("Ljava/util/List;"); + world = new BcelWorld(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/TypeXTestCase.java b/weaver/src/test/java/org/aspectj/weaver/TypeXTestCase.java new file mode 100644 index 000000000..dc4c2e3d1 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/TypeXTestCase.java @@ -0,0 +1,219 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.weaver; + +import junit.framework.TestCase; + +import org.aspectj.testing.util.TestUtil; +import org.aspectj.util.LangUtil; +import org.aspectj.weaver.bcel.BcelWorld; + +/** + * This is a test case for all the portions of UnresolvedType that don't require a world. + */ +public class TypeXTestCase extends TestCase { + + public TypeXTestCase(String name) { + super(name); + } + + public void testUnresolvedTypes() { + // basic equality + String[] testNames = + new String[] {"int", "long", "int[]", "boolean[][]", + "java.lang.String", "java.lang.String[]", "void" }; + String[] testSigs = + new String[] {"I", "J", "[I", "[[Z", + "Ljava/lang/String;", "[Ljava/lang/String;", "V" }; + + String[] componentNames = + new String[] {null, null, "int", "boolean[]", + null, "java.lang.String", null }; + + int[] sizes = new int[] {1, 2, 1, 1, 1, 1, 0}; + + boolean[] isPrimitive = + new boolean[] { true, true, false, false, false, false, true }; + + nameSignatureTest(testNames, testSigs); + arrayTest(UnresolvedType.forNames(testNames), componentNames); + arrayTest(UnresolvedType.forSignatures(testSigs), componentNames); + + sizeTest(UnresolvedType.forNames(testNames), sizes); + sizeTest(UnresolvedType.forSignatures(testSigs), sizes); + + isPrimitiveTest(UnresolvedType.forSignatures(testSigs), isPrimitive); + } + + public void testNameAndSigWithInners() { + UnresolvedType t = UnresolvedType.forName("java.util.Map$Entry"); + assertEquals(t.getName(), "java.util.Map$Entry"); + assertEquals(t.getSignature(), "Ljava/util/Map$Entry;"); + assertEquals(t.getOutermostType(), UnresolvedType.forName("java.util.Map")); + assertEquals(UnresolvedType.forName("java.util.Map").getOutermostType(), UnresolvedType.forName("java.util.Map")); + } + + public void testNameAndSigWithParameters() { + UnresolvedType t = UnresolvedType.forName("java.util.List"); + assertEquals(t.getName(),"java.util.List"); + assertEquals(t.getSignature(),"Pjava/util/List;"); + t = UnresolvedType.forSignature("Pjava/util/List;"); + assertEquals(t.getName(),"java.util.List"); + assertEquals(t.getSignature(),"Pjava/util/List;"); + t = UnresolvedType.forName("java.util.Map>"); + assertEquals(t.getName(),"java.util.Map>"); + assertEquals(t.getSignature(),"Pjava/util/Map;>;"); + t = UnresolvedType.forSignature("Pjava/util/Map;>;"); + assertEquals(t.getName(),"java.util.Map>"); + assertEquals(t.getSignature(),"Pjava/util/Map;>;"); + } + + /** + * Verify UnresolvedType signature processing creates the right kind of UnresolvedType's from a signature. + * + * For example, calling UnresolvedType.dump() for + * "Ljava/util/Map;Ljava/lang/String;>;" + * results in: + * UnresolvedType: signature=Ljava/util/Map;Ljava/lang/String;>; parameterized=true #params=2 + * UnresolvedType: signature=Ljava/util/List; parameterized=true #params=1 + * UnresolvedType: signature=Ljava/lang/String; parameterized=false #params=0 + * UnresolvedType: signature=Ljava/lang/String; parameterized=false #params=0 + */ + public void testTypexGenericSignatureProcessing() { + UnresolvedType tx = null; + + tx = UnresolvedType.forSignature("Pjava/util/Set;"); + checkTX(tx,true,1); + + tx = UnresolvedType.forSignature("Pjava/util/Set;>;"); + checkTX(tx,true,1); + + tx = UnresolvedType.forSignature("Pjava/util/Map;Ljava/lang/String;>;"); + checkTX(tx,true,2); + checkTX(tx.getTypeParameters()[0],true,1); + checkTX(tx.getTypeParameters()[1],false,0); +// System.err.println(tx.dump()); + } + + public void testTypeXForParameterizedTypes() { + if (LangUtil.is15VMOrGreater()) { // no funny types pre 1.5 + World world = new BcelWorld(); + UnresolvedType stringType = UnresolvedType.forName("java/lang/String"); + ResolvedType listOfStringType = + TypeFactory.createParameterizedType( + UnresolvedType.forName("java/util/List").resolve(world), + new UnresolvedType[] {stringType}, + world); + assertEquals("1 type param",1,listOfStringType.typeParameters.length); + assertEquals(stringType,listOfStringType.typeParameters[0]); + assertTrue(listOfStringType.isParameterizedType()); + assertFalse(listOfStringType.isGenericType()); + } + } + + public void testTypeFactoryForParameterizedTypes() { + if (LangUtil.is15VMOrGreater()) { // no funny types pre 1.5 + UnresolvedType enumOfSimpleType = + TypeFactory.createTypeFromSignature("Pjava/lang/Enum;"); + assertEquals(1, enumOfSimpleType.getTypeParameters().length); + + UnresolvedType enumOfNestedType = + TypeFactory.createTypeFromSignature("Pjava/lang/Enum;"); + assertEquals(1, enumOfNestedType.getTypeParameters().length); + + // is this signature right? + UnresolvedType nestedTypeOfParameterized = + TypeFactory.createTypeFromSignature("PMyInterface$MyOtherType;"); + assertEquals(0, nestedTypeOfParameterized.getTypeParameters().length); + + // how about this one? is this valid? + UnresolvedType doublyNestedTypeSignatures = + TypeFactory.createTypeFromSignature("PMyInterface$MyOtherType;"); + assertEquals(1, doublyNestedTypeSignatures.getTypeParameters().length); + + } + } + + private void checkTX(UnresolvedType tx,boolean shouldBeParameterized,int numberOfTypeParameters) { + assertTrue("Expected parameterization flag to be "+shouldBeParameterized,tx.isParameterizedType()==shouldBeParameterized); + if (numberOfTypeParameters==0) { + UnresolvedType[] params = tx.getTypeParameters(); + assertTrue("Expected 0 type parameters but found "+params.length, params.length==0); + } else { + assertTrue("Expected #type parameters to be "+numberOfTypeParameters,tx.getTypeParameters().length==numberOfTypeParameters); + } + } + + + private void isPrimitiveTest(UnresolvedType[] types, boolean[] isPrimitives) { + for (int i = 0, len = types.length; i < len; i++) { + UnresolvedType type = types[i]; + boolean b = isPrimitives[i]; + assertEquals(type + " is primitive: ", b, type.isPrimitiveType()); + } + } + + private void sizeTest(UnresolvedType[] types, int[] sizes) { + for (int i = 0, len = types.length; i < len; i++) { + UnresolvedType type = types[i]; + int size = sizes[i]; + assertEquals("size of " + type + ": ", size, type.getSize()); + } + } + + private void arrayTest(UnresolvedType[] types, String[] components) { + for (int i = 0, len = types.length; i < len; i++) { + UnresolvedType type = types[i]; + String component = components[i]; + assertEquals(type + " is array: ", component != null, type.isArray()); + if (component != null) + assertEquals(type + " componentType: ", component, + type.getComponentType().getName()); + } + } + + private void nameSignatureTest(String[] ns, String[] ss) { + for (int i = 0, len = ns.length; i < len; i++) { + String n = ns[i]; + String s = ss[i]; + UnresolvedType tn = UnresolvedType.forName(n); + UnresolvedType ts = UnresolvedType.forSignature(s); + + assertEquals("forName(n).getName()", n, + tn.getName()); + assertEquals("forSignature(s).getSignature()", s, + ts.getSignature()); + assertEquals("forName(n).getSignature()", s, + tn.getSignature()); + assertEquals("forSignature(n).getName()", n, + ts.getName()); + + TestUtil.assertCommutativeEquals(tn, tn, true); + TestUtil.assertCommutativeEquals(ts, ts, true); + TestUtil.assertCommutativeEquals(tn, ts, true); + + for (int j = 0; j < len; j++) { + if (i == j) continue; + UnresolvedType tn1 = UnresolvedType.forName(ns[j]); + UnresolvedType ts1 = UnresolvedType.forSignature(ss[j]); + TestUtil.assertCommutativeEquals(tn, tn1, false); + TestUtil.assertCommutativeEquals(ts, tn1, false); + TestUtil.assertCommutativeEquals(tn, ts1, false); + TestUtil.assertCommutativeEquals(ts, ts1, false); + } + } + } + + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/Weaver5ModuleTests.java b/weaver/src/test/java/org/aspectj/weaver/Weaver5ModuleTests.java new file mode 100644 index 000000000..d54732b08 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/Weaver5ModuleTests.java @@ -0,0 +1,37 @@ +//package org.aspectj.weaver; +///******************************************************************************* +// * Copyright (c) 2005 Contributors. +// * All rights reserved. +// * This program and the accompanying materials are made available +// * under the terms of the Eclipse Public License v1.0 +// * which accompanies this distribution and is available at +// * http://eclipse.org/legal/epl-v10.html +// * +// * Contributors: (See CVS logs) +// * +// *******************************************************************************/ +// +//import junit.framework.Test; +//import junit.framework.TestCase; +//import junit.framework.TestSuite; +// +//import org.aspectj.testing.util.TestUtil; +// +///** +// */ +//public class Weaver5ModuleTests extends TestCase { +// +// public static Test suite() { +// TestSuite suite = new TestSuite(Weaver5ModuleTests.class.getName()); +// if (TestUtil.is15VMOrGreater()) { +// TestUtil.loadTestsReflectively(suite, "org.aspectj.weaver.AllWeaver5Tests", false); +// } else { +// suite.addTest(TestUtil.testNamed("all tests require 1.5")); +// } +// return suite; +// } +// public static void main(String[] args) { +// junit.textui.TestRunner.main(new String[] {Weaver5ModuleTests.class.getName()}); +// } +// +//} diff --git a/weaver/src/test/java/org/aspectj/weaver/WeaverMessagesTestCase.java b/weaver/src/test/java/org/aspectj/weaver/WeaverMessagesTestCase.java new file mode 100644 index 000000000..7326971d3 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/WeaverMessagesTestCase.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.aspectj.weaver; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; +import java.util.MissingResourceException; + +import junit.framework.TestCase; + +/** + * @author Adrian Colyer + */ +public class WeaverMessagesTestCase extends TestCase { + + public void testAllMessagesDefined() { + + Class wmClass = WeaverMessages.class; + Field[] fields = wmClass.getDeclaredFields(); + List fieldList = new ArrayList(); + for (int i = 0; i < fields.length; i++) { + Field f = fields[i]; + if (f.getType() == String.class) { + try { + String key = (String) f.get(null); +// String value = WeaverMessages.format(key); + assertFalse("Each key should be unique",fieldList.contains(key)); + fieldList.add(key); +// System.out.println(key + "," + value); + } catch (IllegalAccessException ex) { + } catch(MissingResourceException mrEx) { + fail("Missing resource: " + mrEx); + } + } + } + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/WeaverTestCase.java b/weaver/src/test/java/org/aspectj/weaver/WeaverTestCase.java new file mode 100644 index 000000000..152cae782 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/WeaverTestCase.java @@ -0,0 +1,50 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver; + +import java.io.File; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +import org.aspectj.util.FileUtil; + +public abstract class WeaverTestCase extends TestCase { + + public static final String TESTDATA_PATH = "../weaver/testdata"; + public static final String OUTDIR_PATH = "../weaver/out"; + + /** @return File outDir (writable) or null if unable to write */ + public static File getOutdir() { + File result = new File(OUTDIR_PATH); + if (result.mkdirs() || (result.canWrite() && result.isDirectory())) { + return result; + } + return null; + } + + /** best efforts to delete the output directory and any contents */ + public static void removeOutDir() { + File outDir = getOutdir(); + if (null != outDir) { + FileUtil.deleteContents(outDir); + outDir.delete(); + } + } + + public WeaverTestCase(String name) { + super(name); + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/bcel/AfterReturningWeaveTestCase.java b/weaver/src/test/java/org/aspectj/weaver/bcel/AfterReturningWeaveTestCase.java new file mode 100644 index 000000000..8ccafe4de --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/bcel/AfterReturningWeaveTestCase.java @@ -0,0 +1,58 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.weaver.bcel; + +import java.io.IOException; + +import org.aspectj.weaver.ShadowMunger; + +public class AfterReturningWeaveTestCase extends WeaveTestCase { + { + regenerate = false; + } + + public AfterReturningWeaveTestCase(String name) { + super(name); + } + + public void testAfterReturning() throws IOException { + weaveTest( + getStandardTargets(), + "AfterReturning", + makeAdviceAll("afterReturning")); + } + + public void testAfterReturningParam() throws IOException { + weaveTest( + getStandardTargets(), + "AfterReturningParam", + makeAdviceField("afterReturning", "java.lang.Object")); + } + public void testAfterReturningCheckcastParam() throws IOException { + weaveTest( + getStandardTargets(), + "AfterReturningCheckcastParam", + makeAdviceField("afterReturning", "java.rmi.server.LogStream")); + } + + public void testAfterReturningConversionParam() throws IOException { + String mungerString = + "afterReturning(): call(int *.*(..)) -> " + + "static void Aspect.ajc_afterReturning_field_get(java.lang.Object)"; + ShadowMunger cm = makeConcreteAdvice(mungerString, 1); + + weaveTest("FancyHelloWorld", "AfterReturningConversionParam", cm); + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/bcel/AfterThrowingWeaveTestCase.java b/weaver/src/test/java/org/aspectj/weaver/bcel/AfterThrowingWeaveTestCase.java new file mode 100644 index 000000000..b0c3f04ce --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/bcel/AfterThrowingWeaveTestCase.java @@ -0,0 +1,45 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.bcel; + +import java.io.IOException; + +import org.aspectj.weaver.Advice; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.ShadowMunger; + +public class AfterThrowingWeaveTestCase extends WeaveTestCase { + { + regenerate = false; + } + + public AfterThrowingWeaveTestCase(String name) { + super(name); + } + + public void testAfterThrowing() throws IOException { + weaveTest(getStandardTargets(), "AfterThrowing", makeAdviceAll("afterThrowing")); + } + + public void testAfterThrowingParam() throws IOException { + BcelWorld world = new BcelWorld(); + + ShadowMunger myMunger = BcelTestUtils.shadowMunger(world, + "afterThrowing(): get(* *.out) -> static void Aspect.ajc_afterThrowing_field_get(java.lang.Throwable)", + Advice.ExtraArgument); + ShadowMunger cm = myMunger.concretize(ResolvedType.MISSING, world, null); + + weaveTest(getStandardTargets(), "AfterThrowingParam", cm); + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/bcel/AfterWeaveTestCase.java b/weaver/src/test/java/org/aspectj/weaver/bcel/AfterWeaveTestCase.java new file mode 100644 index 000000000..a51c69b9a --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/bcel/AfterWeaveTestCase.java @@ -0,0 +1,31 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.weaver.bcel; + +import java.io.*; + +public class AfterWeaveTestCase extends WeaveTestCase { + { + regenerate = false; + } + + public AfterWeaveTestCase(String name) { + super(name); + } + + + public void testAfter() throws IOException { + weaveTest(getStandardTargets(), "After", makeAdviceAll("after")); + } +} diff --git a/weaver/src/test/java/org/aspectj/weaver/bcel/ArgsWeaveTestCase.java b/weaver/src/test/java/org/aspectj/weaver/bcel/ArgsWeaveTestCase.java new file mode 100644 index 000000000..1108c3574 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/bcel/ArgsWeaveTestCase.java @@ -0,0 +1,119 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.weaver.bcel; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.aspectj.apache.bcel.Constants; +import org.aspectj.apache.bcel.generic.InstructionFactory; +import org.aspectj.apache.bcel.generic.InstructionHandle; +import org.aspectj.apache.bcel.generic.InstructionList; +import org.aspectj.apache.bcel.generic.Type; +import org.aspectj.weaver.AdviceKind; +import org.aspectj.weaver.MemberImpl; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.ShadowMunger; +import org.aspectj.weaver.UnresolvedType; + +/**. + */ +public class ArgsWeaveTestCase extends WeaveTestCase { + { + regenerate = false; + } + + public ArgsWeaveTestCase(String name) { + super(name); + } + + + public void testAfterReturningArgs() throws IOException { + weaveTest("HelloWorld", "ArgsAfterReturningHelloWorld", makeArgsMunger("afterReturning")); + } + + + public void testFancyAfterReturningArgs() throws IOException { + weaveTest("FancyHelloWorld", "ArgsAfterReturningFancyHelloWorld", makeArgsMunger("afterReturning")); + } + + public void testThrowing() throws IOException { + weaveTest("HelloWorld", "ArgsAfterThrowingHelloWorld", makeArgsMunger("afterThrowing")); + } + + public void testLots() throws IOException { + List l = new ArrayList<>(); + + BcelAdvice p1 = + makeArgsMunger("before"); + + BcelAdvice p2 = + makeArgsMunger("afterThrowing"); + + BcelAdvice p3 = + makeArgsMunger("afterReturning"); + + l.add(p1); + l.add(p2); + l.add(p3); + + weaveTest("HelloWorld", "ArgsBeforeAfterHelloWorld", addLexicalOrder(l)); + } + + /* private */ InstructionList getArgsAdviceTag(BcelShadow shadow, String where) { + String methodName = + "ajc_" + where + "_" + shadow.getKind().toLegalJavaIdentifier(); + InstructionFactory fact = shadow.getFactory(); + InstructionList il = new InstructionList(); + + + il.append( + BcelRenderer.renderExpr( + fact, + new BcelWorld(), + shadow.getArgVar(0), + Type.OBJECT)); + + il.append( + fact.createInvoke( + "Aspect", + methodName, + Type.VOID, + new Type[] { Type.OBJECT }, + Constants.INVOKESTATIC)); + + return il; + } + + private BcelAdvice makeArgsMunger(final String kindx) { + ResolvedType rtx = world.resolve(UnresolvedType.forName("Aspect"),true); + assertTrue("Cant find required type Aspect",!rtx.isMissing()); + return new BcelAdvice(AdviceKind.stringToKind(kindx), makePointcutNoZeroArg(), + MemberImpl.method(UnresolvedType.forName("Aspect"), 0, "foo", "()V"), 0, -1, -1, null, + rtx) { + @Override + public void specializeOn(Shadow shadow) { + super.specializeOn(shadow); + shadow.getArgVar(0); + } + @Override + public InstructionList getAdviceInstructions(BcelShadow shadow, BcelVar extraVar, InstructionHandle fk) { + return getArgsAdviceTag(shadow, kindx); + } + }; + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/bcel/AroundArgsWeaveTestCase.java b/weaver/src/test/java/org/aspectj/weaver/bcel/AroundArgsWeaveTestCase.java new file mode 100644 index 000000000..4088755b5 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/bcel/AroundArgsWeaveTestCase.java @@ -0,0 +1,41 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.weaver.bcel; + +import java.io.IOException; + +import org.aspectj.weaver.ShadowMunger; + +public class AroundArgsWeaveTestCase extends WeaveTestCase { + { + regenerate = false; + } + + public AroundArgsWeaveTestCase(String name) { + super(name); + } + + public void testWeave() throws IOException + { + String label = "AroundArgs"; + ShadowMunger p = + makeConcreteAdvice( + "around(list) : " + + "(call(public * add(..)) && target(list)) -> " + + "static boolean Aspect.ajc_around0" + + "(java.util.ArrayList, org.aspectj.runtime.internal.AroundClosure)"); + weaveTest(new String[] {"DynamicHelloWorld"}, label, p); + + } +} diff --git a/weaver/src/test/java/org/aspectj/weaver/bcel/AroundWeaveTestCase.java b/weaver/src/test/java/org/aspectj/weaver/bcel/AroundWeaveTestCase.java new file mode 100644 index 000000000..a9d1fe9b4 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/bcel/AroundWeaveTestCase.java @@ -0,0 +1,100 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.weaver.bcel; + +import java.io.IOException; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; + +import org.aspectj.weaver.AdviceKind; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.MemberImpl; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.ShadowMunger; +import org.aspectj.weaver.UnresolvedType; + +public class AroundWeaveTestCase extends WeaveTestCase { + { + regenerate = false; + } + + public AroundWeaveTestCase(String name) { + super(name); + } + + public void testAround() throws IOException { + aroundTest("Around", true); + } + + public void testAroundAll() throws IOException { + aroundTest("AroundAll", false); + } + + public void testAroundAndOthers() throws IOException { + aroundTestAndOthers("AroundAndOthers", true); + } + + public void testAroundAllAndOthers() throws IOException { + aroundTestAndOthers("AroundAllAndOthers", false); + } + + + private BcelAdvice makeAroundMunger(final boolean matchOnlyPrintln) { + BcelWorld world = super.world; + final Member sig = + MemberImpl.method( + UnresolvedType.forName("Aspect"), + Modifier.STATIC, + "ajc_around", + "(Lorg/aspectj/runtime/internal/AroundClosure;)Ljava/lang/Object;"); + + return new BcelAdvice( + AdviceKind.stringToKind("around"), + matchOnlyPrintln ? makePointcutPrintln() : makePointcutAll(), + sig, 0, -1, -1, null, UnresolvedType.forName("Aspect").resolve(world)) + { + @Override + public void specializeOn(Shadow s) { + super.specializeOn(s); + ((BcelShadow) s).initializeForAroundClosure(); + } + }; + } + + private void aroundTest(String outName, final boolean matchOnlyPrintln) throws IOException { + weaveTest(getStandardTargets(), outName, makeAroundMunger(matchOnlyPrintln)); + } + + private void aroundTestAndOthers(String outName, final boolean matchOnlyPrintln) + throws IOException + { + + List l = new ArrayList<>(); + + // the afterReturning was taken out to avoid circular advice dependency + + l.addAll(makeAdviceAll("before", matchOnlyPrintln)); + //l.addAll(makeAdviceAll("afterReturning", matchOnlyPrintln)); + + l.add(makeAroundMunger(matchOnlyPrintln)); + + l.addAll(makeAdviceAll("before", matchOnlyPrintln)); + //l.addAll(makeAdviceAll("afterReturning", matchOnlyPrintln)); + + l.add(makeAroundMunger(matchOnlyPrintln)); + weaveTest(getStandardTargets(), outName, addLexicalOrder(l)); + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/bcel/BcelGenericSignatureToTypeXTestCase.java b/weaver/src/test/java/org/aspectj/weaver/bcel/BcelGenericSignatureToTypeXTestCase.java new file mode 100644 index 000000000..58f774840 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/bcel/BcelGenericSignatureToTypeXTestCase.java @@ -0,0 +1,68 @@ +/* ******************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.bcel; + +import junit.framework.TestCase; + +import org.aspectj.apache.bcel.Repository; +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.classfile.Signature; +import org.aspectj.util.GenericSignature; +import org.aspectj.util.GenericSignatureParser; +import org.aspectj.util.GenericSignature.ClassSignature; +import org.aspectj.weaver.UnresolvedType; + +/** + * @author colyer + * + */ +public class BcelGenericSignatureToTypeXTestCase extends TestCase { + + public final GenericSignature.ClassSignature getGenericClassTypeSignature(JavaClass jClass) { + Signature sig = jClass.getSignatureAttribute(); + if (sig != null) { + GenericSignatureParser parser = new GenericSignatureParser(); + ClassSignature classSig = parser.parseAsClassSignature(sig.getSignature()); + return classSig; + } + return null; + } + + public void testEnumFromHell() throws Exception { + BcelWorld world = new BcelWorld(); + JavaClass javaLangEnum = Repository.lookupClass("java/lang/Enum"); + GenericSignature.ClassSignature cSig = getGenericClassTypeSignature(javaLangEnum); + UnresolvedType superclass = BcelGenericSignatureToTypeXConverter.classTypeSignature2TypeX(cSig.superclassSignature, + cSig.formalTypeParameters, world); + assertEquals("Ljava/lang/Object;", superclass.getSignature()); + assertEquals("2 superinterfaces", 2, cSig.superInterfaceSignatures.length); + UnresolvedType comparable = BcelGenericSignatureToTypeXConverter.classTypeSignature2TypeX(cSig.superInterfaceSignatures[0], + cSig.formalTypeParameters, world); + assertEquals("Pjava/lang/Comparable;", comparable.getSignature()); + UnresolvedType serializable = BcelGenericSignatureToTypeXConverter.classTypeSignature2TypeX( + cSig.superInterfaceSignatures[1], cSig.formalTypeParameters, world); + assertEquals("Ljava/io/Serializable;", serializable.getSignature()); + } + + public void testColonColon() throws Exception { + BcelWorld world = new BcelWorld(); + GenericSignature.ClassSignature cSig = new GenericSignatureParser() + .parseAsClassSignature("Ljava/lang/Object;Ljava/lang/Comparable;"); + UnresolvedType resolved = BcelGenericSignatureToTypeXConverter.classTypeSignature2TypeX(cSig.superclassSignature, + cSig.formalTypeParameters, world); + assertEquals("Ljava/lang/Object;", resolved.getSignature()); + BcelGenericSignatureToTypeXConverter.classTypeSignature2TypeX(cSig.superInterfaceSignatures[0], cSig.formalTypeParameters, + world); + + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/bcel/BcelTestUtils.java b/weaver/src/test/java/org/aspectj/weaver/bcel/BcelTestUtils.java new file mode 100644 index 000000000..bc9f47a85 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/bcel/BcelTestUtils.java @@ -0,0 +1,65 @@ +/* ******************************************************************* + * Copyright (c) 2008 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement initial implementation + * ******************************************************************/ +package org.aspectj.weaver.bcel; + +import org.aspectj.weaver.Advice; +import org.aspectj.weaver.AdviceKind; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.TestUtils; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.World; +import org.aspectj.weaver.patterns.FormalBinding; +import org.aspectj.weaver.patterns.Pointcut; +import org.aspectj.weaver.patterns.SimpleScope; + +public class BcelTestUtils { + /** + * Moved from BcelWorld to here + * + * Parse a string into advice. + * + *
+ * + *
+	 * Kind ( Id , ... ) : Pointcut -> MethodSignature
+	 * 
+ * + *
+ */ + public static Advice shadowMunger(World w, String str, int extraFlag) { + str = str.trim(); + int start = 0; + int i = str.indexOf('('); + AdviceKind kind = AdviceKind.stringToKind(str.substring(start, i)); + start = ++i; + i = str.indexOf(')', i); + String[] ids = TestUtils.parseIds(str.substring(start, i).trim()); + // start = ++i; + + i = str.indexOf(':', i); + start = ++i; + i = str.indexOf("->", i); + Pointcut pointcut = Pointcut.fromString(str.substring(start, i).trim()); + Member m = TestUtils.methodFromString(str.substring(i + 2, str.length()).trim()); + + // now, we resolve + UnresolvedType[] types = m.getParameterTypes(); + FormalBinding[] bindings = new FormalBinding[ids.length]; + for (int j = 0, len = ids.length; j < len; j++) { + bindings[j] = new FormalBinding(types[j], ids[j], j, 0, 0); + } + + Pointcut p = pointcut.resolve(new SimpleScope(w, bindings)); + + return new BcelAdvice(kind, p, m, extraFlag, 0, 0, null, null); + } +} diff --git a/weaver/src/test/java/org/aspectj/weaver/bcel/BcelWorldReferenceTypeTest.java b/weaver/src/test/java/org/aspectj/weaver/bcel/BcelWorldReferenceTypeTest.java new file mode 100644 index 000000000..0ab70023e --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/bcel/BcelWorldReferenceTypeTest.java @@ -0,0 +1,23 @@ +/* ******************************************************************* + * Copyright (c) 2002-2008 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ +package org.aspectj.weaver.bcel; + +import org.aspectj.weaver.CommonReferenceTypeTests; +import org.aspectj.weaver.World; + +public class BcelWorldReferenceTypeTest extends CommonReferenceTypeTests { + + public World getWorld() { + return new BcelWorld(); + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/bcel/BeforeWeaveTestCase.java b/weaver/src/test/java/org/aspectj/weaver/bcel/BeforeWeaveTestCase.java new file mode 100644 index 000000000..a43689777 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/bcel/BeforeWeaveTestCase.java @@ -0,0 +1,31 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.weaver.bcel; + +import java.io.IOException; + +public class BeforeWeaveTestCase extends WeaveTestCase { + { + regenerate = false; + } + + public BeforeWeaveTestCase(String name) { + super(name); + } + + + public void testBefore() throws IOException { + weaveTest(getStandardTargets(), "Before", makeAdviceAll("before")); + } +} diff --git a/weaver/src/test/java/org/aspectj/weaver/bcel/CheckerTestCase.java b/weaver/src/test/java/org/aspectj/weaver/bcel/CheckerTestCase.java new file mode 100644 index 000000000..b503e9593 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/bcel/CheckerTestCase.java @@ -0,0 +1,48 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.weaver.bcel; + +import java.io.IOException; + +import org.aspectj.weaver.Checker; +import org.aspectj.weaver.patterns.DeclareErrorOrWarning; +import org.aspectj.bridge.*; +import org.aspectj.bridge.MessageHandler; + +public class CheckerTestCase extends WeaveTestCase { + { + regenerate = false; + } + + public CheckerTestCase(String name) { + super(name); + } + + + public void testStaticTjp() throws IOException { + Checker checker = new Checker( + new DeclareErrorOrWarning(true, makePointcutPrintln(), "hey, we found a println")); + + MessageHandler handler = new MessageHandler(); + world.setMessageHandler(handler); + + weaveTest("HelloWorld", "IdHelloWorld", checker); + assertEquals(1, handler.numMessages(IMessage.ERROR, false)); + + handler = new MessageHandler(); + world.setMessageHandler(handler); + weaveTest("FancyHelloWorld", "IdFancyHelloWorld", checker); + assertEquals(3, handler.numMessages(IMessage.ERROR, false)); + } +} diff --git a/weaver/src/test/java/org/aspectj/weaver/bcel/ClassLoaderRepositoryTest.java b/weaver/src/test/java/org/aspectj/weaver/bcel/ClassLoaderRepositoryTest.java new file mode 100644 index 000000000..1d668def2 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/bcel/ClassLoaderRepositoryTest.java @@ -0,0 +1,213 @@ +/* ******************************************************************* + * Copyright (c) 2006 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement initial implementation + * ******************************************************************/ + + +package org.aspectj.weaver.bcel; + +import java.io.File; +import java.lang.ref.Reference; +import java.lang.reflect.Field; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Enumeration; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import junit.framework.TestCase; + +import org.aspectj.apache.bcel.util.ClassLoaderRepository; + +/** NOT YET INCLUDED IN A FULL TEST RUN - WORK IN PROGRESS CHECKING CLASSLOADERREPOSITORY OPTIMIZATIONS */ +public class ClassLoaderRepositoryTest extends TestCase { + private File f; + private ZipFile zf; + private Enumeration entries; + private Map map; + + public void setUp() throws Exception { + f = new File("../lib/aspectj/lib/aspectjtools.jar"); + assertTrue("Couldn't find aspectjtools to test. Tried: "+f.getAbsolutePath(),f.exists()); + zf = new ZipFile(f); + entries = zf.entries(); +// ClassLoaderRepository.sharedCacheCompactFrequency = 16384; + map = getSharedMap(); + } + + public void tearDown() { + new ClassLoaderRepository((ClassLoader) null).reset(); + } + + private ClassLoaderRepository setupRepository() throws Exception { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + ClassLoader res = new URLClassLoader(new URL[]{f.toURL()},cl); + ClassLoaderRepository rep = new ClassLoaderRepository(res); + return rep; + } + + private void compareTwoRepositories() throws Exception { + ClassLoaderRepository rep1 = setupRepository(); + ClassLoaderRepository rep2 = setupRepository(); + int i = 0; + while (entries.hasMoreElements()) { + ZipEntry zfe = (ZipEntry)entries.nextElement(); + String classfileName = zfe.getName(); + if (classfileName.endsWith(".class")) { + String clazzname = classfileName.substring(0,classfileName.length()-6).replace('/','.'); + + // twice by each + rep1.loadClass(clazzname); + rep1.loadClass(clazzname); + rep2.loadClass(clazzname); + rep2.loadClass(clazzname); + i++; + } + } + System.err.println("Successfully compared "+i+" entries!!"); + System.err.println(rep1.report()); + System.err.println(rep2.report()); + } + +// private void loadOnce() throws Exception { +// ClassLoaderRepository rep = setupRepository(); +// while (entries.hasMoreElements()) { +// ZipEntry zfe = (ZipEntry) entries.nextElement(); +// String classfileName = zfe.getName(); +// if (classfileName.endsWith(".class")) { +// String clazzname = classfileName.substring(0, +// classfileName.length() - 6).replace('/', '.'); +// +// rep.loadClass(clazzname); +// } +// } +// } + + public void testMultiThreaded() throws Throwable { + ClassLoaderRepository.useSharedCache=true; +// ClassLoaderRepository.sharedCacheCompactFrequency = 200; + //loadOnce(); + TestThread threads[] = new TestThread[6]; + for (int i=0; i())", + "constructor-call(void java.lang.StringBuffer.(java.lang.String))" }); + } + + public void testId() throws IOException { + final List l = new ArrayList(); + BcelAdvice p = new BcelAdvice(null, makePointcutAll(), null, 0, -1, -1, null, null) { + public boolean implementOn(Shadow shadow) { + l.add(shadow); + return true; + } + }; + weaveTest(new String[] { "HelloWorld" }, "Id2", p); + + checkShadowSet(l, new String[] { "method-execution(void HelloWorld.main(java.lang.String[]))", + "method-call(void java.io.PrintStream.println(java.lang.String))", + "field-get(java.io.PrintStream java.lang.System.out)", "constructor-execution(void HelloWorld.())", }); + } + + // this test requires that Trace has been unzipped and placed in the correct place + // public void testTraceId() throws IOException { + // String saveClassDir = classDir; + // try { + // classDir = "testdata/dummyAspect.jar"; + // + // + // + // final List l = new ArrayList(); + // BcelAdvice p = new BcelAdvice(null, makePointcutAll(), null, 0, -1, -1, null, null) { + // public void implementOn(Shadow shadow) { + // l.add(shadow); + // } + // }; + // boolean tempRunTests = runTests; + // runTests = false; + // weaveTest(new String[] {"DummyAspect"}, "Id", p); + // runTests = tempRunTests; + // + // checkShadowSet(l, new String[] { + // "constructor-execution(void DummyAspect.())", + // // XXX waiting on parser stuff + // //"advice-execution(void DummyAspect.ajc_before_1(java.lang.Object))", + // }); + // } finally { + // classDir = saveClassDir; + // } + // } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/bcel/JImageTestCase.java b/weaver/src/test/java/org/aspectj/weaver/bcel/JImageTestCase.java new file mode 100644 index 000000000..6e89c675c --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/bcel/JImageTestCase.java @@ -0,0 +1,139 @@ +/* ******************************************************************* + * Copyright (c) 2017 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * ******************************************************************/ +package org.aspectj.weaver.bcel; + +import java.io.IOException; +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.aspectj.bridge.AbortException; +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.IMessage.Kind; +import org.aspectj.bridge.IMessageHandler; +import org.aspectj.util.LangUtil; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.bcel.ClassPathManager.ClassFile; +import org.aspectj.weaver.bcel.ClassPathManager.Entry; +import org.aspectj.weaver.bcel.ClassPathManager.JImageEntry; + +import junit.framework.TestCase; + +/** + * Exercise the JImage handling in @link {@link org.aspectj.weaver.bcel.ClassPathManager}. + * + * @author Andy Clement + */ +public class JImageTestCase extends TestCase { + + ClassPathManager cpm; + + public void setUp() throws Exception { + List paths = new ArrayList<>(); + paths.add(LangUtil.getJrtFsFilePath()); + cpm = new ClassPathManager(paths,new TestMessageHandler()); + } + + public void testOnJava9() { + if (!LangUtil.is19VMOrGreater()) { + System.out.println("SKIPPING JIMAGE TESTS AS NOT ON 1.9 OR LATER"); + } + } + + public void testBasicStructureAndCapabilities() { + if (!LangUtil.is19VMOrGreater()) return; + // Should be one entry for finding JRT contents + List entries = cpm.getEntries(); + assertEquals(1,entries.size()); + assertEquals(JImageEntry.class,entries.get(0).getClass()); + + ClassFile stringClassFile = cpm.find(UnresolvedType.JL_STRING); + assertNotNull(stringClassFile); + assertEquals("java/lang/String.class",stringClassFile.getPath()); + } + + public void testBehaviour() throws Exception { + if (!LangUtil.is19VMOrGreater()) return; + JImageEntry jie = getJImageEntry(); + + Map packageCache = JImageEntry.getPackageCache(); + assertTrue(packageCache.size()>0); + // Note: seems to be about 1625 entries in it for Java9 + Path path = packageCache.get("java/lang"); + assertEquals("modules/java.base/java/lang", path.toString()); + path = packageCache.get("java/io"); + assertEquals("modules/java.base/java/io", path.toString()); + + assertNotNull(jie.find("java/lang/String")); + assertNotNull(jie.find("java/io/File")); + // TODO test the filecache, hard because difficult to simulate collection of SoftReferences + } + + + static class TestMessageHandler implements IMessageHandler { + + @Override + public boolean handleMessage(IMessage message) throws AbortException { + return false; + } + + @Override + public boolean isIgnoring(Kind kind) { + return false; + } + + @Override + public void dontIgnore(Kind kind) { + } + + @Override + public void ignore(Kind kind) { + } + + } + + // --- + + private JImageEntry getJImageEntry() { + return (JImageEntry) cpm.getEntries().get(0); + } + + public List getAllTheClasses() { + final List result = new ArrayList<>(); + URI JRT_URI = URI.create("jrt:/"); //$NON-NLS-1$ + FileSystem fs = FileSystems.getFileSystem(JRT_URI); + Iterable roots = fs.getRootDirectories(); + try { + for (java.nio.file.Path path : roots) { + Files.walkFileTree(path, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + if (file.getNameCount()>3 && file.toString().endsWith(".class")) { + String withClassSuffix = file.subpath(2, file.getNameCount()).toString(); + result.add(withClassSuffix.substring(0,withClassSuffix.length()-".class".length())); + } + return FileVisitResult.CONTINUE; + } + }); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + return result; + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/bcel/MegaZipTestCase.java b/weaver/src/test/java/org/aspectj/weaver/bcel/MegaZipTestCase.java new file mode 100644 index 000000000..ba71761b4 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/bcel/MegaZipTestCase.java @@ -0,0 +1,107 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.bcel; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; + +import org.aspectj.weaver.AdviceKind; +import org.aspectj.weaver.WeaverTestCase; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.MemberImpl; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.ShadowMunger; +import org.aspectj.weaver.UnresolvedType; + +public class MegaZipTestCase extends WeaveTestCase { + + private File outDir; + + public MegaZipTestCase(String arg0) { + super(arg0); + } + + public void setUp() throws Exception { + super.setUp(); + outDir = WeaverTestCase.getOutdir(); + } + + public void tearDown() throws Exception { + super.tearDown(); + WeaverTestCase.removeOutDir(); + outDir = null; + } + + private BcelAdvice makeAroundMunger(final boolean matchOnlyPrintln) { + // BcelWorld world = new BcelWorld(); + final Member sig = MemberImpl.method(UnresolvedType.forName("fluffy.Aspect"), Modifier.STATIC, "aroundFun", + "(Lorg/aspectj/runtime/internal/AroundClosure;)Ljava/lang/Object;"); + + return new BcelAdvice(AdviceKind.stringToKind("around"), matchOnlyPrintln ? makePointcutPrintln() : makePointcutAll(), sig, + 0, -1, -1, null, null) { + public void specializeOn(Shadow s) { + super.specializeOn(s); + ((BcelShadow) s).initializeForAroundClosure(); + } + }; + } + + public List getShadowMungers() { + List ret = new ArrayList(); + ret.add(makeConcreteAdvice("before" + "(): call(* *.println(..)) -> static void fluffy.Aspect.before_method_call()")); + ret.add(makeConcreteAdvice("afterReturning" + + "(): call(* *.println(..)) -> static void fluffy.Aspect.afterReturning_method_call()")); + + ret.add(makeConcreteAdvice("before" + "(): execution(* *.*(..)) -> static void fluffy.Aspect.ignoreMe()")); + + ret.add(makeConcreteAdvice("afterReturning" + "(): execution(* *.*(..)) -> static void fluffy.Aspect.ignoreMe()")); + + ret.add(makeConcreteAdvice("afterThrowing" + + "(): execution(* *.*(..)) -> static void fluffy.Aspect.afterThrowing_method_execution(java.lang.Throwable)", 1)); + ret.add(makeConcreteAdvice("after" + "(): execution(* *.*(..)) -> static void fluffy.Aspect.ignoreMe()")); + + ret.add(makeAroundMunger(true)); + return ret; + } + + public void zipTest(String fileName) throws IOException { + long startTime = System.currentTimeMillis(); + File inFile = new File(WeaverTestCase.TESTDATA_PATH, fileName); + File outFile = new File(outDir, fileName); + outFile.delete(); + + world = new BcelWorld("c:/apps/java-1.3.1_04/lib/tools.jar"); + BcelWeaver weaver1 = new BcelWeaver(world); + + ZipFileWeaver weaver = new ZipFileWeaver(inFile); + + weaver1.setShadowMungers(getShadowMungers()); + + weaver.weave(weaver1, outFile); + assertTrue(outFile.lastModified() > startTime); + } + + public void testEmptyForAntJUnit() { + } + + // this is something we test every now and again. + // to try, rename as testBig and put aspectjtools.jar in testdata + public void trytestBig() throws IOException { + System.out.println("could take 80 seconds..."); + zipTest("aspectjtools.jar"); + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/bcel/MoveInstructionsWeaveTestCase.java b/weaver/src/test/java/org/aspectj/weaver/bcel/MoveInstructionsWeaveTestCase.java new file mode 100644 index 000000000..bd7c2ae65 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/bcel/MoveInstructionsWeaveTestCase.java @@ -0,0 +1,81 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.bcel; + +import java.io.IOException; +import java.util.ArrayList; + +import org.aspectj.apache.bcel.generic.InstructionFactory; +import org.aspectj.weaver.NameMangler; +import org.aspectj.weaver.Shadow; + +public class MoveInstructionsWeaveTestCase extends WeaveTestCase { + { + regenerate = false; + } + + public MoveInstructionsWeaveTestCase(String name) { + super(name); + } + + public void testHello() throws IOException { + BcelAdvice p = new BcelAdvice(null, makePointcutAll(), null, 0, -1, -1, null, null) { + public void specializeOn(Shadow s) { + super.specializeOn(s); + ((BcelShadow) s).initializeForAroundClosure(); + } + + public boolean implementOn(Shadow s) { + BcelShadow shadow = (BcelShadow) s; + LazyMethodGen newMethod = shadow.extractShadowInstructionsIntoNewMethod(NameMangler.getExtractableName(shadow + .getSignature()) + + "_extracted", 0, this.getSourceLocation(), new ArrayList(),shadow.getEnclosingClass().isInterface()); + shadow.getRange().append(shadow.makeCallToCallback(newMethod)); + + if (!shadow.isFallsThrough()) { + shadow.getRange().append(InstructionFactory.createReturn(newMethod.getReturnType())); + } + return true; + } + }; + + weaveTest("HelloWorld", "ExtractedHelloWorld", p); + } + + static int counter = 0; + + public void testFancyHello() throws IOException { + BcelAdvice p = new BcelAdvice(null, makePointcutAll(), null, 0, -1, -1, null, null) { + public void specializeOn(Shadow s) { + super.specializeOn(s); + ((BcelShadow) s).initializeForAroundClosure(); + } + + public boolean implementOn(Shadow s) { + BcelShadow shadow = (BcelShadow) s; + LazyMethodGen newMethod = + shadow.extractShadowInstructionsIntoNewMethod(NameMangler.getExtractableName(shadow + .getSignature()) + + "_extracted" + counter++, 0, this.getSourceLocation(), new ArrayList(),shadow.getEnclosingClass().isInterface()); + shadow.getRange().append(shadow.makeCallToCallback(newMethod)); + + if (!shadow.isFallsThrough()) { + shadow.getRange().append(InstructionFactory.createReturn(newMethod.getReturnType())); + } + return true; + } + }; + + weaveTest("FancyHelloWorld", "ExtractedFancyHelloWorld", p); + } +} diff --git a/weaver/src/test/java/org/aspectj/weaver/bcel/NonstaticWeaveTestCase.java b/weaver/src/test/java/org/aspectj/weaver/bcel/NonstaticWeaveTestCase.java new file mode 100644 index 000000000..1f67a6249 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/bcel/NonstaticWeaveTestCase.java @@ -0,0 +1,83 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.weaver.bcel; + +import java.io.IOException; + +import org.aspectj.weaver.*; +import org.aspectj.weaver.patterns.*; + +public class NonstaticWeaveTestCase extends WeaveTestCase { + { + regenerate = false; + } + + public NonstaticWeaveTestCase(String name) { + super(name); + } + + + public void testBefore() throws IOException { + String s = "before(): get(* *.*) -> void Aspect.ajc_before()"; + PerClause per = new PerSingleton(); + per = per.concretize(world.resolve("Aspect")); + + ShadowMunger myMunger = this.makeConcreteAdvice(s, 0, per); + + weaveTest(getStandardTargets(), "NonStaticBefore", myMunger); + } + + public void testBeforeCflow() throws IOException { + String s = "before(): get(* *.*) -> void Aspect.ajc_before()"; + PerClause per = new PatternParser("percflow(execution(void main(..)))").maybeParsePerClause(); + per.resolve(new TestScope(new String[0], new String[0], world)); + + ResolvedType onAspect = world.resolve("Aspect"); + CrosscuttingMembers xcut = new CrosscuttingMembers(onAspect,true); + onAspect.crosscuttingMembers = xcut; + + per = per.concretize(onAspect); + + ShadowMunger myMunger = this.makeConcreteAdvice(s, 0, per); + + xcut.addConcreteShadowMunger(myMunger); + + + weaveTest(getStandardTargets(), "CflowNonStaticBefore", xcut.getShadowMungers()); + } + + public void testBeforePerThis() throws IOException { + String s = "before(): call(* println(..)) -> void Aspect.ajc_before()"; + PerClause per = new PatternParser("pertarget(call(* println(..)))").maybeParsePerClause(); + per.resolve(new TestScope(new String[0], new String[0], world)); + + ResolvedType onAspect = world.resolve("Aspect"); + CrosscuttingMembers xcut = new CrosscuttingMembers(onAspect,true); + onAspect.crosscuttingMembers = xcut; + per = per.concretize(onAspect); + + ShadowMunger myMunger = this.makeConcreteAdvice(s, 0, per); + xcut.addConcreteShadowMunger(myMunger); + +// List mungers = new ArrayList(); +// mungers.add(myMunger); +// mungers.addAll(onAspect.getExtraConcreteShadowMungers()); + + + weaveTest(getStandardTargets(), "PerThisNonStaticBefore", xcut.getShadowMungers()); + } + + + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/bcel/PatternWeaveTestCase.java b/weaver/src/test/java/org/aspectj/weaver/bcel/PatternWeaveTestCase.java new file mode 100644 index 000000000..2d9af5395 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/bcel/PatternWeaveTestCase.java @@ -0,0 +1,122 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.bcel; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.patterns.ConstantPoolSimulator; +import org.aspectj.weaver.patterns.FormalBinding; +import org.aspectj.weaver.patterns.Pointcut; +import org.aspectj.weaver.patterns.SimpleScope; + +public class PatternWeaveTestCase extends WeaveTestCase { + { + regenerate = false; + } + + public PatternWeaveTestCase(String name) { + super(name); + } + + String[] none = new String[0]; + + // XXX this test is incompatible with optimizations made to weaver + + public void testPublic() throws IOException { + String[] publicHello = new String[] { "method-execution(void HelloWorld.main(java.lang.String[]))", }; + String[] publicFancyHello = new String[] { "method-execution(void FancyHelloWorld.main(java.lang.String[]))", + "method-execution(java.lang.String FancyHelloWorld.getName())", }; + checkPointcut("execution(public * *(..))", publicHello, publicFancyHello); + } + + // + // public void testPrintln() throws IOException { + // String[] callPrintlnHello = new String[] { + // "method-call(void java.io.PrintStream.println(java.lang.String))", + // }; + // String[] callPrintlnFancyHello = new String[] { + // "method-call(void java.io.PrintStream.println(java.lang.String))", + // "method-call(void java.io.PrintStream.println(java.lang.String))", + // "method-call(void java.io.PrintStream.println(java.lang.Object))", + // }; + // checkPointcut("call(* println(*))", callPrintlnHello, callPrintlnFancyHello); + // } + // + // public void testMumble() throws IOException { + // checkPointcut("call(* mumble(*))", none, none); + // } + // + // public void testFooBar() throws IOException { + // checkPointcut("call(FooBar *(..))", none, none); + // } + // + // public void testGetOut() throws IOException { + // String[] getOutHello = new String[] { + // "field-get(java.io.PrintStream java.lang.System.out)", + // }; + // + // checkPointcut("get(* java.lang.System.out)", getOutHello, getOutHello); + // } + // + // // private Pointcut makePointcut(String s) { + // // return new PatternParser(s).parsePointcut(); + // // } + // + private void checkPointcut(String pointcutSource, String[] expectedHelloShadows, String[] expectedFancyShadows) + throws IOException { + Pointcut sp = Pointcut.fromString(pointcutSource); + Pointcut rp = sp.resolve(new SimpleScope(world, FormalBinding.NONE)); + Pointcut cp = rp.concretize(ResolvedType.MISSING, ResolvedType.MISSING, 0); + + final List l = new ArrayList(); + BcelAdvice p = new BcelAdvice(null, cp, null, 0, -1, -1, null, null) { + public boolean implementOn(Shadow shadow) { + l.add(shadow); + return true; + } + }; + weaveTest(new String[] { "HelloWorld" }, "PatternWeave", p); + + checkShadowSet(l, expectedHelloShadows); + + l.clear(); + weaveTest(new String[] { "FancyHelloWorld" }, "PatternWeave", p); + + checkShadowSet(l, expectedFancyShadows); + + checkSerialize(rp); + } + + public void checkSerialize(Pointcut p) throws IOException { + ByteArrayOutputStream bo = new ByteArrayOutputStream(); + ConstantPoolSimulator cps = new ConstantPoolSimulator(); + CompressingDataOutputStream out = new CompressingDataOutputStream(bo, cps); + p.write(out); + out.close(); + + ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray()); + VersionedDataInputStream in = new VersionedDataInputStream(bi, cps); + Pointcut newP = Pointcut.read(in, null); + + assertEquals("write/read", p, newP); + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/bcel/PointcutResidueTestCase.java b/weaver/src/test/java/org/aspectj/weaver/bcel/PointcutResidueTestCase.java new file mode 100644 index 000000000..7f6f5f163 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/bcel/PointcutResidueTestCase.java @@ -0,0 +1,189 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.bcel; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; + +import org.aspectj.weaver.AdviceKind; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.CrosscuttingMembers; +import org.aspectj.weaver.MemberImpl; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.ShadowMunger; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.patterns.ConstantPoolSimulator; +import org.aspectj.weaver.patterns.Pointcut; +import org.aspectj.weaver.patterns.SimpleScope; + +public class PointcutResidueTestCase extends WeaveTestCase { + { + regenerate = false; + } + + public PointcutResidueTestCase(String name) { + super(name); + } + + String[] none = new String[0]; + + // ----- + + // ---- + + public void testArgResidue1() throws IOException { + checkMultiArgWeave("StringResidue1", + "call(* *(java.lang.Object, java.lang.Object)) && args(java.lang.String, java.lang.String)"); + } + + public void testArgResidue2() throws IOException { + checkMultiArgWeave("StringResidue2", "call(* *(java.lang.Object, java.lang.Object)) && args(.., java.lang.String)"); + } + + public void testArgResidue3() throws IOException { + checkMultiArgWeave("StringResidue3", "call(* *(java.lang.Object, java.lang.Object)) && args(java.lang.String, ..)"); + } + + // BETAX this is a beta feature. + // public void testArgResidue4() throws IOException { + // checkMultiArgWeave( + // "StringResidue4", + // "call(* *(java.lang.Object, java.lang.Object)) && args(.., java.lang.String, ..)"); + // } + + public void testMultiArgState() throws IOException { + checkWeave("StateResidue", "MultiArgHelloWorld", "call(* *(java.lang.Object, java.lang.Object)) && args(s, ..)", + new String[] { "java.lang.String" }, new String[] { "s" }); + checkWeave("StateResidue", "MultiArgHelloWorld", "call(* *(java.lang.Object, java.lang.Object)) && args(s, *)", + new String[] { "java.lang.String" }, new String[] { "s" }); + } + + public void testAdd() throws IOException { + checkDynamicWeave("AddResidue", "call(public * add(..)) && target(java.util.ArrayList)"); + checkDynamicWeave("AddResidue", "call(public * add(..)) && (target(java.util.ArrayList) || target(java.lang.String))"); + checkDynamicWeave("AddResidue", + "call(public * add(..)) && this(java.io.Serializable) && target(java.util.ArrayList) && !this(java.lang.Integer)"); + } + + public void testNot() throws IOException { + checkDynamicWeave("AddNotResidue", "call(public * add(..)) && !target(java.util.ArrayList)"); + checkDynamicWeave("AddNotResidue", "call(public * add(..)) && !(target(java.util.ArrayList) || target(java.lang.String)) "); + checkDynamicWeave("AddNotResidue", "call(public * add(..)) && target(java.lang.Object) && !target(java.util.ArrayList)"); + } + + public void testState() throws IOException { + checkWeave("AddStateResidue", "DynamicHelloWorld", "call(public * add(..)) && target(list)", + new String[] { "java.util.ArrayList" }, new String[] { "list" }); + checkWeave("AddStateResidue", "DynamicHelloWorld", "target(foo) && !target(java.lang.Integer) && call(public * add(..))", + new String[] { "java.util.ArrayList" }, new String[] { "foo" }); + checkDynamicWeave("AddResidue", "call(public * add(..)) && (target(java.util.ArrayList) || target(java.lang.String))"); + checkDynamicWeave("AddResidue", + "call(public * add(..)) && this(java.io.Serializable) && target(java.util.ArrayList) && !this(java.lang.Integer)"); + } + + public void testNoResidueArgs() throws IOException { + checkDynamicWeave("NoResidue", "call(public * add(..)) && args(java.lang.Object)"); + checkDynamicWeave("NoResidue", "call(public * add(..)) && args(*)"); + checkDynamicWeave("NoResidue", "call(public * add(..))"); + } + + // ---- cflow tests + + public void testCflowState() throws IOException { + checkWeave("CflowStateResidue", "DynamicHelloWorld", + "cflow(call(public * add(..)) && target(list)) && execution(public void main(..))", + new String[] { "java.util.ArrayList" }, new String[] { "list" }); + // checkWeave( + // "CflowStateResidue", + // "DynamicHelloWorld", + // "cflow(call(public * add(..)) && target(list)) && this(obj) && execution(public void doit(..))", + // new String[] { "java.lang.Object", "java.util.ArrayList" }, + // new String[] { "obj", "list" }); + // checkWeave( + // "AddStateResidue", + // "DynamicHelloWorld", + // "target(foo) && !target(java.lang.Integer) && call(public * add(..))", + // new String[] { "java.util.ArrayList" }, + // new String[] { "foo" }); + // checkDynamicWeave( + // "AddResidue", + // "call(public * add(..)) && (target(java.util.ArrayList) || target(java.lang.String))"); + // checkDynamicWeave( + // "AddResidue", + // "call(public * add(..)) && this(java.io.Serializable) && target(java.util.ArrayList) && !this(java.lang.Integer)"); + } + + // ---- + + private void checkDynamicWeave(String label, String pointcutSource) throws IOException { + checkWeave(label, "DynamicHelloWorld", pointcutSource, new String[0], new String[0]); + } + + private void checkMultiArgWeave(String label, String pointcutSource) throws IOException { + checkWeave(label, "MultiArgHelloWorld", pointcutSource, new String[0], new String[0]); + } + + private void checkWeave(String label, String filename, String pointcutSource, String[] formalTypes, String[] formalNames) + throws IOException { + final Pointcut sp = Pointcut.fromString(pointcutSource); + final Pointcut rp = sp.resolve(new SimpleScope(world, SimpleScope.makeFormalBindings(UnresolvedType.forNames(formalTypes), + formalNames))); + + ShadowMunger pp = new BcelAdvice(AdviceKind.Before, rp, MemberImpl.method(UnresolvedType.forName("Aspect"), + Modifier.STATIC, "ajc_before_0", + MemberImpl.typesToSignature(UnresolvedType.VOID, UnresolvedType.forNames(formalTypes), false)), 0, -1, -1, null, + null); + + ResolvedType inAspect = world.resolve("Aspect"); + CrosscuttingMembers xcut = new CrosscuttingMembers(inAspect, true); + inAspect.crosscuttingMembers = xcut; + + ShadowMunger cp = pp.concretize(inAspect, world, null); + + xcut.addConcreteShadowMunger(cp); + + // System.out.println("extras: " + inAspect.getExtraConcreteShadowMungers()); + // List advice = new ArrayList(); + // advice.add(cp); + // advice.addAll(inAspect.getExtraConcreteShadowMungers()); + weaveTest(new String[] { filename }, label, xcut.getShadowMungers()); + + checkSerialize(rp); + } + + public void weaveTest(String name, String outName, ShadowMunger planner) throws IOException { + List l = new ArrayList(1); + l.add(planner); + weaveTest(name, outName, l); + } + + public void checkSerialize(Pointcut p) throws IOException { + ByteArrayOutputStream bo = new ByteArrayOutputStream(); + ConstantPoolSimulator cps = new ConstantPoolSimulator(); + CompressingDataOutputStream out = new CompressingDataOutputStream(bo, cps); + p.write(out); + out.close(); + + ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray()); + VersionedDataInputStream in = new VersionedDataInputStream(bi, cps); + Pointcut newP = Pointcut.read(in, null); + + assertEquals("write/read", p, newP); + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/bcel/TjpWeaveTestCase.java b/weaver/src/test/java/org/aspectj/weaver/bcel/TjpWeaveTestCase.java new file mode 100644 index 000000000..b24b3f6d0 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/bcel/TjpWeaveTestCase.java @@ -0,0 +1,99 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.bcel; + +import java.io.IOException; +import java.util.Arrays; + +import org.aspectj.weaver.Advice; +import org.aspectj.weaver.AdviceKind; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.ShadowMunger; +import org.aspectj.weaver.TestUtils; +import org.aspectj.weaver.UnresolvedType; + +public class TjpWeaveTestCase extends WeaveTestCase { + { + regenerate = false; + } + + public TjpWeaveTestCase(String name) { + super(name); + } + + public void setUp() throws Exception { + super.setUp(); + behave15 = true; + } + + public void tearDown() throws Exception { + super.tearDown(); + behave15 = false; + } + + public void testStaticTjp() throws IOException { + BcelAdvice munger = new BcelAdvice(AdviceKind.stringToKind("before"), makePointcutAll(), + TestUtils.methodFromString("static void Aspect.ajc_before(org.aspectj.lang.JoinPoint$StaticPart)"), + Advice.ThisJoinPointStaticPart, -1, -1, null, null); + + weaveTest("HelloWorld", "StaticTjpBeforeHelloWorld", munger); + } + + public void testEnclosingStaticTjp() throws IOException { + BcelAdvice munger = new BcelAdvice(AdviceKind.stringToKind("before"), makePointcutAll(), + TestUtils.methodFromString("static void Aspect.ajc_before(org.aspectj.lang.JoinPoint$StaticPart)"), + Advice.ThisEnclosingJoinPointStaticPart, -1, -1, null, null); + + weaveTest("HelloWorld", "StaticEnclosingTjpBeforeHelloWorld", munger); + } + + public void testTjp() throws IOException { + BcelAdvice munger = new BcelAdvice(AdviceKind.stringToKind("before"), makePointcutAll(), + TestUtils.methodFromString("static void Aspect.ajc_before(org.aspectj.lang.JoinPoint)"), Advice.ThisJoinPoint, -1, + -1, null, null); + + weaveTest("HelloWorld", "TjpBeforeHelloWorld", munger); + } + + public void testAroundTjp() throws IOException { + BcelAdvice munger = new BcelAdvice( + AdviceKind.stringToKind("around"), + makePointcutAll(), + TestUtils + .methodFromString("static java.lang.Object Aspect.ajc_around(org.aspectj.runtime.internal.AroundClosure, org.aspectj.lang.JoinPoint)"), + Advice.ThisJoinPoint | Advice.ExtraArgument, -1, -1, null, null); + + weaveTest("HelloWorld", "TjpAroundHelloWorld", munger); + } + + public void testAround2Tjp() throws IOException { + ResolvedType rtx = world.resolve(UnresolvedType.forName("Aspect"), true); + assertTrue("Couldnt find type Aspect", !rtx.isMissing()); + BcelAdvice munger1 = new BcelAdvice( + AdviceKind.stringToKind("around"), + makePointcutAll(), + TestUtils + .methodFromString("static java.lang.Object Aspect.ajc_around(org.aspectj.runtime.internal.AroundClosure, org.aspectj.lang.JoinPoint)"), + Advice.ThisJoinPoint | Advice.ExtraArgument, -1, -1, null, rtx); + + BcelAdvice munger2 = new BcelAdvice( + AdviceKind.stringToKind("around"), + makePointcutAll(), + TestUtils + .methodFromString("static java.lang.Object Aspect.ajc_around(org.aspectj.runtime.internal.AroundClosure, org.aspectj.lang.JoinPoint)"), + Advice.ThisJoinPoint | Advice.ExtraArgument, -1, -1, null, rtx); + + weaveTest("HelloWorld", "TjpAround2HelloWorld", Arrays.asList(new ShadowMunger[] { munger1, munger2 })); + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/bcel/TraceJarWeaveTestCase.java b/weaver/src/test/java/org/aspectj/weaver/bcel/TraceJarWeaveTestCase.java new file mode 100644 index 000000000..45535407b --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/bcel/TraceJarWeaveTestCase.java @@ -0,0 +1,40 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.weaver.bcel; + +import java.io.IOException; + +public class TraceJarWeaveTestCase extends WeaveTestCase { + { + regenerate = false; + } + + public TraceJarWeaveTestCase(String name) { + super(name); + } + + + public void testTraceJar() throws IOException { + world = new BcelWorld(getTraceJar()); + BcelWeaver weaver = new BcelWeaver(world); + weaver.addLibraryAspect("MyTrace"); + UnwovenClassFile classFile + = makeUnwovenClassFile(classDir, "DynamicHelloWorld", outDirPath); + + weaver.addClassFile(classFile,false); + weaver.prepareForWeave(); + + weaveTestInner(weaver, classFile, "DynamicHelloWorld", "TraceJarHello"); + } +} diff --git a/weaver/src/test/java/org/aspectj/weaver/bcel/UtilityTestCase.java b/weaver/src/test/java/org/aspectj/weaver/bcel/UtilityTestCase.java new file mode 100644 index 000000000..8083c1532 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/bcel/UtilityTestCase.java @@ -0,0 +1,52 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.weaver.bcel; +import java.io.*; + +import junit.framework.TestCase; + +public class UtilityTestCase extends TestCase { + + public UtilityTestCase(String name) { + super(name); + } + + public void disassembleTest(String name) throws IOException { + BcelWorld world = new BcelWorld("../weaver/bin"); +// world.setFastDelegateSupport(false); + world.addPath(WeaveTestCase.classDir); + + LazyClassGen clazz = new LazyClassGen(BcelWorld.getBcelObjectType(world.resolve(name))); + clazz.print(); + System.out.println(); + } + + + public void testHelloWorld() throws IOException { + disassembleTest("Test"); + } + public void testFancyHelloWorld() throws IOException { + disassembleTest("FancyHelloWorld"); + } +// public void testSwitchy() throws IOException { +// disassembleTest("TestSwitchy"); +// } + + public static void main(String[] args) throws IOException { + BcelWorld world = new BcelWorld(); + LazyClassGen clazz = new LazyClassGen(BcelWorld.getBcelObjectType(world.resolve(args[0]))); + clazz.print(); + } +} + diff --git a/weaver/src/test/java/org/aspectj/weaver/bcel/WeaveOrderTestCase.java b/weaver/src/test/java/org/aspectj/weaver/bcel/WeaveOrderTestCase.java new file mode 100644 index 000000000..f22805f11 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/bcel/WeaveOrderTestCase.java @@ -0,0 +1,149 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.weaver.bcel; + +import org.aspectj.weaver.patterns.*; +import org.aspectj.weaver.*; + +/**. + */ +public class WeaveOrderTestCase extends WeaveTestCase { + { + regenerate = false; + } + + public WeaveOrderTestCase(String name) { + super(name); + } + + + public void testLexicalOrder() { + Advice a1 = + makeConcreteAdvice(AdviceKind.Before, UnresolvedType.OBJECT, UnresolvedType.OBJECT, 1); + Advice a2 = + makeConcreteAdvice(AdviceKind.Before, UnresolvedType.OBJECT, UnresolvedType.THROWABLE, 2); + + assertEquals(-1, a2.compareTo(a1)); + assertEquals(+1, a1.compareTo(a2)); + } + + public void testLexicalOrderWithAfter() { + Advice a1 = + makeConcreteAdvice(AdviceKind.Before, UnresolvedType.OBJECT, UnresolvedType.OBJECT, 1); + Advice a2 = + makeConcreteAdvice(AdviceKind.After, UnresolvedType.OBJECT, UnresolvedType.THROWABLE, 2); + + assertEquals(+1, a2.compareTo(a1)); + assertEquals(-1, a1.compareTo(a2)); + + a1 = + makeConcreteAdvice(AdviceKind.After, UnresolvedType.OBJECT, UnresolvedType.OBJECT, 1); + a2 = + makeConcreteAdvice(AdviceKind.After, UnresolvedType.OBJECT, UnresolvedType.THROWABLE, 2); + + assertEquals(+1, a2.compareTo(a1)); + assertEquals(-1, a1.compareTo(a2)); + } + + public void testSubtypes() { + Advice a1 = + makeConcreteAdvice(AdviceKind.Before, UnresolvedType.OBJECT, UnresolvedType.OBJECT, 1); + Advice a2 = + makeConcreteAdvice(AdviceKind.Before, UnresolvedType.THROWABLE, UnresolvedType.OBJECT, 1); + Advice a3 = + makeConcreteAdvice(AdviceKind.Before, UnresolvedType.forName("java.lang.String"), UnresolvedType.OBJECT, 1); + + assertEquals(+1, a2.compareTo(a1)); + assertEquals(-1, a1.compareTo(a2)); + + assertEquals(+1, a3.compareTo(a1)); + assertEquals(-1, a1.compareTo(a3)); + + assertEquals(0, a3.compareTo(a2)); + assertEquals(0, a2.compareTo(a3)); + } + + + public void testDominates() { + Declare dom = + new PatternParser("declare precedence: java.lang.String, java.lang.Throwable").parseDeclare(); + //??? concretize dom + ResolvedType aType = world.resolve("Aspect"); + CrosscuttingMembers xcut = new CrosscuttingMembers(aType,true); + aType.crosscuttingMembers = xcut; + xcut.addDeclare(dom); + world.getCrosscuttingMembersSet().addFixedCrosscuttingMembers(aType); + + Advice a1 = + makeConcreteAdvice(AdviceKind.Before, UnresolvedType.OBJECT, UnresolvedType.OBJECT, 1); + Advice a2 = + makeConcreteAdvice(AdviceKind.Before, UnresolvedType.OBJECT, UnresolvedType.THROWABLE, 2); + Advice a3 = + makeConcreteAdvice(AdviceKind.Before, UnresolvedType.OBJECT, UnresolvedType.forName("java.lang.String"), 2); + + assertEquals(-1, a2.compareTo(a1)); + assertEquals(+1, a1.compareTo(a2)); + + assertEquals(-1, a3.compareTo(a1)); + assertEquals(+1, a1.compareTo(a3)); + + + assertEquals(+1, a3.compareTo(a2)); + assertEquals(-1, a2.compareTo(a3)); + } + + public void testDominatesHarder() { + Declare dom = + new PatternParser("declare precedence: *, java.lang.String, java.lang.Throwable").parseDeclare(); + //??? concretize dom + ResolvedType aType = world.resolve("Aspect"); + CrosscuttingMembers xcut = new CrosscuttingMembers(aType,true); + aType.crosscuttingMembers = xcut; + xcut.addDeclare(dom); + world.getCrosscuttingMembersSet().addFixedCrosscuttingMembers(aType); + + Advice a1 = + makeConcreteAdvice(AdviceKind.Before, UnresolvedType.OBJECT, UnresolvedType.OBJECT, 2); + Advice a2 = + makeConcreteAdvice(AdviceKind.Before, UnresolvedType.OBJECT, UnresolvedType.THROWABLE, 1); + Advice a3 = + makeConcreteAdvice(AdviceKind.Before, UnresolvedType.OBJECT, UnresolvedType.forName("java.lang.String"), 1); + + assertEquals(-1, a2.compareTo(a1)); + assertEquals(+1, a1.compareTo(a2)); + + assertEquals(-1, a3.compareTo(a1)); + assertEquals(+1, a1.compareTo(a3)); + + + assertEquals(+1, a3.compareTo(a2)); + assertEquals(-1, a2.compareTo(a3)); + } + + + + + private Advice makeConcreteAdvice(AdviceKind kind, UnresolvedType declaringAspect, + UnresolvedType concreteAspect, int lexicalPosition) + { + Advice a1 = new BcelAdvice(kind, makeResolvedPointcut("this(*)"), + MemberImpl.method(declaringAspect, 0, "foo", "()V"), + 0, lexicalPosition, lexicalPosition, null, null); + a1 = (Advice)a1.concretize(concreteAspect.resolve(world), world, null); + return a1; + } + + + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/bcel/WeaveTestCase.java b/weaver/src/test/java/org/aspectj/weaver/bcel/WeaveTestCase.java new file mode 100644 index 000000000..088a9a9d0 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/bcel/WeaveTestCase.java @@ -0,0 +1,318 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.bcel; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.aspectj.apache.bcel.Constants; +import org.aspectj.apache.bcel.generic.InstructionFactory; +import org.aspectj.apache.bcel.generic.InstructionList; +import org.aspectj.apache.bcel.generic.InvokeInstruction; +import org.aspectj.apache.bcel.generic.Type; +import org.aspectj.testing.util.TestUtil; +import org.aspectj.util.FileUtil; +import org.aspectj.util.LangUtil; +import org.aspectj.weaver.Advice; +import org.aspectj.weaver.WeaverTestCase; +import org.aspectj.weaver.ShadowMunger; +import org.aspectj.weaver.patterns.FormalBinding; +import org.aspectj.weaver.patterns.PerClause; +import org.aspectj.weaver.patterns.Pointcut; +import org.aspectj.weaver.patterns.SimpleScope; + +import junit.framework.TestCase; + +public abstract class WeaveTestCase extends TestCase { + + public boolean regenerate = false; + public boolean runTests = true; + public boolean behave15 = false; + + File outDir; + String outDirPath; + + public BcelWorld world = new BcelWorld(); + { + world.addPath(classDir); + // Some of the tests in here rely on comparing output from dumping the delegates - if + // we are using ASM delegates we don't know the names of parameters (they are irrelevant...) + // and are missing from the dumping of asm delegates. This switch ensures we + // continue to use BCEL for these tests. + // world.setFastDelegateSupport(false); + } + + public WeaveTestCase(String name) { + super(name); + } + + @Override + public void setUp() throws Exception { + outDir = WeaverTestCase.getOutdir(); + outDirPath = outDir.getAbsolutePath(); + } + + @Override + public void tearDown() throws Exception { + super.tearDown(); + WeaverTestCase.removeOutDir(); + outDir = null; + outDirPath = null; + } + + public static InstructionList getAdviceTag(BcelShadow shadow, String where) { + String methodName = "ajc_" + where + "_" + shadow.getKind().toLegalJavaIdentifier(); + + InstructionFactory fact = shadow.getFactory(); + InvokeInstruction il = fact.createInvoke("Aspect", methodName, Type.VOID, new Type[] {}, Constants.INVOKESTATIC); + return new InstructionList(il); + } + + public void weaveTest(String name, String outName, ShadowMunger planner) throws IOException { + List l = new ArrayList(1); + l.add(planner); + weaveTest(name, outName, l); + } + + // static String classDir = "../weaver/bin"; + static String classDir = WeaverTestCase.TESTDATA_PATH + File.separator + "bin"; + + public void weaveTest(String name, String outName, List planners) throws IOException { + BcelWeaver weaver = new BcelWeaver(world); + try { + if (behave15) + world.setBehaveInJava5Way(true); + + UnwovenClassFile classFile = makeUnwovenClassFile(classDir, name, outDirPath); + + weaver.addClassFile(classFile, false); + weaver.setShadowMungers(planners); + weaveTestInner(weaver, classFile, name, outName); + } finally { + if (behave15) + world.setBehaveInJava5Way(false); + } + } + + protected void weaveTestInner(BcelWeaver weaver, UnwovenClassFile classFile, String name, String outName) throws IOException { + // int preErrors = currentResult.errorCount(); + BcelObjectType classType = BcelWorld.getBcelObjectType(world.resolve(classFile.getClassName())); + LazyClassGen gen = weaver.weave(classFile, classType); + if (gen == null) { + // we didn't do any weaving, but let's make a gen anyway + gen = classType.getLazyClassGen(); // new LazyClassGen(classType); + } + try { + String filenameToUse = findMostRelevantFile(outName); + checkClass(gen, outDirPath, filenameToUse); + if (runTests) { + System.out.println("*******RUNNING: " + outName + " " + name + " *******"); + TestUtil.runMain(makeClassPath(outDirPath), name); + } + } catch (Error e) { + System.err.println("Comparing to " + outName + ".txt"); + gen.print(System.err); + throw e; + } catch (RuntimeException e) { + gen.print(System.err); + throw e; + } + } + + public String findMostRelevantFile(String name) { + double version = LangUtil.getVmVersion(); + while (version > 0) { + String possibleFileName = name+"."+Double.toString(version)+".txt"; + if (new File(TESTDATA_DIR, possibleFileName).exists()) { + return possibleFileName; + } + version--; + } + // Use the standard file + return name+".txt"; + } + + public String makeClassPath(String outDir) { + return outDir + File.pathSeparator + getTraceJar() + File.pathSeparator + classDir + File.pathSeparator + + System.getProperty("java.class.path"); + } + + /** + * '/' in the name indicates the location of the class + */ + public static UnwovenClassFile makeUnwovenClassFile(String classDir, String name, String outDir) throws IOException { + File outFile = new File(outDir, name + ".class"); + if (classDir.endsWith(".jar")) { + String fname = name + ".class"; + UnwovenClassFile ret = new UnwovenClassFile(outFile.getAbsolutePath(), FileUtil.readAsByteArray(FileUtil + .getStreamFromZip(classDir, fname))); + return ret; + } else { + File inFile = new File(classDir, name + ".class"); + return new UnwovenClassFile(outFile.getAbsolutePath(), FileUtil.readAsByteArray(inFile)); + } + } + + public void checkClass(LazyClassGen gen, String outDir, String expectedFile) throws IOException { + if (regenerate) + genClass(gen, outDir, expectedFile); + else + realCheckClass(gen, outDir, expectedFile); + } + + static final File TESTDATA_DIR = new File(WeaverTestCase.TESTDATA_PATH); + + void genClass(LazyClassGen gen, String outDir, String expectedFile) throws IOException { + // ClassGen b = getJavaClass(outDir, className); + FileOutputStream out = new FileOutputStream(new File(TESTDATA_DIR, expectedFile)); + PrintStream ps = new PrintStream(out); + gen.print(ps); + ps.flush(); + + } + + void realCheckClass(LazyClassGen gen, String outDir, String expectedFile) throws IOException { + TestUtil.assertMultiLineStringEquals(expectedFile/* "classes" */, + FileUtil.readAsString(new File(TESTDATA_DIR, expectedFile)), gen.toLongString()); + } + + // ---- + public ShadowMunger makeConcreteAdvice(String mungerString) { + return makeConcreteAdvice(mungerString, 0, null); + } + + public ShadowMunger makeConcreteAdvice(String mungerString, int extraArgFlag) { + return makeConcreteAdvice(mungerString, extraArgFlag, null); + } + + protected ShadowMunger makeConcreteAdvice(String mungerString, int extraArgFlag, PerClause perClause) { + Advice myMunger = BcelTestUtils.shadowMunger(world, mungerString, extraArgFlag); + + // PerSingleton s = new PerSingleton(); + // s.concretize(world.resolve("Aspect")); + // System.err.println(((KindedPointcut)myMunger.getPointcut().getPointcut()).getKind()); + Advice cm = (Advice) myMunger.concretize(myMunger.getDeclaringAspect().resolve(world), world, perClause); + return cm; + } + + public ShadowMunger makeAdviceField(String kind, String extraArgType) { + return makeConcreteAdvice(kind + "(): get(* *.*) -> static void Aspect.ajc_" + kind + "_field_get(" + extraArgType + ")", 1); + } + + public List makeAdviceAll(String kind, boolean matchOnlyPrintln) { + List ret = new ArrayList(); + if (matchOnlyPrintln) { + ret.add(makeConcreteAdvice(kind + "(): call(* *.println(..)) -> static void Aspect.ajc_" + kind + "_method_execution()")); + } else { + ret.add(makeConcreteAdvice(kind + "(): call(* *.*(..)) -> static void Aspect.ajc_" + kind + "_method_call()")); + ret.add(makeConcreteAdvice(kind + "(): call(*.new(..)) -> static void Aspect.ajc_" + kind + "_constructor_call()")); + ret.add(makeConcreteAdvice(kind + "(): execution(* *.*(..)) -> static void Aspect.ajc_" + kind + "_method_execution()")); + ret.add(makeConcreteAdvice(kind + "(): execution(*.new(..)) -> static void Aspect.ajc_" + kind + + "_constructor_execution()")); + // ret.add( + // makeConcreteMunger( + // kind + // + "(): staticinitialization(*) -> static void Aspect.ajc_" + // + kind + // + "_staticinitialization()")); + ret.add(makeConcreteAdvice(kind + "(): get(* *.*) -> static void Aspect.ajc_" + kind + "_field_get()")); + // ret.add( + // makeConcreteMunger( + // kind + "(): set(* *.*) -> static void Aspect.ajc_" + kind + "_field_set()")); + // XXX no test for advice execution, staticInitialization or (god help us) preInitialization + } + return ret; + } + + public List makeAdviceAll(final String kind) { + return makeAdviceAll(kind, false); + } + + public Pointcut makePointcutAll() { + return makeConcretePointcut("get(* *.*) || call(* *.*(..)) || execution(* *.*(..)) || call(*.new(..)) || execution(*.new(..))"); + } + + public Pointcut makePointcutNoZeroArg() { + return makeConcretePointcut("call(* *.*(*, ..)) || execution(* *.*(*, ..)) || call(*.new(*, ..)) || execution(*.new(*, ..))"); + } + + public Pointcut makePointcutPrintln() { + return makeConcretePointcut("call(* *.println(..))"); + } + + public Pointcut makeConcretePointcut(String s) { + return makeResolvedPointcut(s).concretize(null, null, 0); + } + + public Pointcut makeResolvedPointcut(String s) { + Pointcut pointcut0 = Pointcut.fromString(s); + return pointcut0.resolve(new SimpleScope(world, FormalBinding.NONE)); + } + + // ---- + + public String[] getStandardTargets() { + return new String[] { "HelloWorld", "FancyHelloWorld" }; + } + + public String getTraceJar() { + return WeaverTestCase.TESTDATA_PATH + "/tracing.jar"; + } + + // ---- + + protected void weaveTest(String[] inClassNames, String outKind, ShadowMunger patternMunger) throws IOException { + for (int i = 0; i < inClassNames.length; i++) { + String inFileName = inClassNames[i]; + weaveTest(inFileName, outKind + inFileName, patternMunger); + } + } + + protected void weaveTest(String[] inClassNames, String outKind, List patternMungers) throws IOException { + for (int i = 0; i < inClassNames.length; i++) { + String inFileName = inClassNames[i]; + weaveTest(inFileName, outKind + inFileName, patternMungers); + } + } + + protected List addLexicalOrder(List l) { + int i = 10; + for (ShadowMunger element: l) { + ((Advice)element).setLexicalPosition(i += 10); + } + return l; + } + + // XXX cut-and-paster from IdWeaveTestCase + public void checkShadowSet(List l, String[] ss) { + outer: for (int i = 0, len = ss.length; i < len; i++) { + // inner: + for (Iterator j = l.iterator(); j.hasNext();) { + BcelShadow shadow = (BcelShadow) j.next(); + String shadowString = shadow.toString(); + if (shadowString.equals(ss[i])) { + j.remove(); + continue outer; + } + } + assertTrue("didn't find " + ss[i] + " in " + l, false); + } + assertTrue("too many things in " + l, l.size() == 0); + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/bcel/WorldTestCase.java b/weaver/src/test/java/org/aspectj/weaver/bcel/WorldTestCase.java new file mode 100644 index 000000000..65d079543 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/bcel/WorldTestCase.java @@ -0,0 +1,163 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.bcel; + +import java.lang.reflect.Modifier; + +import org.aspectj.weaver.Advice; +import org.aspectj.weaver.WeaverTestCase; +import org.aspectj.weaver.CommonWorldTests; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.MemberImpl; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.ShadowMunger; +import org.aspectj.weaver.TestUtils; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.World; + +/** + * This is a test case for the nameType parts of worlds. + */ +public class WorldTestCase extends CommonWorldTests { + + private final BcelWorld world = new BcelWorld(WeaverTestCase.TESTDATA_PATH + "/tracing.jar"); + + protected World getWorld() { + return world; + } + + protected boolean getSupportsAutoboxing() { + return true; + } + + // XXX fix the various XXXs before expecting this test to work + public void xtestTraceJar() { + ResolvedType trace = world.resolve(UnresolvedType.forName("Trace"), true); + assertTrue("Couldnt find type Trace", !trace.isMissing()); + fieldsTest(trace, Member.NONE); + /* Member constr = */TestUtils.methodFromString("void Trace.()"); + // XXX need attribute fix - + // methodsTest(trace, new Member[] { constr }); + + interfacesTest(trace, ResolvedType.NONE); + superclassTest(trace, UnresolvedType.OBJECT); + isInterfaceTest(trace, false); + isClassTest(trace, false); + isAspectTest(trace, true); + + pointcutsTest(trace, new Member[] { MemberImpl.pointcut(trace, "traced", "(Ljava/lang/Object;)V"), }); + + modifiersTest(trace.findPointcut("traced"), Modifier.PUBLIC | Modifier.ABSTRACT); + + mungersTest( + trace, + new ShadowMunger[] { + BcelTestUtils.shadowMunger(world, "before(foo): traced(foo) -> void Trace.ajc_before_4(java.lang.Object))", + 0), + BcelTestUtils + .shadowMunger( + world, + "afterReturning(foo): traced(foo) -> void Trace.ajc_afterreturning_3(java.lang.Object, java.lang.Object))", + Advice.ExtraArgument), + BcelTestUtils + .shadowMunger( + world, + "around(): execution(* doit(..)) -> java.lang.Object Trace.ajc_around_2(org.aspectj.runtime.internal.AroundClosure))", + Advice.ExtraArgument), + BcelTestUtils + .shadowMunger( + world, + "around(foo): traced(foo) -> java.lang.Object Trace.ajc_around_1(java.lang.Object, org.aspectj.runtime.internal.AroundClosure))", + Advice.ExtraArgument), }); + + ResolvedType myTrace = world.resolve(UnresolvedType.forName("MyTrace"), true); + assertTrue("Couldnt find type MyTrace", !myTrace.isMissing()); + + interfacesTest(myTrace, ResolvedType.NONE); + superclassTest(myTrace, trace); + isInterfaceTest(myTrace, false); + isClassTest(myTrace, false); + isAspectTest(myTrace, true); + + // XXX need attribute fix - + // fieldsTest(myTrace, Member.NONE); + + pointcutsTest(trace, new Member[] { MemberImpl.pointcut(trace, "traced", "(Ljava/lang/Object;)V"), }); + + modifiersTest(myTrace.findPointcut("traced"), Modifier.PUBLIC); + + // this tests for declared mungers + mungersTest(myTrace, ShadowMunger.NONE); + + } + + public void testIterator() { + int abstractPublic = Modifier.ABSTRACT | Modifier.PUBLIC; + ResolvedType iter = world.getCoreType(UnresolvedType.forRawTypeName("java.util.Iterator")); + + modifiersTest(iter, abstractPublic | Modifier.INTERFACE); + fieldsTest(iter, ResolvedMember.NONE); + methodsTest(iter, new Member[] { + MemberImpl.method(iter, 0, "hasNext", "()Z"), + MemberImpl.method(iter, 0, "remove", "()V"), + MemberImpl.method(iter, 0, "next", "()Ljava/lang/Object;"), + MemberImpl.method(iter, 0, "forEachRemaining", "(Ljava/util/function/Consumer;)V") +// default void forEachRemaining(Consumer action) { +// Objects.requireNonNull(action); +// while (hasNext()) +// action.accept(next()); +// } + }); + ResolvedMember remove = iter.lookupMethod(MemberImpl.method(iter, 0, "remove", "()V")); + assertNotNull("iterator doesn't have remove", remove); + modifiersTest(remove, Modifier.PUBLIC); // no longer abstract in Java8 (default instead) + exceptionsTest(remove, UnresolvedType.NONE); + + ResolvedMember clone = iter.lookupMethod(MemberImpl.method(UnresolvedType.OBJECT, 0, "clone", "()Ljava/lang/Object;")); + assertNotNull("iterator doesn't have clone", clone); + // AV: JRockit Object.clone() is not native.. corrupted test here: + // modifiersTest(clone, Modifier.PROTECTED | Modifier.NATIVE); + assertTrue("should be protected" + clone.toString(), Modifier.isProtected(clone.getModifiers())); + exceptionsTest(clone, UnresolvedType.forNames(new String[] { "java.lang.CloneNotSupportedException" })); + + interfacesTest(iter, ResolvedType.NONE); + superclassTest(iter, UnresolvedType.OBJECT); + pointcutsTest(iter, ResolvedMember.NONE); + mungersTest(iter, ShadowMunger.NONE); + isInterfaceTest(iter, true); + isClassTest(iter, false); + isAspectTest(iter, false); + } + + public void testObjectCoersion() { + assertCouldBeCoercibleFrom("java.lang.Object", "java.lang.String"); + assertCouldBeCoercibleFrom("java.lang.Integer", "java.lang.Object"); + assertCouldBeCoercibleFrom("java.io.Serializable", "java.lang.Runnable"); + assertCouldBeCoercibleFrom("java.util.Stack", "java.lang.Runnable"); + assertCouldNotBeCoercibleFrom("java.lang.Runnable", "java.lang.Integer"); + assertCouldNotBeCoercibleFrom("java.lang.Integer", "java.lang.String"); + assertCouldNotBeCoercibleFrom("java.lang.Integer", "java.lang.Runnable"); + } + + // ---- + + private void assertCouldBeCoercibleFrom(String a, String b) { + isCoerceableFromTest(world.resolve(a), world.resolve(b), true); + } + + private void assertCouldNotBeCoercibleFrom(String a, String b) { + isCoerceableFromTest(world.resolve(a), world.resolve(b), false); + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/bcel/ZipFileWeaver.java b/weaver/src/test/java/org/aspectj/weaver/bcel/ZipFileWeaver.java new file mode 100644 index 000000000..3be5ae332 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/bcel/ZipFileWeaver.java @@ -0,0 +1,39 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.weaver.bcel; + +import java.io.File; +import java.io.IOException; + + +//XXX delete very soon +public class ZipFileWeaver { + File inFile; + public ZipFileWeaver(File inFile) { + super(); + this.inFile = inFile; + } + + public void weave(BcelWeaver weaver, File outFile) throws IOException { + int count = 0; + long startTime = System.currentTimeMillis(); + weaver.addJarFile(inFile, new File("."),false); + weaver.weave(outFile); + long stopTime = System.currentTimeMillis(); + + + System.out.println("handled " + count + " entries, in " + + (stopTime-startTime)/1000. + " seconds"); + } +} diff --git a/weaver/src/test/java/org/aspectj/weaver/bcel/ZipTestCase.java b/weaver/src/test/java/org/aspectj/weaver/bcel/ZipTestCase.java new file mode 100644 index 000000000..71f3bc926 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/bcel/ZipTestCase.java @@ -0,0 +1,121 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.bcel; + +import java.io.File; +import java.io.IOException; +import java.util.Collection; + +import junit.framework.TestCase; + +import org.aspectj.weaver.WeaverTestCase; + +public class ZipTestCase extends TestCase { + + File outDir; + + /** + * Constructor for ZipTestCase. + * + * @param arg0 + */ + public ZipTestCase(String arg0) { + super(arg0); + } + + public void setUp() { + outDir = WeaverTestCase.getOutdir(); + } + + public void tearDown() { + WeaverTestCase.removeOutDir(); + outDir = null; + } + + public void zipTest(String fileName, String aspectjar) throws IOException { + zipTest(fileName, aspectjar, false); + } + + public void zipTest(String fileName, String aspectjar, boolean isInJar) throws IOException { + File inFile = new File(fileName); + File outFile = new File(outDir, inFile.getName()); + BcelWorld world = new BcelWorld(); + BcelWeaver weaver = new BcelWeaver(world); + + long startTime = System.currentTimeMillis(); + // ensure that a fast cpu doesn't complete file write within 1000ms of start + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + weaver.addJarFile(inFile, new File("."), false); + + if (aspectjar != null) { + if (isInJar) { + weaver.addJarFile(new File(aspectjar), new File("."), false); + } else { + weaver.addLibraryJarFile(new File(aspectjar)); + world.addPath(new File(aspectjar).toString()); + } + } + weaver.addLibraryJarFile(new File(WeaverTestCase.TESTDATA_PATH + "/Regex.jar")); // ??? + world.addPath(new File(WeaverTestCase.TESTDATA_PATH + "/Regex.jar").getPath()); + + Collection woven = weaver.weave(outFile); + long stopTime = System.currentTimeMillis(); + + System.out.println("handled " + woven.size() + " entries, in " + (stopTime - startTime) / 1000. + " seconds"); + // last mod times on linux (at least) are only accurate to the second. + // with fast disks and a fast cpu the following test can fail if the write completes less than + // 1000 milliseconds after the start of the test, hence the 1000ms delay added above. + assertTrue(outFile.lastModified() > startTime); + } + + public void testSmall() throws IOException { + zipTest(WeaverTestCase.TESTDATA_PATH + "/Regex.jar", null); + } + + public void testSmallWithAspects() throws IOException { + System.out.println("could take 4 seconds..."); + zipTest(WeaverTestCase.TESTDATA_PATH + "/Regex.jar", WeaverTestCase.TESTDATA_PATH + "/megatrace.jar"); + } + + public void testSmallWithAspectsNoWeave() throws IOException { + System.out.println("could take 4 seconds..."); + zipTest(WeaverTestCase.TESTDATA_PATH + "/Regex.jar", WeaverTestCase.TESTDATA_PATH + "/megatraceNoweave.jar", true); + } + + public void testBig() throws IOException { + System.out.println("could take 4 seconds..."); + zipTest("../lib/bcel/bcel.jar", null); + } + + public void testBigWithEasyNoTrace() throws IOException { + System.out.println("could take 4 seconds..."); + zipTest("../lib/bcel/bcel.jar", WeaverTestCase.TESTDATA_PATH + "/megatrace0easy.jar"); + } + + // this is something we test every now and again. + public void xtestBigWithHardNoTrace() throws IOException { + System.out.println("could take 24 seconds..."); + zipTest("../lib/bcel/bcel.jar", WeaverTestCase.TESTDATA_PATH + "/megatrace0hard.jar"); + } + + public void xtestBigWithAspects() throws IOException { + System.out.println("could take 40 seconds..."); + zipTest("../lib/bcel/bcel.jar", WeaverTestCase.TESTDATA_PATH + "/megatrace.jar"); + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/patterns/AnnotationPatternMatchingTestCase.java b/weaver/src/test/java/org/aspectj/weaver/patterns/AnnotationPatternMatchingTestCase.java new file mode 100644 index 000000000..753e7a6b0 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/patterns/AnnotationPatternMatchingTestCase.java @@ -0,0 +1,252 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM Corporation. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement initial implementation + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestCase; + +import org.aspectj.bridge.AbortException; +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.IMessage.Kind; +import org.aspectj.bridge.IMessageHandler; +import org.aspectj.util.LangUtil; +import org.aspectj.weaver.WeaverTestCase; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.bcel.BcelWorld; + +/* + * Sample types that this program uses are: + + import p.SimpleAnnotation; + + @SimpleAnnotation(id=2) + public class AnnotatedClass { + + @SimpleAnnotation(id=3) + public void m1() { } + + @SimpleAnnotation(id=4) + int i; + } + + * with SimpleAnnotation defined as: + + package p; + import java.lang.annotation.*; + + @Retention(RetentionPolicy.RUNTIME) + public @interface SimpleAnnotation { + int id(); + String fruit() default "bananas"; + } + + *NOTE NOTE NOTE NOTE NOTE NOTE NOTE* + If you need to rebuild the test data code, run 'ant -f build-15.xml' in the + testdata directory. + + */ +public class AnnotationPatternMatchingTestCase extends TestCase { + + private BcelWorld world; + private AnnotationTypePattern fooTP, simpleAnnotationTP; + + private ResolvedType loadType(String name) { + if (world == null) { + world = new BcelWorld(WeaverTestCase.TESTDATA_PATH + "/testcode.jar"); + world.setBehaveInJava5Way(true); + } + return world.resolve(name); + } + + private void initAnnotationTypePatterns() { + PatternParser p = new PatternParser("@Foo"); + fooTP = p.maybeParseAnnotationPattern(); + fooTP = fooTP.resolveBindings(makeSimpleScope(), new Bindings(3), true); + + p = new PatternParser("@p.SimpleAnnotation"); + simpleAnnotationTP = p.maybeParseAnnotationPattern(); + simpleAnnotationTP = simpleAnnotationTP.resolveBindings(makeSimpleScope(), new Bindings(3), true); + } + + public void testAnnotationPatternMatchingOnTypes() { + if (LangUtil.is15VMOrGreater()) { + ResolvedType rtx = loadType("AnnotatedClass"); + initAnnotationTypePatterns(); + + // One should match + assertTrue("@Foo should not match on the AnnotatedClass", fooTP.matches(rtx).alwaysFalse()); + assertTrue("@SimpleAnnotation should match on the AnnotatedClass", simpleAnnotationTP.matches(rtx).alwaysTrue()); + } + + } + + static class MyMessageHandler implements IMessageHandler { + public List messages = new ArrayList(); + + public boolean handleMessage(IMessage message) throws AbortException { + messages.add(message); + return false; + } + + public boolean isIgnoring(Kind kind) { + return false; + } + + public void dontIgnore(IMessage.Kind kind) { + } + + public void ignore(Kind kind) { + } + } + + public void testReferenceToNonAnnotationType() { + // ResolvedType rtx = + loadType("AnnotatedClass"); // inits the world + PatternParser p = new PatternParser("@java.lang.String"); + + MyMessageHandler mh = new MyMessageHandler(); + world.setMessageHandler(mh); + AnnotationTypePattern atp = p.maybeParseAnnotationPattern(); + atp = atp.resolveBindings(makeSimpleScope(), new Bindings(3), true); + + assertTrue("Expected 1 error message but got " + mh.messages.size(), mh.messages.size() == 1); + + String expected = "Type referred to is not an annotation type"; + String msg = ((IMessage) mh.messages.get(0)).toString(); + assertTrue("Expected: " + expected + " but got " + msg, msg.indexOf(expected) != -1); + } + + public void testReferenceViaFormalToNonAnnotationType() { + // ResolvedType rtx = + loadType("AnnotatedClass"); // inits the world + PatternParser p = new PatternParser("a"); + + MyMessageHandler mh = new MyMessageHandler(); + world.setMessageHandler(mh); + AnnotationTypePattern atp = p.parseAnnotationNameOrVarTypePattern(); + atp = atp.resolveBindings(makeSimpleScope(), new Bindings(3), true); + + assertTrue("Expected 3 error messages but got " + mh.messages.size(), mh.messages.size() == 3); + + String expected = "Type referred to is not an annotation type"; + String msg = ((IMessage) mh.messages.get(0)).toString(); + assertTrue("Expected: " + expected + " but got " + msg, msg.indexOf(expected) != -1); + + // expected = "Binding not supported in @pcds (1.5.0 M1 limitation): null"; + // msg = ((IMessage)mh.messages.get(1)).toString(); + // assertTrue("Expected: "+expected+" but got "+msg,msg.indexOf(expected)!=-1); + } + + public TestScope makeSimpleScope() { + return new TestScope(new String[] { "int", "java.lang.String" }, new String[] { "a", "b" }, world); + } + + public void testUnresolvedAnnotationTypes() { + ResolvedType rtx = loadType("AnnotatedClass"); + + PatternParser p = new PatternParser("@Foo"); + AnnotationTypePattern fooTP = p.maybeParseAnnotationPattern(); + try { + fooTP.matches(rtx); + fail("Should have failed with illegal state exception, fooTP is not resolved"); + } catch (IllegalStateException ise) { + // Correct! + } + } + + public void testAnnotationPatternMatchingOnMethods() { + if (LangUtil.is15VMOrGreater()) { + ResolvedType rtx = loadType("AnnotatedClass"); + ResolvedMember aMethod = rtx.getDeclaredMethods()[1]; + + assertTrue("Haven't got the right method, I'm looking for 'm1()': " + aMethod.getName(), aMethod.getName().equals("m1")); + + initAnnotationTypePatterns(); + + // One should match + assertTrue("@Foo should not match on the AnnotatedClass.m1() method", fooTP.matches(aMethod).alwaysFalse()); + assertTrue("@SimpleAnnotation should match on the AnnotatedClass.m1() method", simpleAnnotationTP.matches(aMethod) + .alwaysTrue()); + } + } + + public void testAnnotationPatternMatchingOnFields() { + if (LangUtil.is15VMOrGreater()) { + ResolvedType rtx = loadType("AnnotatedClass"); + ResolvedMember aField = rtx.getDeclaredFields()[0]; + + assertTrue("Haven't got the right field, I'm looking for 'i'" + aField.getName(), aField.getName().equals("i")); + + initAnnotationTypePatterns(); + + // One should match + assertTrue("@Foo should not match on the AnnotatedClass.i field", fooTP.matches(aField).alwaysFalse()); + assertTrue("@SimpleAnnotation should match on the AnnotatedClass.i field", simpleAnnotationTP.matches(aField) + .alwaysTrue()); + } + + } + + public void testAnnotationTypeResolutionOnTypes() { + ResolvedType rtx = loadType("AnnotatedClass"); + ResolvedType[] types = rtx.getAnnotationTypes(); + assertTrue("Did not expect null", types != null); + assertTrue("Expected 1 entry but got " + types.length, types.length == 1); + assertTrue("Should be 'p.SimpleAnnotation' but is " + types[0], types[0].equals(world.resolve("p.SimpleAnnotation"))); + } + + public void testAnnotationTypeResolutionOnMethods() { + ResolvedType rtx = loadType("AnnotatedClass"); + + ResolvedMember aMethod = rtx.getDeclaredMethods()[1]; + assertTrue("Haven't got the right method, I'm looking for 'm1()': " + aMethod.getName(), aMethod.getName().equals("m1")); + + ResolvedType[] types = aMethod.getAnnotationTypes(); + assertTrue("Did not expect null", types != null); + assertTrue("Expected 1 entry but got " + types.length, types.length == 1); + assertTrue("Should be 'p.SimpleAnnotation' but is " + types[0], types[0].equals(world.resolve("p.SimpleAnnotation"))); + } + + public void testAnnotationTypeResolutionOnFields() { + ResolvedType rtx = loadType("AnnotatedClass"); + + ResolvedMember aField = rtx.getDeclaredFields()[0]; + + assertTrue("Haven't got the right field, I'm looking for 'i'" + aField.getName(), aField.getName().equals("i")); + + ResolvedType[] types = aField.getAnnotationTypes(); + assertTrue("Did not expect null", types != null); + assertTrue("Expected 1 entry but got " + types.length, types.length == 1); + assertTrue("Should be 'p.SimpleAnnotation' but is " + types[0], types[0].equals(world.resolve("p.SimpleAnnotation"))); + } + + public void testWildPatternMatchingOnTypes() { + + ResolvedType rtx = loadType("AnnotatedClass"); + initAnnotationTypePatterns(); + + // Let's create something wild + PatternParser p = new PatternParser("@(Foo || Boo)"); + AnnotationTypePattern ap = p.maybeParseAnnotationPattern(); + ap = ap.resolveBindings(makeSimpleScope(), new Bindings(3), true); + assertTrue("shouldnt match the type AnnotatedClass", ap.matches(rtx).alwaysFalse()); + + p = new PatternParser("@(p.SimpleAnnotation || Boo)"); + ap = p.maybeParseAnnotationPattern(); + ap = ap.resolveBindings(makeSimpleScope(), new Bindings(3), true); + assertTrue("should match the type AnnotatedClass", ap.matches(rtx).alwaysTrue()); + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/patterns/AnnotationPatternTestCase.java b/weaver/src/test/java/org/aspectj/weaver/patterns/AnnotationPatternTestCase.java new file mode 100644 index 000000000..899b40b8e --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/patterns/AnnotationPatternTestCase.java @@ -0,0 +1,382 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM Corporation. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import junit.framework.TestCase; + +import org.aspectj.bridge.AbortException; +import org.aspectj.util.LangUtil; +import org.aspectj.weaver.AnnotatedElement; +import org.aspectj.weaver.AnnotationAJ; +import org.aspectj.weaver.WeaverTestCase; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.bcel.BcelWorld; + +public class AnnotationPatternTestCase extends TestCase { + + public void testParseSimpleAnnotationPattern() { + PatternParser p = new PatternParser("@Foo"); + AnnotationTypePattern foo = p.maybeParseAnnotationPattern(); + foo = foo.resolveBindings(makeSimpleScope(), new Bindings(3), true); + assertTrue("ExactAnnotationTypePattern", foo instanceof ExactAnnotationTypePattern); + assertEquals("Foo", UnresolvedType.forSignature("LFoo;"), ((ExactAnnotationTypePattern) foo).annotationType); + } + + public void testParseAndAnnotationPattern() { + PatternParser p = new PatternParser("@Foo @Goo"); + AnnotationTypePattern fooAndGoo = p.maybeParseAnnotationPattern(); + assertTrue("AndAnnotationTypePattern", fooAndGoo instanceof AndAnnotationTypePattern); + assertEquals("@(Foo) @(Goo)", fooAndGoo.toString()); + fooAndGoo = fooAndGoo.resolveBindings(makeSimpleScope(), new Bindings(3), true); + assertEquals("@Foo @Goo", fooAndGoo.toString()); + AnnotationTypePattern left = ((AndAnnotationTypePattern) fooAndGoo).getLeft(); + AnnotationTypePattern right = ((AndAnnotationTypePattern) fooAndGoo).getRight(); + assertEquals("Foo", UnresolvedType.forSignature("LFoo;"), ((ExactAnnotationTypePattern) left).annotationType); + assertEquals("Goo", UnresolvedType.forSignature("LGoo;"), ((ExactAnnotationTypePattern) right).annotationType); + } + + // + // public void testParseOrAnnotationPattern() { + // PatternParser p = new PatternParser("@Foo || @Goo"); + // AnnotationTypePattern fooOrGoo = p.parseAnnotationTypePattern(); + // assertTrue("OrAnnotationTypePattern",fooOrGoo instanceof + // OrAnnotationTypePattern); + // assertEquals("(@Foo || @Goo)",fooOrGoo.toString()); + // AnnotationTypePattern left = + // ((OrAnnotationTypePattern)fooOrGoo).getLeft(); + // AnnotationTypePattern right = + // ((OrAnnotationTypePattern)fooOrGoo).getRight(); + // assertEquals("Foo",UnresolvedType.forName("Foo"),(( + // ExactAnnotationTypePattern)left).annotationType); + // assertEquals("Goo",UnresolvedType.forName("Goo"),(( + // ExactAnnotationTypePattern)right).annotationType); + // } + // + public void testParseNotAnnotationPattern() { + PatternParser p = new PatternParser("!@Foo"); + AnnotationTypePattern notFoo = p.maybeParseAnnotationPattern(); + assertTrue("NotAnnotationTypePattern", notFoo instanceof NotAnnotationTypePattern); + notFoo = notFoo.resolveBindings(makeSimpleScope(), new Bindings(3), true); + assertEquals("!@Foo", notFoo.toString()); + AnnotationTypePattern body = ((NotAnnotationTypePattern) notFoo).getNegatedPattern(); + assertEquals("Foo", UnresolvedType.forName("Foo"), ((ExactAnnotationTypePattern) body).annotationType); + } + + public void testParseBracketedAnnotationPattern() { + PatternParser p = new PatternParser("(@Foo)"); + AnnotationTypePattern foo = p.maybeParseAnnotationPattern(); + // cannot start with ( so, we get ANY + assertEquals("ANY", AnnotationTypePattern.ANY, foo); + } + + public void testParseFQAnnPattern() { + PatternParser p = new PatternParser("@org.aspectj.Foo"); + AnnotationTypePattern foo = p.maybeParseAnnotationPattern(); + assertEquals("@(org.aspectj.Foo)", foo.toString()); + } + + public void testParseComboPattern() { + // PatternParser p = new PatternParser("!((@Foo || @Goo) && !@Boo)"); + PatternParser p = new PatternParser("@(Foo || Goo)!@Boo"); + AnnotationTypePattern ap = p.maybeParseAnnotationPattern(); + ap = ap.resolveBindings(makeSimpleScope(), new Bindings(3), true); + AndAnnotationTypePattern atp = (AndAnnotationTypePattern) ap; + NotAnnotationTypePattern notBoo = (NotAnnotationTypePattern) atp.getRight(); + // ExactAnnotationTypePattern boo = (ExactAnnotationTypePattern) + notBoo.getNegatedPattern(); + // AnnotationTypePattern fooOrGoo = (AnnotationTypePattern) + atp.getLeft(); + assertEquals("@((Foo || Goo)) !@Boo", ap.toString()); + } + + // public void testParseAndOrPattern() { + // PatternParser p = new PatternParser("@Foo && @Boo || @Goo"); + // AnnotationTypePattern andOr = p.parseAnnotationTypePattern(); + // assertTrue("Should be or pattern",andOr instanceof + // OrAnnotationTypePattern); + // } + // + public void testParseBadPattern() { + PatternParser p = new PatternParser("@@Foo"); + try { + p.maybeParseAnnotationPattern(); + fail("ParserException expected"); + } catch (ParserException pEx) { + assertEquals("name pattern", pEx.getMessage()); + } + } + + public void testParseBadPattern2() { + PatternParser p = new PatternParser("Foo"); + AnnotationTypePattern bad = p.maybeParseAnnotationPattern(); + assertEquals("ANY", AnnotationTypePattern.ANY, bad); + } + + public void testParseNameOrVarAnnotationPattern() { + PatternParser p = new PatternParser("Foo"); + AnnotationTypePattern foo = p.parseAnnotationNameOrVarTypePattern(); + assertTrue("ExactAnnotationTypePattern expected", foo != null); + assertEquals("Foo", UnresolvedType.forName("Foo"), ((ExactAnnotationTypePattern) foo).annotationType); + } + + public void testParseNameOrVarAnnotationPatternWithNot() { + PatternParser p = new PatternParser("!@Foo"); + try { + // AnnotationTypePattern bad = + p.parseAnnotationNameOrVarTypePattern(); + fail("ParserException expected"); + } catch (ParserException pEx) { + assertEquals("identifier", pEx.getMessage()); + } + } + + public void testParseNameOrVarAnnotationPatternWithOr() { + PatternParser p = new PatternParser("Foo || Boo"); + AnnotationTypePattern foo = p.parseAnnotationNameOrVarTypePattern(); + // rest of pattern not consumed... + assertTrue("ExactAnnotationTypePattern", foo instanceof ExactAnnotationTypePattern); + assertEquals("Foo", UnresolvedType.forName("Foo"), ((ExactAnnotationTypePattern) foo).annotationType); + } + + public void testParseNameOrVarAnnotationWithBinding() { + PatternParser p = new PatternParser("foo"); + AnnotationTypePattern foo = p.parseAnnotationNameOrVarTypePattern(); + assertTrue("ExactAnnotationTypePattern", foo instanceof ExactAnnotationTypePattern); + assertEquals("@foo", ((ExactAnnotationTypePattern) foo).toString()); + } + + public void testParseNameOrVarAnnotationPatternWithAnd() { + PatternParser p = new PatternParser("Foo Boo"); + AnnotationTypePattern foo = p.parseAnnotationNameOrVarTypePattern(); + // rest of pattern not consumed... + assertEquals("@Foo", foo.toString()); + } + + public void testMaybeParseAnnotationPattern() { + PatternParser p = new PatternParser("@Foo"); + AnnotationTypePattern a = p.maybeParseAnnotationPattern(); + assertNotNull("Should find annotation pattern", a); + p = new PatternParser("Foo && Boo"); + a = p.maybeParseAnnotationPattern(); + assertEquals("Should be ANY pattern for a non-match", AnnotationTypePattern.ANY, a); + } + + public void testParseTypePatternsWithAnnotations() { + PatternParser p = new PatternParser("@Foo *"); + TypePattern t = p.parseTypePattern(); + assertTrue("AnyWithAnnotationTypePattern", t instanceof AnyWithAnnotationTypePattern); + AnnotationTypePattern atp = t.annotationPattern; + assertEquals("@(Foo)", atp.toString()); + assertEquals("(@(Foo) *)", t.toString()); + } + + public void testParseTypePatternsWithAnnotationsComplex() { + PatternParser p = new PatternParser("(@(Foo || Boo) (Foo || Boo))"); + TypePattern t = p.parseTypePattern(); + assertTrue("OrTypePattern", t instanceof OrTypePattern); + assertEquals("((@((Foo || Boo)) Foo) || (@((Foo || Boo)) Boo))", t.toString()); + } + + public void testNotSyntax() { + PatternParser p = new PatternParser("!@Foo (Foo || Boo))"); + TypePattern t = p.parseTypePattern(); + assertTrue("OrTypePattern", t instanceof OrTypePattern); + assertEquals("((!@(Foo) Foo) || (!@(Foo) Boo))", t.toString()); + } + + public void testParseMethodOrConstructorSigNoAP() { + PatternParser p = new PatternParser("* *.*(..)"); + SignaturePattern s = p.parseMethodOrConstructorSignaturePattern(); + assertEquals("Any annotation", AnnotationTypePattern.ANY, s.getAnnotationPattern()); + assertEquals("Any return", "*", s.getReturnType().toString()); + assertEquals("Any dec type", "*", s.getDeclaringType().toString()); + assertEquals("Any name", "*", s.getName().toString()); + assertEquals("* *.*(..)", s.toString()); + } + + public void testParseMethodOrConstructorSigSimpleAP() { + PatternParser p = new PatternParser("@Foo * *.*(..)"); + SignaturePattern s = p.parseMethodOrConstructorSignaturePattern(); + assertEquals("@(Foo) annotation", "@(Foo)", s.getAnnotationPattern().toString()); + assertEquals("Any return", "*", s.getReturnType().toString()); + assertEquals("Any dec type", "*", s.getDeclaringType().toString()); + assertEquals("Any name", "*", s.getName().toString()); + assertEquals("@(Foo) * *.*(..)", s.toString()); + } + + public void testParseMethodOrConstructorSigComplexAP() { + PatternParser p = new PatternParser("!@(Foo || Goo) * *.*(..)"); + SignaturePattern s = p.parseMethodOrConstructorSignaturePattern(); + assertEquals("complex annotation", "!@((Foo || Goo))", s.getAnnotationPattern().toString()); + assertEquals("Any return", "*", s.getReturnType().toString()); + assertEquals("Any dec type", "*", s.getDeclaringType().toString()); + assertEquals("Any name", "*", s.getName().toString()); + assertEquals("!@((Foo || Goo)) * *.*(..)", s.toString()); + } + + public void testParseMethodFieldSigNoAP() { + PatternParser p = new PatternParser("* *.*"); + SignaturePattern s = p.parseFieldSignaturePattern(); + assertEquals("Any annotation", AnnotationTypePattern.ANY, s.getAnnotationPattern()); + assertEquals("Any field type", "*", s.getReturnType().toString()); + assertEquals("Any dec type", "*", s.getDeclaringType().toString()); + assertEquals("Any name", "*", s.getName().toString()); + assertEquals("* *.*", s.toString()); + } + + public void testParseFieldSigSimpleAP() { + PatternParser p = new PatternParser("@Foo * *.*"); + SignaturePattern s = p.parseFieldSignaturePattern(); + assertEquals("@Foo annotation", "@(Foo)", s.getAnnotationPattern().toString()); + assertEquals("Any field type", "*", s.getReturnType().toString()); + assertEquals("Any dec type", "*", s.getDeclaringType().toString()); + assertEquals("Any name", "*", s.getName().toString()); + assertEquals("@(Foo) * *.*", s.toString()); + } + + public void testParseFieldSigComplexAP() { + PatternParser p = new PatternParser("!@(Foo || Goo) * *.*"); + SignaturePattern s = p.parseFieldSignaturePattern(); + assertEquals("complex annotation", "!@((Foo || Goo))", s.getAnnotationPattern().toString()); + assertEquals("Any field type", "*", s.getReturnType().toString()); + assertEquals("Any dec type", "*", s.getDeclaringType().toString()); + assertEquals("Any name", "*", s.getName().toString()); + assertEquals("!@((Foo || Goo)) * *.*", s.toString()); + } + + public void testExactAnnotationPatternMatching() { + if (LangUtil.is15VMOrGreater()) { + PatternParser p = new PatternParser("@Foo"); + AnnotationTypePattern ap = p.maybeParseAnnotationPattern(); + ap = ap.resolveBindings(makeSimpleScope(), new Bindings(3), true); + AnnotatedElementImpl ae = new AnnotatedElementImpl(new String[] { "Foo" }); + assertTrue("matches element with Foo", ap.matches(ae).alwaysTrue()); + AnnotatedElementImpl ae2 = new AnnotatedElementImpl(new String[] { "Boo" }); + assertTrue("does not match element with Boo", ap.matches(ae2).alwaysFalse()); + } + } + + public void testBindingAnnotationPatternMatching() { + if (LangUtil.is15VMOrGreater()) { + PatternParser p = new PatternParser("foo"); + AnnotationTypePattern ap = p.parseAnnotationNameOrVarTypePattern(); + try { + ap = ap.resolveBindings(makeSimpleScope(), new Bindings(3), true); + } catch (AbortException abEx) { + assertEquals("Binding not supported in @pcds (1.5.0 M1 limitation): null", abEx.getMessage()); + } + // uncomment these next lines once binding is supported + // AnnotatedElementImpl ae = new AnnotatedElementImpl(new + // String[]{"Foo"}); + // assertTrue("matches element with Foo",ap.matches(ae).alwaysTrue()) + // ; + // AnnotatedElementImpl ae2 = new AnnotatedElementImpl(new + // String[]{"Boo"}); + // assertTrue("does not match element with Boo",ap.matches(ae2). + // alwaysFalse()); + } + } + + public void testAndAnnotationPatternMatching() { + if (LangUtil.is15VMOrGreater()) { + PatternParser p = new PatternParser("@Foo @Boo"); + AnnotationTypePattern ap = p.maybeParseAnnotationPattern(); + ap = ap.resolveBindings(makeSimpleScope(), new Bindings(3), true); + AnnotatedElementImpl ae = new AnnotatedElementImpl(new String[] { "Foo", "Boo" }); + assertTrue("matches foo and boo", ap.matches(ae).alwaysTrue()); + ae = new AnnotatedElementImpl(new String[] { "Foo" }); + assertTrue("does not match foo", ap.matches(ae).alwaysFalse()); + ae = new AnnotatedElementImpl(new String[] { "Boo" }); + assertTrue("does not match boo", ap.matches(ae).alwaysFalse()); + ae = new AnnotatedElementImpl(new String[] { "Goo" }); + assertTrue("does not match goo", ap.matches(ae).alwaysFalse()); + } + } + + // + // public void testOrAnnotationPatternMatching() { + // PatternParser p = new PatternParser("@Foo || @Boo"); + // AnnotationTypePattern ap = p.parseAnnotationTypePattern(); + // ap = ap.resolveBindings(makeSimpleScope(),new Bindings(3),true); + // AnnotatedElementImpl ae = new AnnotatedElementImpl(new String[] + // {"Foo","Boo"}); + // assertTrue("matches foo and boo",ap.matches(ae).alwaysTrue()); + // ae = new AnnotatedElementImpl(new String[] {"Foo"}); + // assertTrue("matches foo",ap.matches(ae).alwaysTrue()); + // ae = new AnnotatedElementImpl(new String[] {"Boo"}); + // assertTrue("matches boo",ap.matches(ae).alwaysTrue()); + // ae = new AnnotatedElementImpl(new String[] {"Goo"}); + // assertTrue("does not match goo",ap.matches(ae).alwaysFalse()); + // } + // + public void testNotAnnotationPatternMatching() { + if (LangUtil.is15VMOrGreater()) { + PatternParser p = new PatternParser("!@Foo"); + AnnotationTypePattern ap = p.maybeParseAnnotationPattern(); + ap = ap.resolveBindings(makeSimpleScope(), new Bindings(3), true); + AnnotatedElementImpl ae = new AnnotatedElementImpl(new String[] { "Foo", "Boo" }); + assertTrue("does not match foo and boo", ap.matches(ae).alwaysFalse()); + ae = new AnnotatedElementImpl(new String[] { "Boo" }); + assertTrue("matches boo", ap.matches(ae).alwaysTrue()); + } + } + + public void testAnyAnnotationPatternMatching() { + AnnotatedElementImpl ae = new AnnotatedElementImpl(new String[] { "Foo", "Boo" }); + assertTrue("always matches", AnnotationTypePattern.ANY.matches(ae).alwaysTrue()); + ae = new AnnotatedElementImpl(new String[] {}); + assertTrue("always matches", AnnotationTypePattern.ANY.matches(ae).alwaysTrue()); + } + + public TestScope makeSimpleScope() { + BcelWorld bWorld = new BcelWorld(WeaverTestCase.TESTDATA_PATH + "/testcode.jar"); // testcode contains Foo/Boo/Goo/etc + bWorld.setBehaveInJava5Way(true); + return new TestScope(new String[] { "int", "java.lang.String", "Foo", "Boo", "Goo" }, new String[] { "a", "b", "foo", + "boo", "goo" }, bWorld); + } + + // put test cases for AnnotationPatternList matching in separate test + // class... + + static class AnnotatedElementImpl implements AnnotatedElement { + + private String[] annotationTypes; + + public AnnotatedElementImpl(String[] annotationTypes) { + this.annotationTypes = annotationTypes; + } + + public boolean hasAnnotation(UnresolvedType ofType) { + for (int i = 0; i < annotationTypes.length; i++) { + if (annotationTypes[i].equals(ofType.getName())) { + return true; + } + } + return false; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.AnnotatedElement#getAnnotationTypes() + */ + public ResolvedType[] getAnnotationTypes() { + // TODO Auto-generated method stub + return null; + } + + public AnnotationAJ getAnnotationOfType(UnresolvedType ofType) { + // TODO Auto-generated method stub + return null; + } + + } +} diff --git a/weaver/src/test/java/org/aspectj/weaver/patterns/ConcretizationTestCase.java b/weaver/src/test/java/org/aspectj/weaver/patterns/ConcretizationTestCase.java new file mode 100644 index 000000000..8c3fd0d9c --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/patterns/ConcretizationTestCase.java @@ -0,0 +1,116 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.weaver.patterns; + +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.bcel.WeaveTestCase; + +public class ConcretizationTestCase extends WeaveTestCase { + { + regenerate = false; + } + + public ConcretizationTestCase(String name) { + super(name); + } + + public void testNothingForAntJUnit() { + } + + // String[] none = new String[0]; + + /* + * XXX temporarily skipping public void testCflowResidual() throws IOException { + * + * BcelAdvice a = (BcelAdvice) makeConcreteTestAdviceEntryPart(); + * + * TestShadow shadow = new TestShadow(Shadow.MethodCall, Member.methodFromString("int Aspect.i(int x)"), UnresolvedType.OBJECT, + * world); + * + * ExposedState state = new ExposedState(1); + * + * a.specializeOn(shadow); + * + * //System.err.println(shadow); //System.err.println(a); + * + * //System.err.println(a.exposedState); + * + * + * } + * + * + * + * public Advice makeConcreteTestAdviceEntryPart() throws IOException { // XXX copied from below, refactor later + * + * + * // returns the advice for the entry part of cflow(foo(a)) Pointcut in = createResolvedPointcut( + * "cflow(foo(a)) && (args(b) && !cflow(foo(int)))", new String[] { "b", "a" }, new String[] { "float", "int" }); + * + * ResolvedPointcutDefinition ref = new ResolvedPointcutDefinition( UnresolvedType.forName("Aspect"), 0, "foo", new + * UnresolvedType[] { UnresolvedType.INT }, createResolvedPointcut( "args(refA)", new String[] { "refA" }, new String[] { "int" + * })); BcelObjectType target = (BcelObjectType) world.resolve("Aspect"); + * + * // now munge this to get the pointcut in it + * + * target.addPointcutDefinition(ref); CrosscuttingMembers xcut = new CrosscuttingMembers(target); target.crosscuttingMembers = + * xcut; + * + * Advice adviceMember = new BcelAdvice( AdviceKind.Before, in, Member.method(UnresolvedType.forName("FOO"), 0, "garadf", + * "(FI)V"), 0, 0, 0, null, null); // The pointcut to concretize + * + * // this returns the actual advice, but we don't care about it now. in.concretize(target, 2, adviceMember); + * + * List c = (List)xcut.getCflowEntries(); //target.getExtraConcreteShadowMungers(); + * + * return (Advice) c.get(0); } + * + * public void XtestCflow() throws IOException { Pointcut in = + * createResolvedPointcut("cflow(foo(a)) && (args(b) && !cflow(foo(int)))", new String[] {"b", "a"}, new String[] {"float", + * "int"} ); + * + * ResolvedPointcutDefinition ref = new ResolvedPointcutDefinition(UnresolvedType.forName("Aspect"), 0, "foo", new + * UnresolvedType[] { UnresolvedType.INT }, createResolvedPointcut("args(refA)", new String[] {"refA"}, new String[] {"int"})); + * + * List expectedSlots = new ArrayList(); expectedSlots.add(new ConcreteCflowPointcut.Slot(1, UnresolvedType.INT, 0)); + * + * checkConcr(in, ref, expectedSlots); } + * + * public void checkConcr( Pointcut in, ResolvedPointcutDefinition referredTo, List expectedSlots) throws IOException { + * + * BcelObjectType target = (BcelObjectType)world.resolve("Aspect"); + * + * // now munge this to get the pointcut in it + * + * target.addPointcutDefinition(referredTo); + * + * + * Advice adviceMember = new BcelAdvice(AdviceKind.Before, in, Member.method(UnresolvedType.forName("FOO"), 0, "garadf", + * "(FI)V"), 0, 0, 0, null, null); + * + * // The pointcut to concretize AndPointcut ap = (AndPointcut)in.concretize(target, 2, adviceMember); + * + * + * ConcreteCflowPointcut conc = (ConcreteCflowPointcut)ap.left; + * + * List slots = conc.slots; TestUtil.assertSetEquals(expectedSlots, slots); + * + * } + */ + + public Pointcut createResolvedPointcut(String pointcutSource, String[] formalNames, String[] formalTypes) { + final Pointcut sp = Pointcut.fromString(pointcutSource); + final Pointcut rp = sp.resolve(new SimpleScope(world, SimpleScope.makeFormalBindings(UnresolvedType.forNames(formalTypes), + formalNames))); + return rp; + } +} diff --git a/weaver/src/test/java/org/aspectj/weaver/patterns/WildTypePatternResolutionTestCase.java b/weaver/src/test/java/org/aspectj/weaver/patterns/WildTypePatternResolutionTestCase.java new file mode 100644 index 000000000..5eda5d577 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/patterns/WildTypePatternResolutionTestCase.java @@ -0,0 +1,422 @@ +/* ******************************************************************* + * Copyright (c) 2005 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer initial implementation + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import junit.framework.TestCase; + +import org.aspectj.weaver.BoundedReferenceType; +import org.aspectj.weaver.CompressingDataOutputStream; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.TypeFactory; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; +import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; +import org.aspectj.weaver.bcel.BcelWorld; + +// TODO write test cases for instanceof matching + +public class WildTypePatternResolutionTestCase extends TestCase { + + private World world; + private Bindings bindings; + private SimpleScope scope; + private ResolvedType javaUtilList; + private ResolvedType javaLangString; + private ResolvedType javaUtilListOfString; + private ResolvedType javaUtilListOfDouble; + private ResolvedType javaUtilListOfSomething; + + /** + * Foo where Foo exists and is generic Parser creates WildTypePattern namePatterns={Foo} resolveBindings resolves Foo to RT(Foo + * - raw) return ExactTypePattern(LFoo;) + */ + public void testSimpleFoo() { + TypePattern rtp = resolveWildTypePattern("List", false); + + assertTrue("resolves to exact type", rtp instanceof ExactTypePattern); + UnresolvedType exactType = rtp.getExactType(); + assertTrue(exactType.isRawType()); + assertEquals("Ljava/util/List;", exactType.getSignature()); + + ResolvedType rt = exactType.resolve(world); + assertEquals("Ljava/util/List;", rt.getSignature()); + assertTrue(rt.isRawType()); + + ExactTypePattern etp = (ExactTypePattern) writeAndRead(rtp); + exactType = etp.getExactType(); + + assertEquals("Ljava/util/List;", exactType.getSignature()); + + rt = exactType.resolve(world); + assertEquals("Ljava/util/List;", rt.getSignature()); + assertTrue(rt.isRawType()); + + assertTrue("matches List", etp.matches(javaUtilList, TypePattern.STATIC).alwaysTrue()); + assertTrue("matches generic List", etp.matches(javaUtilList.getGenericType(), TypePattern.STATIC).alwaysTrue()); + assertTrue("matches parameterized list", etp.matches(javaUtilListOfString, TypePattern.STATIC).alwaysTrue()); + assertTrue("does not match String", etp.matches(javaLangString, TypePattern.STATIC).alwaysFalse()); + } + + /** + * Foo where Foo exists and String meets the bounds Parser creates WildTypePattern namePatterns = {Foo}, + * typeParameters=WTP{String} resolveBindings resolves typeParameters to ExactTypePattern(String) resolves Foo to RT(Foo) + * returns ExactTypePattern(PFoo; - parameterized) + */ + public void testParameterized() { + TypePattern rtp = resolveWildTypePattern("List", false); + + assertTrue("resolves to exact type", rtp instanceof ExactTypePattern); + UnresolvedType exactType = rtp.getExactType(); + assertTrue(exactType.isParameterizedType()); + assertEquals("Pjava/util/List;", exactType.getSignature()); + + ResolvedType rt = exactType.resolve(world); + assertEquals("Pjava/util/List;", rt.getSignature()); + assertTrue(rt.isParameterizedType()); + + ExactTypePattern etp = (ExactTypePattern) writeAndRead(rtp); + exactType = etp.getExactType(); + + assertEquals("Pjava/util/List;", rt.getSignature()); + assertTrue(rt.isParameterizedType()); + + rt = exactType.resolve(world); + assertEquals("Pjava/util/List;", rt.getSignature()); + assertTrue(rt.isParameterizedType()); + + assertFalse("does not match List", etp.matches(javaUtilList, TypePattern.STATIC).alwaysTrue()); + assertFalse("does not match generic List", etp.matches(javaUtilList.getGenericType(), TypePattern.STATIC).alwaysTrue()); + assertTrue("matches parameterized list", etp.matches(javaUtilListOfString, TypePattern.STATIC).alwaysTrue()); + assertFalse("does not match parameterized list of double", etp.matches(javaUtilListOfDouble, TypePattern.STATIC) + .alwaysTrue()); + assertTrue("does not match String", etp.matches(javaLangString, TypePattern.STATIC).alwaysFalse()); + + } + + /** + * Foo where Foo exists and takes one bound Parser creates WildTypePattern namePatterns = {Foo}, typeParameters=WTP{Str*} + * resolveBindings resolves typeParameters to WTP{Str*} resolves Foo to RT(Foo) returns WildTypePattern(name = Foo, + * typeParameters = WTP{Str*} isGeneric=false) + */ + public void testParameterizedWildCard() { + TypePattern rtp = resolveWildTypePattern("List", false); + + assertTrue("resolves to WildTypePattern", rtp instanceof WildTypePattern); + assertTrue("one type parameter", rtp.typeParameters.size() == 1); + assertTrue("missing", ResolvedType.isMissing(rtp.getExactType())); + + WildTypePattern wtp = (WildTypePattern) writeAndRead(rtp); + assertTrue("one type parameter", wtp.typeParameters.size() == 1); + assertTrue("missing", ResolvedType.isMissing(wtp.getExactType())); + assertEquals("Str*", wtp.getTypeParameters().getTypePatterns()[0].toString()); + + assertFalse("does not match List", wtp.matches(javaUtilList, TypePattern.STATIC).alwaysTrue()); + assertFalse("does not match generic List", wtp.matches(javaUtilList.getGenericType(), TypePattern.STATIC).alwaysTrue()); + assertTrue("matches parameterized list", wtp.matches(javaUtilListOfString, TypePattern.STATIC).alwaysTrue()); + assertFalse("does not match parameterized list of double", wtp.matches(javaUtilListOfDouble, TypePattern.STATIC) + .alwaysTrue()); + assertTrue("does not match String", wtp.matches(javaLangString, TypePattern.STATIC).alwaysFalse()); + } + + /** + * Fo* Parser creates WildTypePattern namePatterns = {Fo*}, typeParameters=WTP{String} resolveBindings resolves + * typeParameters to ETP{String} returns WildTypePattern(name = Fo*, typeParameters = ETP{String} isGeneric=false) + */ + public void testWildcardParameterized() { + TypePattern rtp = resolveWildTypePattern("Li*", false); + + assertTrue("resolves to WildTypePattern", rtp instanceof WildTypePattern); + assertTrue("one type parameter", rtp.typeParameters.size() == 1); + assertEquals("Ljava/lang/String;", rtp.typeParameters.getTypePatterns()[0].getExactType().getSignature()); + + WildTypePattern wtp = (WildTypePattern) writeAndRead(rtp); + assertTrue("one type parameter", wtp.typeParameters.size() == 1); + assertEquals("Ljava/lang/String;", wtp.typeParameters.getTypePatterns()[0].getExactType().getSignature()); + + assertFalse("does not match List", wtp.matches(javaUtilList, TypePattern.STATIC).alwaysTrue()); + assertFalse("does not match generic List", wtp.matches(javaUtilList.getGenericType(), TypePattern.STATIC).alwaysTrue()); + assertTrue("matches parameterized list", wtp.matches(javaUtilListOfString, TypePattern.STATIC).alwaysTrue()); + assertFalse("does not match parameterized list of double", wtp.matches(javaUtilListOfDouble, TypePattern.STATIC) + .alwaysTrue()); + assertTrue("does not match String", wtp.matches(javaLangString, TypePattern.STATIC).alwaysFalse()); + } + + /** + * Foo + */ + public void testSomething() { + TypePattern rtp = resolveWildTypePattern("List", false); + + assertTrue("resolves to exact type", rtp instanceof ExactTypePattern); + UnresolvedType exactType = rtp.getExactType(); + assertTrue(exactType.isParameterizedType()); + assertEquals("Pjava/util/List<*>;", exactType.getSignature()); + + ExactTypePattern etp = (ExactTypePattern) writeAndRead(rtp); + exactType = etp.getExactType(); + assertTrue(exactType.isParameterizedType()); + assertEquals("Pjava/util/List<*>;", exactType.getSignature()); + + assertFalse("does not match List", etp.matches(javaUtilList, TypePattern.STATIC).alwaysTrue()); + assertFalse("does not match generic List", etp.matches(javaUtilList.getGenericType(), TypePattern.STATIC).alwaysTrue()); + assertFalse("does not match parameterized list", etp.matches(javaUtilListOfString, TypePattern.STATIC).alwaysTrue()); + assertFalse("does not match parameterized list of double", etp.matches(javaUtilListOfDouble, TypePattern.STATIC) + .alwaysTrue()); + assertTrue("does not match String", etp.matches(javaLangString, TypePattern.STATIC).alwaysFalse()); + + assertTrue("matches list of something", etp.matches(javaUtilListOfSomething, TypePattern.STATIC).alwaysTrue()); + } + + /** + * Foo + */ + public void testSomethingExtends() { + TypePattern rtp = resolveWildTypePattern("List", false); + + assertTrue("resolves to exact type", rtp instanceof ExactTypePattern); + UnresolvedType exactType = rtp.getExactType(); + assertTrue(exactType.isParameterizedType()); + assertEquals("Pjava/util/List<+Ljava/lang/Number;>;", exactType.getSignature()); + assertTrue("got a bounded reference type", exactType.getTypeParameters()[0] instanceof BoundedReferenceType); + + ExactTypePattern etp = (ExactTypePattern) writeAndRead(rtp); + exactType = etp.getExactType(); + exactType = exactType.resolve(world); + assertTrue(exactType.isParameterizedType()); + assertEquals("Pjava/util/List<+Ljava/lang/Number;>;", exactType.getSignature()); + assertTrue("got a bounded reference type", exactType.getTypeParameters()[0] instanceof BoundedReferenceType); + + assertFalse("does not match List", etp.matches(javaUtilList, TypePattern.STATIC).alwaysTrue()); + assertFalse("does not match generic List", etp.matches(javaUtilList.getGenericType(), TypePattern.STATIC).alwaysTrue()); + assertFalse("does not match parameterized list", etp.matches(javaUtilListOfString, TypePattern.STATIC).alwaysTrue()); + assertFalse("does not match parameterized list of double", etp.matches(javaUtilListOfDouble, TypePattern.STATIC) + .alwaysTrue()); + assertTrue("does not match String", etp.matches(javaLangString, TypePattern.STATIC).alwaysFalse()); + assertFalse("does not match list of something", etp.matches(javaUtilListOfSomething, TypePattern.STATIC).alwaysTrue()); + + ResolvedType listOfNumber = TypeFactory.createParameterizedType(javaUtilList, new UnresolvedType[] { UnresolvedType + .forName("java.lang.Number").resolve(world) }, world); + + ResolvedType listOfDouble = TypeFactory.createParameterizedType(javaUtilList, new UnresolvedType[] { UnresolvedType + .forName("java.lang.Double").resolve(world) }, world); + + assertFalse("does not match list of number", etp.matches(listOfNumber, TypePattern.STATIC).alwaysTrue()); + assertFalse("does not match list of double", etp.matches(listOfDouble, TypePattern.STATIC).alwaysTrue()); + + ResolvedType extendsNumber = TypeFactory.createTypeFromSignature("+Ljava/lang/Number;").resolve(world); + ResolvedType listOfExtendsNumber = TypeFactory.createParameterizedType(javaUtilList, + new UnresolvedType[] { extendsNumber }, world); + + assertTrue("matches list of ? extends number", etp.matches(listOfExtendsNumber, TypePattern.STATIC).alwaysTrue()); + + } + + /** + * Foo + */ + public void testSomethingExtendsPattern() { + TypePattern rtp = resolveWildTypePattern("List", false); + + assertTrue("resolves to wild type pattern", rtp instanceof WildTypePattern); + assertEquals("one type parameter", 1, rtp.getTypeParameters().size()); + TypePattern tp = rtp.getTypeParameters().getTypePatterns()[0]; + assertTrue("parameter is wild", tp instanceof WildTypePattern); + WildTypePattern tpwtp = (WildTypePattern) tp; + assertEquals("?", tpwtp.getNamePatterns()[0].maybeGetSimpleName()); + assertEquals("java.lang.Number+", tpwtp.upperBound.toString()); + + WildTypePattern wtp = (WildTypePattern) writeAndRead(rtp); + assertEquals("one type parameter", 1, wtp.getTypeParameters().size()); + tp = rtp.getTypeParameters().getTypePatterns()[0]; + assertTrue("parameter is wild", tp instanceof WildTypePattern); + tpwtp = (WildTypePattern) tp; + assertEquals("?", tpwtp.getNamePatterns()[0].maybeGetSimpleName()); + assertEquals("java.lang.Number+", tpwtp.upperBound.toString()); + + assertFalse("does not match List", wtp.matches(javaUtilList, TypePattern.STATIC).alwaysTrue()); + assertFalse("does not match generic List", wtp.matches(javaUtilList.getGenericType(), TypePattern.STATIC).alwaysTrue()); + assertFalse("does not match parameterized list", wtp.matches(javaUtilListOfString, TypePattern.STATIC).alwaysTrue()); + assertFalse("does not match parameterized list of double", wtp.matches(javaUtilListOfDouble, TypePattern.STATIC) + .alwaysTrue()); + assertTrue("does not match String", wtp.matches(javaLangString, TypePattern.STATIC).alwaysFalse()); + assertFalse("does not match list of something", wtp.matches(javaUtilListOfSomething, TypePattern.STATIC).alwaysTrue()); + + ResolvedType listOfNumber = TypeFactory.createParameterizedType(javaUtilList, new UnresolvedType[] { UnresolvedType + .forName("java.lang.Number").resolve(world) }, world); + + ResolvedType listOfDouble = TypeFactory.createParameterizedType(javaUtilList, new UnresolvedType[] { UnresolvedType + .forName("java.lang.Double").resolve(world) }, world); + + assertFalse("does not match list of number", wtp.matches(listOfNumber, TypePattern.STATIC).alwaysTrue()); + assertFalse("does not match list of double", wtp.matches(listOfDouble, TypePattern.STATIC).alwaysTrue()); + + ResolvedType extendsNumber = TypeFactory.createTypeFromSignature("+Ljava/lang/Number;").resolve(world); + ResolvedType listOfExtendsNumber = TypeFactory.createParameterizedType(javaUtilList, + new UnresolvedType[] { extendsNumber }, world); + + assertTrue("matches list of ? extends number", wtp.matches(listOfExtendsNumber, TypePattern.STATIC).alwaysTrue()); + + ResolvedType extendsDouble = TypeFactory.createTypeFromSignature("+Ljava/lang/Double;").resolve(world); + ResolvedType listOfExtendsDouble = TypeFactory.createParameterizedType(javaUtilList, + new UnresolvedType[] { extendsDouble }, world); + + assertTrue("matches list of ? extends double", wtp.matches(listOfExtendsDouble, TypePattern.STATIC).alwaysTrue()); + + } + + /** + * Foo + */ + public void testSomethingExtendsPatternv2() { + TypePattern rtp = resolveWildTypePattern("List", false); + + assertTrue("resolves to wild type pattern", rtp instanceof WildTypePattern); + assertEquals("one type parameter", 1, rtp.getTypeParameters().size()); + TypePattern tp = rtp.getTypeParameters().getTypePatterns()[0]; + assertTrue("parameter is wild", tp instanceof WildTypePattern); + WildTypePattern tpwtp = (WildTypePattern) tp; + assertEquals("?", tpwtp.getNamePatterns()[0].maybeGetSimpleName()); + assertEquals("Num*", tpwtp.upperBound.toString()); + + WildTypePattern wtp = (WildTypePattern) writeAndRead(rtp); + assertEquals("one type parameter", 1, wtp.getTypeParameters().size()); + tp = rtp.getTypeParameters().getTypePatterns()[0]; + assertTrue("parameter is wild", tp instanceof WildTypePattern); + tpwtp = (WildTypePattern) tp; + assertEquals("?", tpwtp.getNamePatterns()[0].maybeGetSimpleName()); + assertEquals("Num*", tpwtp.upperBound.toString()); + + assertFalse("does not match List", wtp.matches(javaUtilList, TypePattern.STATIC).alwaysTrue()); + assertFalse("does not match generic List", wtp.matches(javaUtilList.getGenericType(), TypePattern.STATIC).alwaysTrue()); + assertFalse("does not match parameterized list", wtp.matches(javaUtilListOfString, TypePattern.STATIC).alwaysTrue()); + assertFalse("does not match parameterized list of double", wtp.matches(javaUtilListOfDouble, TypePattern.STATIC) + .alwaysTrue()); + assertTrue("does not match String", wtp.matches(javaLangString, TypePattern.STATIC).alwaysFalse()); + assertFalse("does not match list of something", wtp.matches(javaUtilListOfSomething, TypePattern.STATIC).alwaysTrue()); + + ResolvedType listOfNumber = TypeFactory.createParameterizedType(javaUtilList, new UnresolvedType[] { UnresolvedType + .forName("java.lang.Number").resolve(world) }, world); + + ResolvedType listOfDouble = TypeFactory.createParameterizedType(javaUtilList, new UnresolvedType[] { UnresolvedType + .forName("java.lang.Double").resolve(world) }, world); + + assertFalse("does not match list of number", wtp.matches(listOfNumber, TypePattern.STATIC).alwaysTrue()); + assertFalse("does not match list of double", wtp.matches(listOfDouble, TypePattern.STATIC).alwaysTrue()); + + ResolvedType extendsNumber = TypeFactory.createTypeFromSignature("+Ljava/lang/Number;").resolve(world); + ResolvedType listOfExtendsNumber = TypeFactory.createParameterizedType(javaUtilList, + new UnresolvedType[] { extendsNumber }, world); + + assertTrue("matches list of ? extends number", wtp.matches(listOfExtendsNumber, TypePattern.STATIC).alwaysTrue()); + + ResolvedType extendsDouble = TypeFactory.createTypeFromSignature("+Ljava/lang/Double;").resolve(world); + ResolvedType listOfExtendsDouble = TypeFactory.createParameterizedType(javaUtilList, + new UnresolvedType[] { extendsDouble }, world); + + assertFalse("does not match list of ? extends double", wtp.matches(listOfExtendsDouble, TypePattern.STATIC).alwaysTrue()); + } + + /** + * Foo + * + */ + public void testSomethingSuper() { + TypePattern rtp = resolveWildTypePattern("List", false); + + assertTrue("resolves to exact type", rtp instanceof ExactTypePattern); + UnresolvedType exactType = rtp.getExactType(); + assertTrue(exactType.isParameterizedType()); + assertEquals("Pjava/util/List<-Ljava/lang/Double;>;", exactType.getSignature()); + assertTrue("got a bounded reference type", exactType.getTypeParameters()[0] instanceof BoundedReferenceType); + + ExactTypePattern etp = (ExactTypePattern) writeAndRead(rtp); + exactType = etp.getExactType(); + exactType = exactType.resolve(world); + assertTrue(exactType.isParameterizedType()); + assertEquals("Pjava/util/List<-Ljava/lang/Double;>;", exactType.getSignature()); + assertTrue("got a bounded reference type", exactType.getTypeParameters()[0] instanceof BoundedReferenceType); + + assertFalse("does not match List", etp.matches(javaUtilList, TypePattern.STATIC).alwaysTrue()); + assertFalse("does not match generic List", etp.matches(javaUtilList.getGenericType(), TypePattern.STATIC).alwaysTrue()); + assertFalse("does not match parameterized list", etp.matches(javaUtilListOfString, TypePattern.STATIC).alwaysTrue()); + assertFalse("does not match parameterized list of double", etp.matches(javaUtilListOfDouble, TypePattern.STATIC) + .alwaysTrue()); + assertTrue("does not match String", etp.matches(javaLangString, TypePattern.STATIC).alwaysFalse()); + assertFalse("does not match list of something", etp.matches(javaUtilListOfSomething, TypePattern.STATIC).alwaysTrue()); + + ResolvedType listOfNumber = TypeFactory.createParameterizedType(javaUtilList, new UnresolvedType[] { UnresolvedType + .forName("java.lang.Number").resolve(world) }, world); + + ResolvedType listOfDouble = TypeFactory.createParameterizedType(javaUtilList, new UnresolvedType[] { UnresolvedType + .forName("java.lang.Double").resolve(world) }, world); + + assertFalse("does not match list of number", etp.matches(listOfNumber, TypePattern.STATIC).alwaysTrue()); + assertFalse("does not match list of double", etp.matches(listOfDouble, TypePattern.STATIC).alwaysTrue()); + + ResolvedType superDouble = TypeFactory.createTypeFromSignature("-Ljava/lang/Double;").resolve(world); + ResolvedType listOfSuperDouble = TypeFactory.createParameterizedType(javaUtilList, new UnresolvedType[] { superDouble }, + world); + + assertTrue("matches list of ? super double", etp.matches(listOfSuperDouble, TypePattern.STATIC).alwaysTrue()); + } + + private TypePattern resolveWildTypePattern(String source, boolean requireExact) { + WildTypePattern wtp = makeWildTypePattern(source); + return wtp.resolveBindings(scope, bindings, false, requireExact); + } + + private WildTypePattern makeWildTypePattern(String source) { + PatternParser parser = new PatternParser(source); + return (WildTypePattern) parser.parseTypePattern(); + } + + private TypePattern writeAndRead(TypePattern etp) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ConstantPoolSimulator cps = new ConstantPoolSimulator(); + CompressingDataOutputStream dos = new CompressingDataOutputStream(baos, cps); + etp.write(dos); + dos.flush(); + dos.close(); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + VersionedDataInputStream in = new VersionedDataInputStream(bais, cps); + in.setVersion(new WeaverVersionInfo()); + TypePattern ret = TypePattern.read(in, null); + return ret; + } catch (IOException ioEx) { + fail(ioEx + " thrown during serialization"); + } + return null; + } + + protected void setUp() throws Exception { + super.setUp(); + this.world = new BcelWorld(); + this.world.setBehaveInJava5Way(true); + this.bindings = new Bindings(0); + this.scope = new SimpleScope(world, new FormalBinding[] {}); + this.scope.setImportedPrefixes(new String[] { "java.io.", "java.util.", "java.lang." }); + this.javaLangString = UnresolvedType.forName("java.lang.String").resolve(world); + this.javaUtilList = UnresolvedType.forName("java.util.List").resolve(world); + this.javaUtilListOfString = TypeFactory.createParameterizedType(javaUtilList, new UnresolvedType[] { javaLangString }, + world); + this.javaUtilListOfDouble = TypeFactory.createParameterizedType(javaUtilList, new UnresolvedType[] { UnresolvedType + .forName("java.lang.Double").resolve(world) }, world); + this.javaUtilListOfSomething = TypeFactory.createParameterizedType(javaUtilList, + new UnresolvedType[] { UnresolvedType.SOMETHING.resolve(world) }, world); + } +} diff --git a/weaver/src/test/java/org/aspectj/weaver/patterns/bcel/BcelAndOrNotTestCase.java b/weaver/src/test/java/org/aspectj/weaver/patterns/bcel/BcelAndOrNotTestCase.java new file mode 100644 index 000000000..d72960fea --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/patterns/bcel/BcelAndOrNotTestCase.java @@ -0,0 +1,24 @@ +/* ******************************************************************* + * Copyright (c) 2008 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement + * ******************************************************************/ +package org.aspectj.weaver.patterns.bcel; + +import org.aspectj.weaver.World; +import org.aspectj.weaver.bcel.BcelWorld; +import org.aspectj.weaver.patterns.AndOrNotTestCase; + +public class BcelAndOrNotTestCase extends AndOrNotTestCase { + + public World getWorld() { + return new BcelWorld(); + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/patterns/bcel/BcelBindingTestCase.java b/weaver/src/test/java/org/aspectj/weaver/patterns/bcel/BcelBindingTestCase.java new file mode 100644 index 000000000..fac53dee1 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/patterns/bcel/BcelBindingTestCase.java @@ -0,0 +1,24 @@ +/* ******************************************************************* + * Copyright (c) 2008 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement + * ******************************************************************/ +package org.aspectj.weaver.patterns.bcel; + +import org.aspectj.weaver.World; +import org.aspectj.weaver.bcel.BcelWorld; +import org.aspectj.weaver.patterns.BindingTestCase; + +public class BcelBindingTestCase extends BindingTestCase { + + public World getWorld() { + return new BcelWorld(); + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/patterns/bcel/BcelModifiersPatternTestCase.java b/weaver/src/test/java/org/aspectj/weaver/patterns/bcel/BcelModifiersPatternTestCase.java new file mode 100644 index 000000000..06b89dc15 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/patterns/bcel/BcelModifiersPatternTestCase.java @@ -0,0 +1,24 @@ +/* ******************************************************************* + * Copyright (c) 2008 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement + * ******************************************************************/ +package org.aspectj.weaver.patterns.bcel; + +import org.aspectj.weaver.World; +import org.aspectj.weaver.bcel.BcelWorld; +import org.aspectj.weaver.patterns.ModifiersPatternTestCase; + +public class BcelModifiersPatternTestCase extends ModifiersPatternTestCase { + + public World getWorld() { + return new BcelWorld(); + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/patterns/bcel/BcelParserTestCase.java b/weaver/src/test/java/org/aspectj/weaver/patterns/bcel/BcelParserTestCase.java new file mode 100644 index 000000000..6dc837709 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/patterns/bcel/BcelParserTestCase.java @@ -0,0 +1,25 @@ +/* ******************************************************************* + * Copyright (c) 2008 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement + * ******************************************************************/ +package org.aspectj.weaver.patterns.bcel; + +import org.aspectj.weaver.WeaverTestCase; +import org.aspectj.weaver.World; +import org.aspectj.weaver.bcel.BcelWorld; +import org.aspectj.weaver.patterns.ParserTestCase; + +public class BcelParserTestCase extends ParserTestCase { + + public World getWorld() { + return new BcelWorld(WeaverTestCase.TESTDATA_PATH + "/testcode.jar"); + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/patterns/bcel/BcelSignaturePatternTestCase.java b/weaver/src/test/java/org/aspectj/weaver/patterns/bcel/BcelSignaturePatternTestCase.java new file mode 100644 index 000000000..e7f0f9890 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/patterns/bcel/BcelSignaturePatternTestCase.java @@ -0,0 +1,24 @@ +/* ******************************************************************* + * Copyright (c) 2008 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement + * ******************************************************************/ +package org.aspectj.weaver.patterns.bcel; + +import org.aspectj.weaver.World; +import org.aspectj.weaver.bcel.BcelWorld; +import org.aspectj.weaver.patterns.SignaturePatternTestCase; + +public class BcelSignaturePatternTestCase extends SignaturePatternTestCase { + + public World getWorld() { + return new BcelWorld(); + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/patterns/bcel/BcelTypePatternListTestCase.java b/weaver/src/test/java/org/aspectj/weaver/patterns/bcel/BcelTypePatternListTestCase.java new file mode 100644 index 000000000..935533d3c --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/patterns/bcel/BcelTypePatternListTestCase.java @@ -0,0 +1,23 @@ +/* ******************************************************************* + * Copyright (c) 2008 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement + * ******************************************************************/ +package org.aspectj.weaver.patterns.bcel; + +import org.aspectj.weaver.World; +import org.aspectj.weaver.bcel.BcelWorld; +import org.aspectj.weaver.patterns.TypePatternListTestCase; + +public class BcelTypePatternListTestCase extends TypePatternListTestCase { + + public World getWorld() { + return new BcelWorld(); + } +} diff --git a/weaver/src/test/java/org/aspectj/weaver/patterns/bcel/BcelTypePatternTestCase.java b/weaver/src/test/java/org/aspectj/weaver/patterns/bcel/BcelTypePatternTestCase.java new file mode 100644 index 000000000..47b2ce3af --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/patterns/bcel/BcelTypePatternTestCase.java @@ -0,0 +1,24 @@ +/* ******************************************************************* + * Copyright (c) 2008 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement + * ******************************************************************/ +package org.aspectj.weaver.patterns.bcel; + +import org.aspectj.weaver.World; +import org.aspectj.weaver.bcel.BcelWorld; +import org.aspectj.weaver.patterns.TypePatternTestCase; + +public class BcelTypePatternTestCase extends TypePatternTestCase { + + public World getWorld() { + return new BcelWorld(); + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/patterns/bcel/BcelWithinTestCase.java b/weaver/src/test/java/org/aspectj/weaver/patterns/bcel/BcelWithinTestCase.java new file mode 100644 index 000000000..8da1c172f --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/patterns/bcel/BcelWithinTestCase.java @@ -0,0 +1,23 @@ +/* ******************************************************************* + * Copyright (c) 2008 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement + * ******************************************************************/ +package org.aspectj.weaver.patterns.bcel; + +import org.aspectj.weaver.World; +import org.aspectj.weaver.bcel.BcelWorld; +import org.aspectj.weaver.patterns.WithinTestCase; + +public class BcelWithinTestCase extends WithinTestCase { + + public World getWorld() { + return new BcelWorld(); + } +} diff --git a/weaver/src/test/java/org/aspectj/weaver/reflect/ReflectionBasedReferenceTypeDelegateTest.java b/weaver/src/test/java/org/aspectj/weaver/reflect/ReflectionBasedReferenceTypeDelegateTest.java new file mode 100644 index 000000000..72c7c85f7 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/reflect/ReflectionBasedReferenceTypeDelegateTest.java @@ -0,0 +1,316 @@ +/* ******************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.reflect; + +import java.lang.reflect.Method; +import java.util.HashSet; +import java.util.Set; + +import junit.framework.TestCase; + +import org.aspectj.bridge.IMessageHandler; +import org.aspectj.weaver.ReferenceType; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.bcel.BcelWorld; + +public abstract class ReflectionBasedReferenceTypeDelegateTest extends TestCase { + + protected ReflectionWorld world; + private ResolvedType objectType; + private ResolvedType classType; + + public void testIsAspect() { + assertFalse(objectType.isAspect()); + } + + public void testIsAnnotationStyleAspect() { + assertFalse(objectType.isAnnotationStyleAspect()); + } + + public void testIsInterface() { + assertFalse(objectType.isInterface()); + assertTrue(world.resolve("java.io.Serializable").isInterface()); + } + + public void testIsEnum() { + assertFalse(objectType.isEnum()); + } + + public void testIsAnnotation() { + assertFalse(objectType.isAnnotation()); + } + + public void testIsAnnotationWithRuntimeRetention() { + assertFalse(objectType.isAnnotationWithRuntimeRetention()); + } + + public void testIsClass() { + assertTrue(objectType.isClass()); + assertFalse(world.resolve("java.io.Serializable").isClass()); + } + + public void testIsGeneric() { + assertFalse(objectType.isGenericType()); + } + + public void testIsExposedToWeaver() { + assertFalse(objectType.isExposedToWeaver()); + } + + public void testHasAnnotation() { + assertFalse(objectType.hasAnnotation(UnresolvedType.forName("Foo"))); + } + + public void testGetAnnotations() { + assertEquals("no entries", 0, objectType.getAnnotations().length); + } + + public void testGetAnnotationTypes() { + assertEquals("no entries", 0, objectType.getAnnotationTypes().length); + } + + public void testGetTypeVariables() { + assertEquals("no entries", 0, objectType.getTypeVariables().length); + } + + public void testGetPerClause() { + assertNull(objectType.getPerClause()); + } + + public void testGetModifiers() { + assertEquals(Object.class.getModifiers(), objectType.getModifiers()); + } + + public void testGetSuperclass() { + assertTrue("Superclass of object should be null, but it is: " + objectType.getSuperclass(), + objectType.getSuperclass() == null); + assertEquals(objectType, world.resolve("java.lang.Class").getSuperclass()); + ResolvedType d = world.resolve("reflect.tests.D"); + assertEquals(world.resolve("reflect.tests.C"), d.getSuperclass()); + } + + protected int findMethod(String name, ResolvedMember[] methods) { + for (int i = 0; i < methods.length; i++) { + if (name.equals(methods[i].getName())) { + return i; + } + } + return -1; + } + + protected int findMethod(String name, int numArgs, ResolvedMember[] methods) { + for (int i = 0; i < methods.length; i++) { + if (name.equals(methods[i].getName()) && (methods[i].getParameterTypes().length == numArgs)) { + return i; + } + } + return -1; + } + + public void testGetDeclaredMethods() { + ResolvedMember[] methods = objectType.getDeclaredMethods(); + assertEquals(Object.class.getDeclaredMethods().length + Object.class.getDeclaredConstructors().length, methods.length); + + ResolvedType c = world.resolve("reflect.tests.C"); + methods = c.getDeclaredMethods(); + assertEquals(3, methods.length); + int idx = findMethod("foo", methods); + assertTrue(idx > -1); + + assertEquals(world.resolve("java.lang.String"), methods[idx].getReturnType()); + assertEquals(1, methods[idx].getParameterTypes().length); + assertEquals(objectType, methods[idx].getParameterTypes()[0]); + assertEquals(1, methods[idx].getExceptions().length); + assertEquals(world.resolve("java.lang.Exception"), methods[idx].getExceptions()[0]); + int baridx = findMethod("bar", methods); + int initidx = findMethod("", methods); + assertTrue(baridx > -1); + assertTrue(initidx > -1); + assertTrue(baridx != initidx && baridx != idx && idx <= 2 && initidx <= 2 && baridx <= 2); + + ResolvedType d = world.resolve("reflect.tests.D"); + methods = d.getDeclaredMethods(); + assertEquals(2, methods.length); + + classType = world.resolve("java.lang.Class"); + methods = classType.getDeclaredMethods(); + assertEquals(Class.class.getDeclaredMethods().length + Class.class.getDeclaredConstructors().length, methods.length); + } + + public void testGetDeclaredFields() { + ResolvedMember[] fields = objectType.getDeclaredFields(); + assertEquals(0, fields.length); + + ResolvedType c = world.resolve("reflect.tests.C"); + fields = c.getDeclaredFields(); + + assertEquals(2, fields.length); + assertEquals("f", fields[0].getName()); + assertEquals("s", fields[1].getName()); + assertEquals(UnresolvedType.INT, fields[0].getReturnType()); + assertEquals(world.resolve("java.lang.String"), fields[1].getReturnType()); + } + + public void testGetDeclaredInterfaces() { + ResolvedType[] interfaces = objectType.getDeclaredInterfaces(); + assertEquals(0, interfaces.length); + + ResolvedType d = world.resolve("reflect.tests.D"); + interfaces = d.getDeclaredInterfaces(); + assertEquals(1, interfaces.length); + assertEquals(world.resolve("java.io.Serializable"), interfaces[0]); + } + + public void testGetDeclaredPointcuts() { + ResolvedMember[] pointcuts = objectType.getDeclaredPointcuts(); + assertEquals(0, pointcuts.length); + } + + public void testSerializableSuperclass() { + ResolvedType serializableType = world.resolve("java.io.Serializable"); + ResolvedType superType = serializableType.getSuperclass(); + assertTrue("Superclass of serializable should be Object but was " + superType, superType.equals(UnresolvedType.OBJECT)); + + BcelWorld bcelworld = new BcelWorld(); + bcelworld.setBehaveInJava5Way(true); + ResolvedType bcelSupertype = bcelworld.resolve(UnresolvedType.SERIALIZABLE).getSuperclass(); + assertTrue("Should be null but is " + bcelSupertype, bcelSupertype.equals(UnresolvedType.OBJECT)); + } + + public void testSubinterfaceSuperclass() { + ResolvedType ifaceType = world.resolve("java.security.Key"); + ResolvedType superType = ifaceType.getSuperclass(); + assertTrue("Superclass should be Object but was " + superType, superType.equals(UnresolvedType.OBJECT)); + + BcelWorld bcelworld = new BcelWorld(); + bcelworld.setBehaveInJava5Way(true); + ResolvedType bcelSupertype = bcelworld.resolve("java.security.Key").getSuperclass(); + assertTrue("Should be null but is " + bcelSupertype, bcelSupertype.equals(UnresolvedType.OBJECT)); + } + + public void testVoidSuperclass() { + ResolvedType voidType = world.resolve(Void.TYPE); + ResolvedType superType = voidType.getSuperclass(); + assertNull(superType); + + BcelWorld bcelworld = new BcelWorld(); + bcelworld.setBehaveInJava5Way(true); + ResolvedType bcelSupertype = bcelworld.resolve("void").getSuperclass(); + assertTrue("Should be null but is " + bcelSupertype, bcelSupertype == null); + } + + public void testIntSuperclass() { + ResolvedType voidType = world.resolve(Integer.TYPE); + ResolvedType superType = voidType.getSuperclass(); + assertNull(superType); + + BcelWorld bcelworld = new BcelWorld(); + bcelworld.setBehaveInJava5Way(true); + ResolvedType bcelSupertype = bcelworld.resolve("int").getSuperclass(); + assertTrue("Should be null but is " + bcelSupertype, bcelSupertype == null); + } + + public void testGenericInterfaceSuperclass_BcelWorldResolution() { + BcelWorld bcelworld = new BcelWorld(); + bcelworld.setBehaveInJava5Way(true); + + UnresolvedType javaUtilMap = UnresolvedType.forName("java.util.Map"); + + ReferenceType rawType = (ReferenceType) bcelworld.resolve(javaUtilMap); + assertTrue("Should be the raw type ?!? " + rawType.getTypekind(), rawType.isRawType()); + + ReferenceType genericType = (ReferenceType) rawType.getGenericType(); + assertTrue("Should be the generic type ?!? " + genericType.getTypekind(), genericType.isGenericType()); + + ResolvedType rt = rawType.getSuperclass(); + assertTrue("Superclass for Map raw type should be Object but was " + rt, rt.equals(UnresolvedType.OBJECT)); + + ResolvedType rt2 = genericType.getSuperclass(); + assertTrue("Superclass for Map generic type should be Object but was " + rt2, rt2.equals(UnresolvedType.OBJECT)); + } + + // FIXME asc maybe. The reflection list of methods returned doesn't include (the static initializer) ... is that really + // a problem. + public void testCompareSubclassDelegates() { + + boolean barfIfClinitMissing = false; + world.setBehaveInJava5Way(true); + + BcelWorld bcelWorld = new BcelWorld(getClass().getClassLoader(), IMessageHandler.THROW, null); + bcelWorld.setBehaveInJava5Way(true); + UnresolvedType javaUtilHashMap = UnresolvedType.forName("java.util.HashMap"); + ReferenceType rawType = (ReferenceType) bcelWorld.resolve(javaUtilHashMap); + + ReferenceType rawReflectType = (ReferenceType) world.resolve(javaUtilHashMap); + ResolvedMember[] rms1 = rawType.getDelegate().getDeclaredMethods(); + ResolvedMember[] rms2 = rawReflectType.getDelegate().getDeclaredMethods(); + StringBuffer errors = new StringBuffer(); + Set one = new HashSet(); + for (int i = 0; i < rms1.length; i++) { + one.add(rms1[i].toString()); + } + Set two = new HashSet(); + for (int i = 0; i < rms2.length; i++) { + two.add(rms2[i].toString()); + } + for (int i = 0; i < rms2.length; i++) { + if (!one.contains(rms2[i].toString())) { + errors.append("Couldn't find " + rms2[i].toString() + " in the bcel set\n"); + } + } + for (int i = 0; i < rms1.length; i++) { + if (!two.contains(rms1[i].toString())) { + if (!barfIfClinitMissing && rms1[i].getName().equals("")) + continue; + errors.append("Couldn't find " + rms1[i].toString() + " in the reflection set\n"); + } + } + assertTrue("Errors:" + errors.toString(), errors.length() == 0); + + // the good old ibm vm seems to offer clinit through its reflection support (see pr145322) + if (rms1.length == rms2.length) + return; + if (barfIfClinitMissing) { + // the numbers must be exact + assertEquals(rms1.length, rms2.length); + } else { + // the numbers can be out by one in favour of bcel + if (rms1.length != (rms2.length + 1)) { + for (int i = 0; i < rms1.length; i++) { + System.err.println("bcel" + i + " is " + rms1[i]); + } + for (int i = 0; i < rms2.length; i++) { + System.err.println("refl" + i + " is " + rms2[i]); + } + } + assertTrue("Should be one extra (clinit) in BCEL case, but bcel=" + rms1.length + " reflect=" + rms2.length, + rms1.length == rms2.length + 1); + } + } + + public void testArrayArgsSig() throws Exception { + Method invokeMethod = Method.class.getMethod("invoke", new Class[] { Object.class, Object[].class }); + ResolvedMember reflectionMethod = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMethod(invokeMethod, world); + String exp = "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"; + assertTrue("Expected: \n" + exp + "\n but got:\n" + reflectionMethod.getSignature(), reflectionMethod.getSignature() + .equals(exp)); + } + + // todo: array of int + + protected void setUp() throws Exception { + world = new ReflectionWorld(getClass().getClassLoader()); + objectType = world.resolve("java.lang.Object"); + } +} diff --git a/weaver/src/test/java/org/aspectj/weaver/reflect/ReflectionWorldReferenceTypeTest.java b/weaver/src/test/java/org/aspectj/weaver/reflect/ReflectionWorldReferenceTypeTest.java new file mode 100644 index 000000000..720c88289 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/reflect/ReflectionWorldReferenceTypeTest.java @@ -0,0 +1,27 @@ +/* ******************************************************************* + * Copyright (c) 2002-2008 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement + * ******************************************************************/ +package org.aspectj.weaver.reflect; + +import org.aspectj.weaver.CommonReferenceTypeTests; +import org.aspectj.weaver.World; + +public class ReflectionWorldReferenceTypeTest extends CommonReferenceTypeTests { + + protected boolean getSupportsAutoboxing() { + return true; + } + + public World getWorld() { + return new ReflectionWorld(false, getClass().getClassLoader()); + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/reflect/ReflectionWorldTest.java b/weaver/src/test/java/org/aspectj/weaver/reflect/ReflectionWorldTest.java new file mode 100644 index 000000000..ede4e26d1 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/reflect/ReflectionWorldTest.java @@ -0,0 +1,290 @@ +/* ******************************************************************* + * Copyright (c) 2005,2017 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * ******************************************************************/ +package org.aspectj.weaver.reflect; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.List; +import java.util.Map; + +import org.aspectj.bridge.IMessageHandler; +import org.aspectj.weaver.ReferenceType; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.WeakClassLoaderReference; +import org.aspectj.weaver.World; +import org.aspectj.weaver.bcel.BcelWorld; + +import junit.framework.TestCase; + +/** + * @author Andy Clement + * @author Adrian Colyer + */ +public class ReflectionWorldTest extends TestCase { + + public void testDelegateCreation() { + World world = new ReflectionWorld(getClass().getClassLoader()); + ResolvedType rt = world.resolve("java.lang.Object"); + assertNotNull(rt); + assertEquals("Ljava/lang/Object;", rt.getSignature()); + } + + // Removed for now. In Spring the reflection worlds are customized by introducing new + // PCD handlers. It means more thought needs to be put into reusing worlds. + public void xtestReflectionWorldFactory() throws Exception { + ClassLoader parent = getClass().getClassLoader(); + ClassLoader cl1 = new URLClassLoader(new URL[] {}, parent); + ClassLoader cl2 = new URLClassLoader(new URL[] {}, parent); + + WeakClassLoaderReference wcl1 = new WeakClassLoaderReference(cl1); + WeakClassLoaderReference wcl2 = new WeakClassLoaderReference(cl2); + ReflectionWorld a = ReflectionWorld.getReflectionWorldFor(wcl1); + + ResolvedType stringClass1 = a.resolve(String.class); + assertNotNull(stringClass1); + + ReflectionWorld b = ReflectionWorld.getReflectionWorldFor(wcl1); + + // They should be the same because the classloader has not gone away + assertTrue(a==b); + + cl1 = null; + for (int i=0;i<100;i++) { + System.gc(); // How robust is it that this should be causing the reference to be collected? + } + b = ReflectionWorld.getReflectionWorldFor(wcl1); + + assertFalse(a==b); + + cl1 = new URLClassLoader(new URL[] {}, parent); + wcl1 = new WeakClassLoaderReference(cl1); + a = ReflectionWorld.getReflectionWorldFor(wcl1); + b = ReflectionWorld.getReflectionWorldFor(wcl2); + assertFalse(a==b); + + Field declaredField = ReflectionWorld.class.getDeclaredField("rworlds"); + declaredField.setAccessible(true); + Map worlds = (Map)declaredField.get(null); + assertEquals(2, worlds.size()); + + cl2 = null; + for (int i=0;i<100;i++) { + System.gc(); // How robust is it that this should be causing the reference to be collected? + } + ReflectionWorld.getReflectionWorldFor(wcl1); // need to call this to trigger tidyup + assertEquals(1, worlds.size()); + + cl1 = null; + for (int i=0;i<100;i++) { + System.gc(); // How robust is it that this should be causing the reference to be collected? + } + ReflectionWorld.getReflectionWorldFor(wcl1); // need to call this to trigger tidyup + assertEquals(0, worlds.size()); + + cl1 = new URLClassLoader(new URL[] {}, parent); + wcl1 = new WeakClassLoaderReference(cl1); + ReflectionWorld reflectionWorldFor = ReflectionWorld.getReflectionWorldFor(wcl1); + assertEquals(1, worlds.size()); + ReflectionWorld.cleanUpWorlds(); + assertEquals(0, worlds.size()); + } + + public void testArrayTypes() { + IReflectionWorld world = new ReflectionWorld(getClass().getClassLoader()); + String[] strArray = new String[1]; + ResolvedType rt = world.resolve(strArray.getClass()); + assertTrue(rt.isArray()); + } + + public void testPrimitiveTypes() { + IReflectionWorld world = new ReflectionWorld(getClass().getClassLoader()); + assertEquals("int", UnresolvedType.INT, world.resolve(int.class)); + assertEquals("void", UnresolvedType.VOID, world.resolve(void.class)); + } + + static class AbstractSuperClass {} + static interface InterfaceOne {} + static interface InterfaceTwo {} + static class ID {} + static abstract class AbstractTestClass extends AbstractSuperClass implements InterfaceOne, InterfaceTwo { + + } + static class TestType {} +// static class ConcreteClass extends AbstractTestClass { + static class ConcreteClass extends AbstractTestClass> { + } + + static class Bar extends ConcreteClass {} + + public void testGenerics() { + ReflectionWorld world = new ReflectionWorld(getClass().getClassLoader()); +// world.lookupOrCreateName(UnresolvedType.forName(AbstractTestClass.class.getName())); +// ResolvedType resolvedType = world.resolve(AbstractTestClass.class); + JavaLangTypeToResolvedTypeConverter converter = new JavaLangTypeToResolvedTypeConverter(world); + ResolvedType resolvedType2 = converter.fromType(ConcreteClass.class); + } + + public void xtestTypeConversions_509327() throws Exception { + ReflectionWorld rWorld = new ReflectionWorld(getClass().getClassLoader()); + JavaLangTypeToResolvedTypeConverter converter = new JavaLangTypeToResolvedTypeConverter(rWorld); + + // Check basic conversion of String to String + Method method = TestClass.class.getDeclaredMethod("m"); + Type stringType = method.getGenericReturnType(); + assertEquals("java.lang.String",stringType.getTypeName()); + ResolvedType stringResolvedType = converter.fromType(stringType); + assertEquals("java.lang.String",stringResolvedType.getName()); + + // public String m() { return ""; } + method = TestClass2.class.getDeclaredMethod("m"); + stringType = method.getGenericReturnType(); + assertEquals("java.lang.String",stringType.getTypeName()); + stringResolvedType = converter.fromType(stringType); + assertEquals("java.lang.String",stringResolvedType.getName()); + + // Verify that the conversion process creates the same thing as the bcel unpacking + + // Here the return type is a non-static inner of a generic class + // public Inner m2() { return null; } + method = TestClass2.class.getDeclaredMethod("m2"); + Type innerType = method.getGenericReturnType(); + assertEquals("org.aspectj.weaver.reflect.ReflectionWorldTest.org.aspectj.weaver.reflect.ReflectionWorldTest$TestClass2.Inner",innerType.getTypeName()); + ResolvedType rType_Inner = converter.fromType(innerType); + assertEquals("Lorg/aspectj/weaver/reflect/ReflectionWorldTest$TestClass2$Inner;",rType_Inner.getSignature()); + assertEquals(UnresolvedType.TypeKind.SIMPLE,rType_Inner.getTypekind()); + ResolvedType rType_Outer = rType_Inner.getOuterClass(); + assertEquals("Lorg/aspectj/weaver/reflect/ReflectionWorldTest$TestClass2;",rType_Outer.getSignature()); + + BcelWorld bWorld = new BcelWorld(getClass().getClassLoader(), IMessageHandler.THROW, null); + bWorld.setBehaveInJava5Way(true); + UnresolvedType javaUtilHashMap = UnresolvedType.forName("java.util.HashMap"); + ReferenceType rawType = (ReferenceType) bWorld.resolve(javaUtilHashMap); + assertNotNull(rawType); + + // Now use bcel to resolve the same m2 method, and compare the signatures of the return types + ResolvedType bResolved_TestClass2 = bWorld.resolve(UnresolvedType.forName(TestClass2.class.getName())); + assertNotNull(bResolved_TestClass2); + ResolvedMember bMethod_m2 = findMethod(bResolved_TestClass2,"m2"); + ResolvedType bType_Inner = (ResolvedType) bMethod_m2.getReturnType(); + assertEquals("Lorg/aspectj/weaver/reflect/ReflectionWorldTest$TestClass2$Inner;",bType_Inner.getSignature()); + assertEquals(UnresolvedType.TypeKind.SIMPLE,bType_Inner.getTypekind()); + ResolvedType bType_Outer = bType_Inner.getOuterClass(); + assertEquals("Lorg/aspectj/weaver/reflect/ReflectionWorldTest$TestClass2;",bType_Outer.getSignature()); + + assertEquals(bType_Inner.getSignature(),rType_Inner.getSignature()); + assertEquals(bType_Outer.getSignature(),rType_Outer.getSignature()); + } + + + public void xtestTypeConversions_509327_2() throws Exception { + ReflectionWorld world = new ReflectionWorld(getClass().getClassLoader()); + JavaLangTypeToResolvedTypeConverter converter = new JavaLangTypeToResolvedTypeConverter(world); + BcelWorld bWorld = new BcelWorld(getClass().getClassLoader(), IMessageHandler.THROW, null); + bWorld.setBehaveInJava5Way(true); + + // Slightly more advanced, now the method is returning a parameterized form of the outer + // generic class + + // public TestClass2.Inner m3() { return new TestClass2.Inner("Foo"); } + Method method = TestClass2.class.getDeclaredMethod("m3"); + Type type_ParameterizedInner = method.getGenericReturnType(); + assertEquals("org.aspectj.weaver.reflect.ReflectionWorldTest.org.aspectj.weaver.reflect.ReflectionWorldTest$TestClass2.Inner",type_ParameterizedInner.getTypeName()); + ResolvedType rType_ParameterizedInner = converter.fromType(type_ParameterizedInner); + // NOTE: DECLARED PARAMETERIZATION OF OUTER IS LOST + assertEquals("Lorg/aspectj/weaver/reflect/ReflectionWorldTest$TestClass2$Inner;",rType_ParameterizedInner.getSignature()); + + ResolvedType bResolved_TestClass2 = bWorld.resolve(UnresolvedType.forName(TestClass2.class.getName())); + assertNotNull(bResolved_TestClass2); + ResolvedMember bMethod_m3 = findMethod(bResolved_TestClass2,"m3"); + ResolvedType bType_Inner = (ResolvedType) bMethod_m3.getReturnType(); + // NOTE: DECLARED PARAMETERIZATION OF OUTER IS LOST + assertEquals("Lorg/aspectj/weaver/reflect/ReflectionWorldTest$TestClass2$Inner;",bType_Inner.getSignature()); + + assertEquals(UnresolvedType.TypeKind.SIMPLE,bType_Inner.getTypekind()); + ResolvedType bType_Outer = bType_Inner.getOuterClass(); + + // Fields seem to lose it too, although the backinggenericmember has the info +// ResolvedMember bField_f = findField(bResolved_TestClass2,"f"); +// ResolvedMember backingGenericMember = bField_f.getBackingGenericMember(); +// System.out.println(backingGenericMember); +// System.out.println(backingGenericMember.getGenericReturnType()); +// System.out.println(bField_f); +// System.out.println(bField_f.getSignature()); +// System.out.println(bField_f.getGenericReturnType()); + } + +// public void testbar() throws Exception { +// ReflectionWorld world = new ReflectionWorld(getClass().getClassLoader()); +// JavaLangTypeToResolvedTypeConverter converter = new JavaLangTypeToResolvedTypeConverter(world); +// +// // public TestClass2.Inner m3() { return new TestClass2.Inner("Foo"); } +// Method method = TestClass2.class.getDeclaredMethod("m3"); +// Type type_ParameterizedInner = method.getGenericReturnType(); +// assertEquals("org.aspectj.weaver.reflect.ReflectionWorldTest.org.aspectj.weaver.reflect.ReflectionWorldTest$TestClass2.Inner",type_ParameterizedInner.getTypeName()); +// ResolvedType rType_ParameterizedInner = converter.fromType(type_ParameterizedInner); +// System.out.println(rType_ParameterizedInner); +// System.out.println(type_ParameterizedInner.getTypeName()); +// } +// +// public void testfoo() { +// ReflectionWorld world = new ReflectionWorld(getClass().getClassLoader()); +// JavaLangTypeToResolvedTypeConverter converter = new JavaLangTypeToResolvedTypeConverter(world); +// BcelWorld bWorld = new BcelWorld(getClass().getClassLoader(), IMessageHandler.THROW, null); +// bWorld.setBehaveInJava5Way(true); +// +// +// ResolvedType bResolved_TestClass2 = bWorld.resolve(UnresolvedType.forName(TestClass2.class.getName())); +// ResolvedMember bField_f = findField(bResolved_TestClass2,"f"); +// System.out.println(bField_f); +// System.out.println(bField_f.getGenericReturnType()); +// System.out.println(bField_f.getReturnType()); +// System.out.println(bField_f.getBackingGenericMember().getGenericReturnType()); +// } + + static class TestClass { + public String m() { return ""; } + } + + static class TestClass2 { + class Inner { + T t; + Inner(T t) { + this.t = t; + } + } + public String m() { return ""; } + public Inner m2() { return null; } + public TestClass2 f; + public TestClass2.Inner m3() { return new TestClass2.Inner("Foo"); } + } + + private ResolvedMember findMethod(ResolvedType resolvedType, String methodName) { + for (ResolvedMember method: resolvedType.getDeclaredMethods()) { + if (method.getName().equals(methodName)) { + return method; + } + } + return null; + } + + private ResolvedMember findField(ResolvedType resolvedType, String fieldName) { + for (ResolvedMember field: resolvedType.getDeclaredFields()) { + if (field.getName().equals(fieldName)) { + return field; + } + } + return null; + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/tools/Java15PointcutExpressionTest.java b/weaver/src/test/java/org/aspectj/weaver/tools/Java15PointcutExpressionTest.java new file mode 100644 index 000000000..f651a2f29 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/tools/Java15PointcutExpressionTest.java @@ -0,0 +1,746 @@ +/* ******************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.tools; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.StringTokenizer; + +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.internal.tools.PointcutExpressionImpl; +import org.aspectj.weaver.patterns.AbstractPatternNodeVisitor; +import org.aspectj.weaver.patterns.AndAnnotationTypePattern; +import org.aspectj.weaver.patterns.AnnotationPatternList; +import org.aspectj.weaver.patterns.AnyAnnotationTypePattern; +import org.aspectj.weaver.patterns.BindingAnnotationTypePattern; +import org.aspectj.weaver.patterns.ExactAnnotationTypePattern; +import org.aspectj.weaver.patterns.KindedPointcut; +import org.aspectj.weaver.patterns.NotAnnotationTypePattern; +import org.aspectj.weaver.patterns.OrAnnotationTypePattern; +import org.aspectj.weaver.patterns.SignaturePattern; +import org.aspectj.weaver.patterns.TypePattern; +import org.aspectj.weaver.patterns.TypePatternList; +import org.aspectj.weaver.patterns.WildAnnotationTypePattern; + +import test.A1AnnotatedType; +import test.A2AnnotatedType; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * Test parameter pointcut parsing. Extended by Andy Clement to cover parameter annotation matching. + * + */ +public class Java15PointcutExpressionTest extends TestCase { + + public static Test suite() { + TestSuite suite = new TestSuite("Java15PointcutExpressionTest"); + suite.addTestSuite(Java15PointcutExpressionTest.class); + return suite; + } + + private PointcutParser parser; + private Method a; + private Method b; + private Method c; + private Method d; + + /** + * Parse some expressions and ensure we capture the parameter annotations and parameter type annotations correctly. + * Buckle up, this will get complicated ;) + */ + public void testParseParameterAnnotationExpressions() { + PointcutParser p = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingSpecifiedClassloaderForResolution(this.getClass().getClassLoader()); + PointcutExpression pexpr = null; + + pexpr = p.parsePointcutExpression("execution(public void foo(@MA *))"); + checkParameterAnnotations(pexpr,0,null,"@MA","exact[@MA:t]"); + + pexpr = p.parsePointcutExpression("execution(public void foo(@MA (*)))"); + checkParameterAnnotations(pexpr,0,"@MA",null,"exact[@MA:p]"); + + pexpr = p.parsePointcutExpression("execution(public void foo(@MA @MB *))"); + checkParameterAnnotations(pexpr,0,null,"@MA @MB","(exact[@MA:t] and exact[@MB:t])"); + + pexpr = p.parsePointcutExpression("execution(public void foo(@MA (@MB *)))"); + checkParameterAnnotations(pexpr,0,"@MA","@MB","(exact[@MA:p] and exact[@MB:t])"); + + pexpr = p.parsePointcutExpression("execution(public void foo(@MA @MB (@MC *)))"); + checkParameterAnnotations(pexpr,0,"@MA @MB","@MC","((exact[@MA:p] and exact[@MB:p]) and exact[@MC:t])"); + + pexpr = p.parsePointcutExpression("execution(public void foo(@MA (@MB @MC @MD *)))"); + checkParameterAnnotations(pexpr,0,"@MA","@MB @MC @MD","(exact[@MA:p] and ((exact[@MB:t] and exact[@MC:t]) and exact[@MD:t]))"); + + pexpr = p.parsePointcutExpression("execution(public void foo(@(MA || MB) (@MC @MD *)))"); + checkParameterAnnotations(pexpr,0,null/*Should be MA MB */,"@MC @MD","(wild[(MA || MB)] and (exact[@MC:t] and exact[@MD:t]))"); // I dont think WildAnnotationTypePatterns work properly... + + pexpr = p.parsePointcutExpression("execution(public void foo(@MA (@MB *),(@MC *),@MD (*)))"); + checkParameterAnnotations(pexpr,0,"@MA","@MB","(exact[@MA:p] and exact[@MB:t])"); + checkParameterAnnotations(pexpr,1,null,"@MC","exact[@MC:t]"); + checkParameterAnnotations(pexpr,2,"@MD",null,"exact[@MD:p]"); + + } + + public void testMatchingAnnotationValueExpressions() throws SecurityException, NoSuchMethodException { + PointcutParser p = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingSpecifiedClassloaderForResolution(this.getClass().getClassLoader()); + PointcutExpression pexpr = null; + ShadowMatch match = null; + + Method n = test.AnnoValues.class.getMethod("none"); + Method r = test.AnnoValues.class.getMethod("redMethod"); + Method g = test.AnnoValues.class.getMethod("greenMethod"); + Method b = test.AnnoValues.class.getMethod("blueMethod"); + Method d = test.AnnoValues.class.getMethod("defaultMethod"); + + pexpr = p.parsePointcutExpression("execution(@test.A3(test.Color.RED) public void *(..))"); + assertTrue("Should match", pexpr.matchesMethodExecution(n).neverMatches()); // default value RED + assertTrue("Should match", pexpr.matchesMethodExecution(r).alwaysMatches()); + assertTrue("Should not match", pexpr.matchesMethodExecution(g).neverMatches()); + assertTrue("Should not match", pexpr.matchesMethodExecution(b).neverMatches()); + assertTrue("Should match", pexpr.matchesMethodExecution(d).alwaysMatches()); + + pexpr = p.parsePointcutExpression("execution(@test.A3(test.Color.GREEN) public void *(..))"); + assertTrue("Should not match", pexpr.matchesMethodExecution(n).neverMatches()); // default value RED + assertTrue("Should not match", pexpr.matchesMethodExecution(r).neverMatches()); + assertTrue("Should match", pexpr.matchesMethodExecution(g).alwaysMatches()); + assertTrue("Should not match", pexpr.matchesMethodExecution(b).neverMatches()); + assertTrue("Should not match", pexpr.matchesMethodExecution(d).neverMatches()); + + pexpr = p.parsePointcutExpression("execution(@test.A3(test.Color.BLUE) public void *(..))"); + assertTrue("Should not match", pexpr.matchesMethodExecution(n).neverMatches()); // default value RED + assertTrue("Should not match", pexpr.matchesMethodExecution(r).neverMatches()); + assertTrue("Should not match", pexpr.matchesMethodExecution(g).neverMatches()); + assertTrue("Should match", pexpr.matchesMethodExecution(b).alwaysMatches()); + assertTrue("Should not match", pexpr.matchesMethodExecution(d).neverMatches()); + + pexpr = p.parsePointcutExpression("execution(@test.A3 public void *(..))"); + assertTrue("Should match", pexpr.matchesMethodExecution(n).neverMatches()); // default value RED + assertTrue("Should match", pexpr.matchesMethodExecution(r).alwaysMatches()); + assertTrue("Should match", pexpr.matchesMethodExecution(g).alwaysMatches()); + assertTrue("Should match", pexpr.matchesMethodExecution(b).alwaysMatches()); + assertTrue("Should match", pexpr.matchesMethodExecution(d).alwaysMatches()); + + } + + private static final Set DEFAULT_SUPPORTED_PRIMITIVES = new HashSet(); + + static { + DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION); + DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.ARGS); + DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.REFERENCE); + DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.THIS); + DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.TARGET); + DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.WITHIN); + DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ANNOTATION); + DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_WITHIN); + DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ARGS); + DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_TARGET); + } + + public void testPerformanceOfPointcutParsing() { + String expression = "execution(public * rewards.internal.*.*Repository+.*(..))"; + long stime1 = System.currentTimeMillis(); + PointcutParser parser = PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingContextClassloaderForResolution(DEFAULT_SUPPORTED_PRIMITIVES); + long stime2 = System.currentTimeMillis(); + PointcutExpression pointcutExpression = parser.parsePointcutExpression(expression, null, new PointcutParameter[0]); + long etime = System.currentTimeMillis(); + System.out.println("Time to get a parser "+(stime2-stime1)+"ms"); + System.out.println("Time taken to parse expression is "+(etime-stime2)+"ms"); + } + + + public void testPerformanceOfPointcutParsingWithBean() { + String expression = "execution(public * rewards.internal.*.*Repository+.*(..))"; + PointcutParser parser = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingSpecifiedClassloaderForResolution(this.getClass().getClassLoader()); + BeanDesignatorHandler beanHandler = new BeanDesignatorHandler(); + parser.registerPointcutDesignatorHandler(beanHandler); + long stime = System.currentTimeMillis(); + PointcutExpression pointcutExpression = parser.parsePointcutExpression(expression, null, new PointcutParameter[0]); + long etime = System.currentTimeMillis(); + System.out.println("Time taken to parse expression is "+(etime-stime)+"ms"); + } + + private class BeanDesignatorHandler implements PointcutDesignatorHandler { + + private String askedToParse; + public boolean simulateDynamicTest = false; + + public String getDesignatorName() { + return "bean"; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.tools.PointcutDesignatorHandler#parse(java.lang.String) + */ + public ContextBasedMatcher parse(String expression) { + this.askedToParse = expression; + return null; +// return new BeanPointcutExpression(expression,this.simulateDynamicTest); + } + + public String getExpressionLastAskedToParse() { + return this.askedToParse; + } + } + + + /** + * Test matching of pointcuts against expressions. A reflection world is being used on the backend here (not a Bcel one). + */ + public void testMatchingParameterAnnotationExpressions() throws SecurityException, NoSuchMethodException { + PointcutParser p = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingSpecifiedClassloaderForResolution(this.getClass().getClassLoader()); + PointcutExpression pexpr = null; + ShadowMatch match = null; + + Method a = test.A.class.getMethod("a",new Class[] {String.class}); // public void a(String s) {} + Method b = test.A.class.getMethod("b",new Class[] {String.class}); // public void b(@A1 String s) {} + Method c = test.A.class.getMethod("c",new Class[] {String.class}); // public void c(@A1 @A2 String s) {} +// Method d = test.A.class.getMethod("d",new Class[] {String.class,String.class});// public void d(@A1 String s,@A2 String t) {} + + Method e = test.A.class.getMethod("e",new Class[] {A1AnnotatedType.class}); // public void e(A1AnnotatedType s) {} + Method f = test.A.class.getMethod("f",new Class[] {A2AnnotatedType.class}); // public void f(A2AnnotatedType s) {} + Method g = test.A.class.getMethod("g",new Class[] {A1AnnotatedType.class}); // public void g(@A2 A1AnnotatedType s) {} + Method h = test.A.class.getMethod("h",new Class[] {A1AnnotatedType.class}); // public void h(@A1 A1AnnotatedType s) {} +// Method i = test.A.class.getMethod("i",new Class[] {A1AnnotatedType.class,String.class}); // public void i(A1AnnotatedType s,@A2 String t) {} +// Method j = test.A.class.getMethod("j",new Class[] {String.class}); // public void j(@A1 @A2 String s) {} + + pexpr = p.parsePointcutExpression("execution(public void *(@test.A1 *))"); + assertTrue("Should not match", pexpr.matchesMethodExecution(a).neverMatches()); + assertTrue("Should not match", pexpr.matchesMethodExecution(b).neverMatches()); + assertTrue("Should not match", pexpr.matchesMethodExecution(c).neverMatches()); + + pexpr = p.parsePointcutExpression("execution(public void *(@test.A1 (*)))"); + assertTrue("Should not match", pexpr.matchesMethodExecution(a).neverMatches()); + assertTrue("Should match", pexpr.matchesMethodExecution(b).alwaysMatches()); + assertTrue("Should match", pexpr.matchesMethodExecution(c).alwaysMatches()); + + pexpr = p.parsePointcutExpression("execution(public void *(@test.A1 *))"); + assertTrue("Should match", pexpr.matchesMethodExecution(e).alwaysMatches()); + assertTrue("Should not match", pexpr.matchesMethodExecution(f).neverMatches()); + assertTrue("Should match", pexpr.matchesMethodExecution(g).alwaysMatches()); + assertTrue("Should match", pexpr.matchesMethodExecution(h).alwaysMatches()); + + pexpr = p.parsePointcutExpression("execution(public void *(@test.A1 (*)))"); + assertTrue("Should not match", pexpr.matchesMethodExecution(e).neverMatches()); + assertTrue("Should not match", pexpr.matchesMethodExecution(f).neverMatches()); + assertTrue("Should not match", pexpr.matchesMethodExecution(g).neverMatches()); + assertTrue("Should match", pexpr.matchesMethodExecution(h).alwaysMatches()); + + pexpr = p.parsePointcutExpression("execution(public void *(@(test.A1 || test.A2) (*)))"); + assertTrue("Should not match", pexpr.matchesMethodExecution(a).neverMatches()); + assertTrue("Should match", pexpr.matchesMethodExecution(b).alwaysMatches()); + assertTrue("Should match", pexpr.matchesMethodExecution(c).alwaysMatches()); + assertTrue("Should match", pexpr.matchesMethodExecution(g).alwaysMatches()); + assertTrue("Should match", pexpr.matchesMethodExecution(h).alwaysMatches()); + + pexpr = p.parsePointcutExpression("execution(public void *(@(test.A1 && test.A2) (*),..))"); + assertTrue("Should not match", pexpr.matchesMethodExecution(a).neverMatches()); + assertTrue("Should not match", pexpr.matchesMethodExecution(b).neverMatches()); + assertTrue("Should not match", pexpr.matchesMethodExecution(c).neverMatches()); + assertTrue("Should not match", pexpr.matchesMethodExecution(g).neverMatches()); + assertTrue("Should not match", pexpr.matchesMethodExecution(h).neverMatches()); +// assertTrue("Should match", pexpr.matchesMethodExecution(j).alwaysMatches()); // should match but does not, broken implementation, old bug - see WildAnnotationTypePattern.match + + + + } + + private void checkParameterAnnotations(PointcutExpression pe,int parameterNumber,String expectedParameterAnnotations,String expectedParameterTypeAnnotations,String expectedNodeStructure) { + org.aspectj.weaver.patterns.Pointcut p = ((PointcutExpressionImpl)pe).getUnderlyingPointcut(); + KindedPointcut kindedP = (KindedPointcut)p; + SignaturePattern sp = kindedP.getSignature(); + TypePatternList tpl = sp.getParameterTypes(); + TypePattern[] tps = tpl.getTypePatterns(); + + // A visitor over the annotation pattern for the parameter will break it down into parameter vs parameter type annotations + MyPatternNodeVisitor mpnv = new MyPatternNodeVisitor(); + tps[parameterNumber].getAnnotationPattern().accept(mpnv,null); + + if (expectedNodeStructure==null) { + // The caller hasn't worked it out yet!! + System.out.println(mpnv.getStringRepresentation()); + } else if (!mpnv.getStringRepresentation().equals(expectedNodeStructure)) { + System.out.println(mpnv.getStringRepresentation()); + fail("Expected annotation pattern node structure for expression "+pe.getPointcutExpression()+ + " was '"+expectedNodeStructure+"' but it turned out to be '"+mpnv.getStringRepresentation()+"'"); + } + + tps[parameterNumber].getAnnotationPattern().toString(); + + // parameter type annotation checking + Set expected = new HashSet(); + expected.addAll(mpnv.getParameterTypeAnnotations()); + + StringTokenizer st = new StringTokenizer(expectedParameterTypeAnnotations==null?"":expectedParameterTypeAnnotations); + while (st.hasMoreTokens()) { + String nextToken = st.nextToken(); + if (!expected.contains(nextToken)) + fail("In pointcut expression "+pe.getPointcutExpression()+" parameter "+parameterNumber+". The annotation type pattern did not include parameter type annotation "+nextToken+". It's full set was "+mpnv.getParameterTypeAnnotations()); + expected.remove(nextToken); + } + if (expected.size()>0) { // we have excess ones! + StringBuffer excessTokens = new StringBuffer(); + for (Iterator iterator = expected.iterator(); iterator.hasNext();) { + String string = (String) iterator.next(); + excessTokens.append(string).append(" "); + } + fail("In pointcut expression "+pe.getPointcutExpression()+" parameter "+parameterNumber+". The annotation type pattern has these unexpected parameter type annotations "+excessTokens.toString()); + } + + // parameter annotation checking + expected = new HashSet(); + expected.addAll(mpnv.getParameterAnnotations()); + + st = new StringTokenizer(expectedParameterAnnotations==null?"":expectedParameterAnnotations); + while (st.hasMoreTokens()) { + String nextToken = st.nextToken(); + if (!expected.contains(nextToken)) + fail("In pointcut expression "+pe.getPointcutExpression()+" parameter "+parameterNumber+". The annotation type pattern did not include parameter annotation "+nextToken+". It's full set was "+mpnv.getParameterAnnotations()); + expected.remove(nextToken); + } + if (expected.size()>0) { // we have excess ones! + StringBuffer excessTokens = new StringBuffer(); + for (Iterator iterator = expected.iterator(); iterator.hasNext();) { + String string = (String) iterator.next(); + excessTokens.append(string).append(" "); + } + fail("In pointcut expression "+pe.getPointcutExpression()+" parameter "+parameterNumber+". The annotation type pattern has these unexpected parameter annotations "+excessTokens.toString()); + } + + } + + static class MyPatternNodeVisitor extends AbstractPatternNodeVisitor { + private StringBuffer stringRep = new StringBuffer(); + private List parameterAnnotations = new ArrayList(); + private List parameterTypeAnnotations = new ArrayList(); + + public String getStringRepresentation() { return stringRep.toString(); } + public List getParameterAnnotations() { return parameterAnnotations; } + public List getParameterTypeAnnotations() { return parameterTypeAnnotations; } + + public Object visit(AndAnnotationTypePattern node, Object data) { + stringRep.append("("); + node.getLeft().accept(this, data); + stringRep.append(" and "); + node.getRight().accept(this, data); + stringRep.append(")"); + return node; + } + public Object visit(AnyAnnotationTypePattern node, Object data) { + stringRep.append("any"); + return node; + } + public Object visit(ExactAnnotationTypePattern node, Object data) { + stringRep.append("exact["+stringify(node.getResolvedAnnotationType())+":"+(node.isForParameterAnnotationMatch()?"p":"t")+"]"); + if (node.isForParameterAnnotationMatch()) { + parameterAnnotations.add(stringify(node.getResolvedAnnotationType())); + } else { + parameterTypeAnnotations.add(stringify(node.getResolvedAnnotationType())); + } + return node; + } + private String stringify(ResolvedType resolvedAnnotationType) { + return "@"+resolvedAnnotationType.getSimpleName(); + } + + public Object visit(BindingAnnotationTypePattern node, Object data) { + stringRep.append("binding"); + + return node; + } + public Object visit(NotAnnotationTypePattern node, Object data) { + stringRep.append("not"); + return node; + } + public Object visit(OrAnnotationTypePattern node, Object data) { + stringRep.append("("); + node.getLeft().accept(this, data); + stringRep.append(" or "); + node.getRight().accept(this, data); + stringRep.append(")"); + return node; + } + public Object visit(WildAnnotationTypePattern node, Object data) { + stringRep.append("wild["); + stringRep.append(node.getTypePattern().toString()); + stringRep.append("]"); + return node; + } + public Object visit(AnnotationPatternList node, Object data) { + stringRep.append("list"); + + return node; + } + + + } + + + + public void testAtThis() { + PointcutExpression atThis = parser.parsePointcutExpression("@this(org.aspectj.weaver.tools.Java15PointcutExpressionTest.MyAnnotation)"); + ShadowMatch sMatch1 = atThis.matchesMethodExecution(a); + ShadowMatch sMatch2 = atThis.matchesMethodExecution(b); + assertTrue("maybe matches A",sMatch1.maybeMatches()); + assertTrue("maybe matches B",sMatch2.maybeMatches()); + JoinPointMatch jp1 = sMatch1.matchesJoinPoint(new A(), new A(), new Object[0]); + assertFalse("does not match",jp1.matches()); + JoinPointMatch jp2 = sMatch2.matchesJoinPoint(new B(), new B(), new Object[0]); + assertTrue("matches",jp2.matches()); + } + + public void testAtTarget() { + PointcutExpression atTarget = parser.parsePointcutExpression("@target(org.aspectj.weaver.tools.Java15PointcutExpressionTest.MyAnnotation)"); + ShadowMatch sMatch1 = atTarget.matchesMethodExecution(a); + ShadowMatch sMatch2 = atTarget.matchesMethodExecution(b); + assertTrue("maybe matches A",sMatch1.maybeMatches()); + assertTrue("maybe matches B",sMatch2.maybeMatches()); + JoinPointMatch jp1 = sMatch1.matchesJoinPoint(new A(), new A(), new Object[0]); + assertFalse("does not match",jp1.matches()); + JoinPointMatch jp2 = sMatch2.matchesJoinPoint(new B(), new B(), new Object[0]); + assertTrue("matches",jp2.matches()); + } + + public void testAtThisWithBinding() { + PointcutParameter param = parser.createPointcutParameter("a",MyAnnotation.class); + B myB = new B(); + MyAnnotation bAnnotation = B.class.getAnnotation(MyAnnotation.class); + PointcutExpression atThis = parser.parsePointcutExpression("@this(a)",A.class,new PointcutParameter[] {param}); + ShadowMatch sMatch1 = atThis.matchesMethodExecution(a); + ShadowMatch sMatch2 = atThis.matchesMethodExecution(b); + assertTrue("maybe matches A",sMatch1.maybeMatches()); + assertTrue("maybe matches B",sMatch2.maybeMatches()); + JoinPointMatch jp1 = sMatch1.matchesJoinPoint(new A(), new A(), new Object[0]); + assertFalse("does not match",jp1.matches()); + JoinPointMatch jp2 = sMatch2.matchesJoinPoint(myB, myB, new Object[0]); + assertTrue("matches",jp2.matches()); + assertEquals(1,jp2.getParameterBindings().length); + assertEquals("should be myB's annotation",bAnnotation,jp2.getParameterBindings()[0].getBinding()); + } + + public void testAtTargetWithBinding() { + PointcutParameter param = parser.createPointcutParameter("a",MyAnnotation.class); + B myB = new B(); + MyAnnotation bAnnotation = B.class.getAnnotation(MyAnnotation.class); + PointcutExpression atThis = parser.parsePointcutExpression("@target(a)",A.class,new PointcutParameter[] {param}); + ShadowMatch sMatch1 = atThis.matchesMethodExecution(a); + ShadowMatch sMatch2 = atThis.matchesMethodExecution(b); + assertTrue("maybe matches A",sMatch1.maybeMatches()); + assertTrue("maybe matches B",sMatch2.maybeMatches()); + JoinPointMatch jp1 = sMatch1.matchesJoinPoint(new A(), new A(), new Object[0]); + assertFalse("does not match",jp1.matches()); + JoinPointMatch jp2 = sMatch2.matchesJoinPoint(myB, myB, new Object[0]); + assertTrue("matches",jp2.matches()); + assertEquals(1,jp2.getParameterBindings().length); + assertEquals("should be myB's annotation",bAnnotation,jp2.getParameterBindings()[0].getBinding()); + } + + public void testAtArgs() { + PointcutExpression atArgs = parser.parsePointcutExpression("@args(..,org.aspectj.weaver.tools.Java15PointcutExpressionTest.MyAnnotation)"); + ShadowMatch sMatch1 = atArgs.matchesMethodExecution(a); + ShadowMatch sMatch2 = atArgs.matchesMethodExecution(c); + assertTrue("never matches A",sMatch1.neverMatches()); + assertTrue("maybe matches C",sMatch2.maybeMatches()); + JoinPointMatch jp2 = sMatch2.matchesJoinPoint(new B(), new B(), new Object[]{new A(),new B()}); + assertTrue("matches",jp2.matches()); + + atArgs = parser.parsePointcutExpression("@args(org.aspectj.weaver.tools.Java15PointcutExpressionTest.MyAnnotation,org.aspectj.weaver.tools.Java15PointcutExpressionTest.MyAnnotation)"); + sMatch1 = atArgs.matchesMethodExecution(a); + sMatch2 = atArgs.matchesMethodExecution(c); + assertTrue("never matches A",sMatch1.neverMatches()); + assertTrue("maybe matches C",sMatch2.maybeMatches()); + JoinPointMatch jp1 = sMatch2.matchesJoinPoint(new A(), new A(), new Object[] {new A(), new B()}); + assertFalse("does not match",jp1.matches()); + jp2 = sMatch2.matchesJoinPoint(new B(), new B(), new Object[] {new B(),new B()}); + assertTrue("matches",jp2.matches()); + } + + public void testAtArgs2() { + PointcutExpression atArgs = parser.parsePointcutExpression("@args(*, org.aspectj.weaver.tools.Java15PointcutExpressionTest.MyAnnotation)"); + ShadowMatch sMatch1 = atArgs.matchesMethodExecution(c); + ShadowMatch sMatch2 = atArgs.matchesMethodExecution(d); + assertTrue("maybe matches c",sMatch1.maybeMatches()); + assertTrue("maybe matches d",sMatch2.maybeMatches()); + JoinPointMatch jp1 = sMatch1.matchesJoinPoint(new B(), new B(), new Object[] {new A(), new B()}); + assertTrue("matches",jp1.matches()); + JoinPointMatch jp2 = sMatch2.matchesJoinPoint(new B(), new B(), new Object[] {new A(),new A()}); + assertFalse("does not match",jp2.matches()); + } + + public void testAtArgsWithBinding() { + PointcutParameter p1 = parser.createPointcutParameter("a",MyAnnotation.class); + PointcutParameter p2 = parser.createPointcutParameter("b", MyAnnotation.class); + PointcutExpression atArgs = parser.parsePointcutExpression("@args(..,a)",A.class,new PointcutParameter[] {p1}); + ShadowMatch sMatch2 = atArgs.matchesMethodExecution(c); + assertTrue("maybe matches C",sMatch2.maybeMatches()); + JoinPointMatch jp2 = sMatch2.matchesJoinPoint(new B(), new B(), new Object[]{new A(),new B()}); + assertTrue("matches",jp2.matches()); + assertEquals(1,jp2.getParameterBindings().length); + MyAnnotation bAnnotation = B.class.getAnnotation(MyAnnotation.class); + assertEquals("annotation on B",bAnnotation,jp2.getParameterBindings()[0].getBinding()); + + atArgs = parser.parsePointcutExpression("@args(a,b)",A.class,new PointcutParameter[] {p1,p2}); + sMatch2 = atArgs.matchesMethodExecution(c); + assertTrue("maybe matches C",sMatch2.maybeMatches()); + jp2 = sMatch2.matchesJoinPoint(new B(), new B(), new Object[] {new B(),new B()}); + assertTrue("matches",jp2.matches()); + assertEquals(2,jp2.getParameterBindings().length); + assertEquals("annotation on B",bAnnotation,jp2.getParameterBindings()[0].getBinding()); + assertEquals("annotation on B",bAnnotation,jp2.getParameterBindings()[1].getBinding()); + } + + public void testAtWithin() { + PointcutExpression atWithin = parser.parsePointcutExpression("@within(org.aspectj.weaver.tools.Java15PointcutExpressionTest.MyAnnotation)"); + ShadowMatch sMatch1 = atWithin.matchesMethodExecution(a); + ShadowMatch sMatch2 = atWithin.matchesMethodExecution(b); + assertTrue("does not match a",sMatch1.neverMatches()); + assertTrue("matches b",sMatch2.alwaysMatches()); + } + + public void testAtWithinWithBinding() { + PointcutParameter p1 = parser.createPointcutParameter("x",MyAnnotation.class); + PointcutExpression atWithin = parser.parsePointcutExpression("@within(x)",B.class,new PointcutParameter[] {p1}); + ShadowMatch sMatch1 = atWithin.matchesMethodExecution(a); + ShadowMatch sMatch2 = atWithin.matchesMethodExecution(b); + assertTrue("does not match a",sMatch1.neverMatches()); + assertTrue("matches b",sMatch2.alwaysMatches()); + JoinPointMatch jpm = sMatch2.matchesJoinPoint(new B(), new B(), new Object[0]); + assertTrue(jpm.matches()); + assertEquals(1,jpm.getParameterBindings().length); + MyAnnotation bAnnotation = B.class.getAnnotation(MyAnnotation.class); + assertEquals("annotation on B",bAnnotation,jpm.getParameterBindings()[0].getBinding()); + } + + public void testAtWithinCode() { + PointcutExpression atWithinCode = parser.parsePointcutExpression("@withincode(org.aspectj.weaver.tools.Java15PointcutExpressionTest.MyAnnotation)"); + ShadowMatch sMatch1 = atWithinCode.matchesMethodCall(a,b); + ShadowMatch sMatch2 = atWithinCode.matchesMethodCall(a,a); + assertTrue("does not match from b",sMatch1.neverMatches()); + assertTrue("matches from a",sMatch2.alwaysMatches()); + } + + public void testAtWithinCodeWithBinding() { + PointcutParameter p1 = parser.createPointcutParameter("x",MyAnnotation.class); + PointcutExpression atWithinCode = parser.parsePointcutExpression("@withincode(x)",A.class,new PointcutParameter[] {p1}); + ShadowMatch sMatch2 = atWithinCode.matchesMethodCall(a,a); + assertTrue("matches from a",sMatch2.alwaysMatches()); + JoinPointMatch jpm = sMatch2.matchesJoinPoint(new A(), new A(), new Object[0]); + assertEquals(1,jpm.getParameterBindings().length); + MyAnnotation annOna = a.getAnnotation(MyAnnotation.class); + assertEquals("MyAnnotation on a",annOna,jpm.getParameterBindings()[0].getBinding()); + } + + public void testAtAnnotation() { + PointcutExpression atAnnotation = parser.parsePointcutExpression("@annotation(org.aspectj.weaver.tools.Java15PointcutExpressionTest.MyAnnotation)"); + ShadowMatch sMatch1 = atAnnotation.matchesMethodCall(b,a); + ShadowMatch sMatch2 = atAnnotation.matchesMethodCall(a,a); + assertTrue("does not match call to b",sMatch1.neverMatches()); + assertTrue("matches call to a",sMatch2.alwaysMatches()); + } + + public void testAtAnnotationWithBinding() { + PointcutParameter p1 = parser.createPointcutParameter("x",MyAnnotation.class); + PointcutExpression atAnnotation = parser.parsePointcutExpression("@annotation(x)",A.class,new PointcutParameter[] {p1}); + ShadowMatch sMatch2 = atAnnotation.matchesMethodCall(a,a); + assertTrue("matches call to a",sMatch2.alwaysMatches()); + JoinPointMatch jpm = sMatch2.matchesJoinPoint(new A(), new A(), new Object[0]); + assertTrue(jpm.matches()); + assertEquals(1,jpm.getParameterBindings().length); + MyAnnotation annOna = a.getAnnotation(MyAnnotation.class); + assertEquals("MyAnnotation on a",annOna,jpm.getParameterBindings()[0].getBinding()); + } + + public void testReferencePointcutNoParams() { + PointcutExpression pc = parser.parsePointcutExpression("foo()",C.class,new PointcutParameter[0]); + ShadowMatch sMatch1 = pc.matchesMethodCall(a,b); + ShadowMatch sMatch2 = pc.matchesMethodExecution(a); + assertTrue("no match on call",sMatch1.neverMatches()); + assertTrue("match on execution",sMatch2.alwaysMatches()); + + pc = parser.parsePointcutExpression("org.aspectj.weaver.tools.Java15PointcutExpressionTest.C.foo()"); + sMatch1 = pc.matchesMethodCall(a,b); + sMatch2 = pc.matchesMethodExecution(a); + assertTrue("no match on call",sMatch1.neverMatches()); + assertTrue("match on execution",sMatch2.alwaysMatches()); + } + + public void testReferencePointcutParams() { + PointcutParameter p1 = parser.createPointcutParameter("x",A.class); + PointcutExpression pc = parser.parsePointcutExpression("goo(x)",C.class,new PointcutParameter[] {p1}); + + ShadowMatch sMatch1 = pc.matchesMethodCall(a,b); + ShadowMatch sMatch2 = pc.matchesMethodExecution(a); + assertTrue("no match on call",sMatch1.neverMatches()); + assertTrue("match on execution",sMatch2.maybeMatches()); + A anA = new A(); + JoinPointMatch jpm = sMatch2.matchesJoinPoint(anA, new A(), new Object[0]); + assertTrue(jpm.matches()); + assertEquals("should be bound to anA",anA,jpm.getParameterBindings()[0].getBinding()); + + } + + public void testExecutionWithClassFileRetentionAnnotation() { + PointcutExpression pc1 = parser.parsePointcutExpression("execution(@org.aspectj.weaver.tools.Java15PointcutExpressionTest.MyAnnotation * *(..))"); + PointcutExpression pc2 = parser.parsePointcutExpression("execution(@org.aspectj.weaver.tools.Java15PointcutExpressionTest.MyClassFileRetentionAnnotation * *(..))"); + ShadowMatch sMatch = pc1.matchesMethodExecution(a); + assertTrue("matches",sMatch.alwaysMatches()); + sMatch = pc2.matchesMethodExecution(a); + assertTrue("no match",sMatch.neverMatches()); + sMatch = pc1.matchesMethodExecution(b); + assertTrue("no match",sMatch.neverMatches()); + sMatch = pc2.matchesMethodExecution(b); + assertTrue("matches",sMatch.alwaysMatches()); + } + + public void testGenericMethodSignatures() throws Exception{ + PointcutExpression ex = parser.parsePointcutExpression("execution(* set*(java.util.List))"); + Method m = TestBean.class.getMethod("setFriends",List.class); + ShadowMatch sm = ex.matchesMethodExecution(m); + assertTrue("should match",sm.alwaysMatches()); + } + + public void testAnnotationInExecution() throws Exception { + parser.parsePointcutExpression("execution(@(org.springframework..*) * *(..))"); + } + + public void testVarArgsMatching() throws Exception { + PointcutExpression ex = parser.parsePointcutExpression("execution(* *(String...))"); + Method usesVarArgs = D.class.getMethod("varArgs",String[].class); + Method noVarArgs = D.class.getMethod("nonVarArgs", String[].class); + ShadowMatch sm1 = ex.matchesMethodExecution(usesVarArgs); + assertTrue("should match",sm1.alwaysMatches()); + ShadowMatch sm2 = ex.matchesMethodExecution(noVarArgs); + assertFalse("should not match",sm2.alwaysMatches()); + } + + public void testJavaLangMatching() throws Exception { + PointcutExpression ex = parser.parsePointcutExpression("@within(java.lang.Deprecated)"); + Method foo = GoldenOldie.class.getMethod("foo"); + ShadowMatch sm1 = ex.matchesMethodExecution(foo); + assertTrue("should match",sm1.alwaysMatches()); + } + + public void testReferencePCsInSameType() throws Exception { + PointcutExpression ex = parser.parsePointcutExpression("org.aspectj.weaver.tools.Java15PointcutExpressionTest.NamedPointcutResolution.c()",NamedPointcutResolution.class,new PointcutParameter[0]); + ShadowMatch sm = ex.matchesMethodExecution(a); + assertTrue("should match",sm.alwaysMatches()); + sm = ex.matchesMethodExecution(b); + assertTrue("does not match",sm.neverMatches()); + } + + public void testReferencePCsInOtherType() throws Exception { + PointcutExpression ex = parser.parsePointcutExpression("org.aspectj.weaver.tools.Java15PointcutExpressionTest.ExternalReferrer.d()",ExternalReferrer.class,new PointcutParameter[0]); + ShadowMatch sm = ex.matchesMethodExecution(a); + assertTrue("should match",sm.alwaysMatches()); + sm = ex.matchesMethodExecution(b); + assertTrue("does not match",sm.neverMatches()); + } + + public void testArrayTypeInArgs() throws Exception { + PointcutParameter[] params = new PointcutParameter[3]; + params[0] = parser.createPointcutParameter("d", Date.class); + params[1] = parser.createPointcutParameter("s", String.class); + params[2] = parser.createPointcutParameter("ss", String[].class); + parser.parsePointcutExpression("org.aspectj.weaver.tools.Java15PointcutExpressionTest.UsesArrays.pc(d,s,ss)",UsesArrays.class,params); + } + + protected void setUp() throws Exception { + super.setUp(); + parser = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingSpecifiedClassloaderForResolution(this.getClass().getClassLoader()); + a = A.class.getMethod("a"); + b = B.class.getMethod("b"); + c = B.class.getMethod("c",new Class[] {A.class,B.class}); + d = B.class.getMethod("d",new Class[] {A.class,A.class}); + } + + @Retention(RetentionPolicy.RUNTIME) + private @interface MyAnnotation {} + + private @interface MyClassFileRetentionAnnotation {} + + private static class A { + @MyAnnotation public void a() {} + } + + @MyAnnotation + private static class B { + @MyClassFileRetentionAnnotation public void b() {} + public void c(A anA, B aB) {} + + public void d(A anA, A anotherA) {} + } + + private static class C { + + @Pointcut("execution(* *(..))") + public void foo() {} + + @Pointcut(value="execution(* *(..)) && this(x)", argNames="x") + public void goo(A x) {} + } + + private static class D { + + public void nonVarArgs(String[] strings) {}; + + public void varArgs(String... strings) {}; + + } + + static class TestBean { + public void setFriends(List friends) {} + } + + @Deprecated + static class GoldenOldie { + public void foo() {} + } + + private static class NamedPointcutResolution { + + @Pointcut("execution(* *(..))") + public void a() {} + + @Pointcut("this(org.aspectj.weaver.tools.Java15PointcutExpressionTest.A)") + public void b() {} + + @Pointcut("a() && b()") + public void c() {} + } + + private static class ExternalReferrer { + + @Pointcut("org.aspectj.weaver.tools.Java15PointcutExpressionTest.NamedPointcutResolution.a() && " + + "org.aspectj.weaver.tools.Java15PointcutExpressionTest.NamedPointcutResolution.b())") + public void d() {} + + } + + private static class UsesArrays { + + @Pointcut("execution(* *(..)) && args(d,s,ss)") + public void pc(Date d, String s, String[] ss) {} + + } +} + + diff --git a/weaver/src/test/java/org/aspectj/weaver/tools/PointcutDesignatorHandlerTest.java b/weaver/src/test/java/org/aspectj/weaver/tools/PointcutDesignatorHandlerTest.java new file mode 100644 index 000000000..83d7b461d --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/tools/PointcutDesignatorHandlerTest.java @@ -0,0 +1,251 @@ +/* ******************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.tools; + +import junit.framework.TestCase; + +import org.aspectj.util.LangUtil; + +/** + * @author Adrian Colyer + * + */ +public class PointcutDesignatorHandlerTest extends TestCase { + + boolean needToSkip = false; + + protected void setUp() throws Exception { + super.setUp(); + needToSkip = needToSkipPointcutParserTests(); + } + + /** this condition can occur on the build machine only, and is way too complex to fix right now... */ + private boolean needToSkipPointcutParserTests() { + if (!LangUtil.is15VMOrGreater()) return false; + try { + Class.forName("org.aspectj.weaver.reflect.Java15ReflectionBasedReferenceTypeDelegate",false,this.getClass().getClassLoader());//ReflectionBasedReferenceTypeDelegate.class.getClassLoader()); + } catch (ClassNotFoundException cnfEx) { + return true; + } + return false; + } + + public void testParseWithoutHandler() { + if (needToSkip) return; + try { + PointcutParser + .getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution() + .parsePointcutExpression("bean(service.*"); + fail("should not be able to parse bean(service.*)"); + } catch(IllegalArgumentException ex) { + assertTrue("contains bean",ex.getMessage().indexOf("bean") != -1); + } + } + + public void testParseWithHandler() { + if (needToSkip) return; + PointcutParser parser = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution(); + BeanDesignatorHandler beanHandler = new BeanDesignatorHandler(); + parser.registerPointcutDesignatorHandler(beanHandler); + parser.parsePointcutExpression("bean(service.*)"); + assertEquals("service.*",beanHandler.getExpressionLastAskedToParse()); + } + + + /* + * Bug 205907 - the registered pointcut designator does not also get registered with the + * InternalUseOnlyPointcutParser inside the Java15ReflectionBasedReferenceTypeDelegate code. First test checks + * parsing is OK + */ + public void testParsingBeanInReferencePointcut01() throws Exception { + if (needToSkip) return; + PointcutParser parser = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution(); + BeanDesignatorHandler beanHandler = new BeanDesignatorHandler(); + parser.registerPointcutDesignatorHandler(beanHandler); + // The pointcut in CounterAspect look as follows: + // + // @Pointcut("execution(* setAge(..)) && bean(testBean1)") + // public void testBean1SetAge() { } + + // This should be found and resolved +// PointcutExpression pc = + parser.parsePointcutExpression("CounterAspect.testBean1SetAge()"); + + } + + /* + * Bug 205907 - the registered pointcut designator does not also get registered with the + * InternalUseOnlyPointcutParser inside the Java15ReflectionBasedReferenceTypeDelegate code. This test checks the + * actual matching. + */ + public void testParsingBeanInReferencePointcut02() throws Exception { + if (needToSkip) return; + PointcutParser parser = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution(); + BeanDesignatorHandler beanHandler = new BeanDesignatorHandler(); + parser.registerPointcutDesignatorHandler(beanHandler); + // The pointcut in CounterAspect look as follows: + // + // @Pointcut("execution(* toString(..)) && bean(testBean1)") + // public void testBean1toString() { } + + // This should be found and resolved + PointcutExpression pc = parser.parsePointcutExpression("CounterAspect.testBean1toString()"); + + DefaultMatchingContext context = new DefaultMatchingContext(); + context.addContextBinding("beanName", "testBean1"); + pc.setMatchingContext(context); + ShadowMatch sm = pc.matchesMethodExecution(Object.class.getMethod("toString", new Class[0])); + assertTrue(sm.alwaysMatches()); + + sm = pc.matchesMethodExecution(Object.class.getMethod("hashCode", new Class[0])); + assertTrue(sm.neverMatches()); + + context = new DefaultMatchingContext(); + context.addContextBinding("beanName", "testBean2"); + pc.setMatchingContext(context); + sm = pc.matchesMethodExecution(Object.class.getMethod("toString", new Class[0])); + assertTrue(sm.neverMatches()); + } + + public void testParseWithHandlerAndMultipleSegments() { + if (needToSkip) return; + PointcutParser parser = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution(); + BeanDesignatorHandler beanHandler = new BeanDesignatorHandler(); + parser.registerPointcutDesignatorHandler(beanHandler); + parser.parsePointcutExpression("bean(org.xyz.someapp..*)"); + assertEquals("org.xyz.someapp..*",beanHandler.getExpressionLastAskedToParse()); + } + + public void testStaticMatch() throws Exception { + if (needToSkip) return; + PointcutParser parser = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution(); + BeanDesignatorHandler beanHandler = new BeanDesignatorHandler(); + parser.registerPointcutDesignatorHandler(beanHandler); + PointcutExpression pc = parser.parsePointcutExpression("bean(myBean)"); + DefaultMatchingContext context = new DefaultMatchingContext(); + context.addContextBinding("beanName","myBean"); + pc.setMatchingContext(context); + ShadowMatch sm = pc.matchesMethodExecution(Object.class.getMethod("toString",new Class[0])); + assertTrue(sm.alwaysMatches()); + context.addContextBinding("beanName", "notMyBean"); + sm = pc.matchesMethodExecution(Object.class.getMethod("toString",new Class[0])); + assertTrue(sm.neverMatches()); + } + + public void testDynamicMatch() throws Exception { + if (needToSkip) return; + PointcutParser parser = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution(); + BeanDesignatorHandler beanHandler = new BeanDesignatorHandler(); + beanHandler.simulateDynamicTest = true; + parser.registerPointcutDesignatorHandler(beanHandler); + PointcutExpression pc = parser.parsePointcutExpression("bean(myBean)"); + ShadowMatch sm = pc.matchesMethodExecution(Object.class.getMethod("toString",new Class[0])); + DefaultMatchingContext context = new DefaultMatchingContext(); + assertTrue(sm.maybeMatches()); + assertFalse(sm.alwaysMatches()); + assertFalse(sm.neverMatches()); + context.addContextBinding("beanName","myBean"); + sm.setMatchingContext(context); + assertTrue(sm.matchesJoinPoint(null, null, null).matches()); + context.addContextBinding("beanName", "notMyBean"); + assertFalse(sm.matchesJoinPoint(null, null, null).matches()); + } + + public void testFastMatch() { + if (needToSkip) return; + PointcutParser parser = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution(); + BeanDesignatorHandler beanHandler = new BeanDesignatorHandler(); + parser.registerPointcutDesignatorHandler(beanHandler); + PointcutExpression pc = parser.parsePointcutExpression("bean(myBean)"); + DefaultMatchingContext context = new DefaultMatchingContext(); + context.addContextBinding("beanName","myBean"); + pc.setMatchingContext(context); + assertTrue(pc.couldMatchJoinPointsInType(String.class)); + context.addContextBinding("beanName","yourBean"); + assertFalse(pc.couldMatchJoinPointsInType(String.class)); + } + + private class BeanDesignatorHandler implements PointcutDesignatorHandler { + + private String askedToParse; + public boolean simulateDynamicTest = false; + + public String getDesignatorName() { + return "bean"; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.tools.PointcutDesignatorHandler#parse(java.lang.String) + */ + public ContextBasedMatcher parse(String expression) { + this.askedToParse = expression; + return new BeanPointcutExpression(expression,this.simulateDynamicTest); + } + + public String getExpressionLastAskedToParse() { + return this.askedToParse; + } + } + + private class BeanPointcutExpression implements ContextBasedMatcher { + + private final String beanNamePattern; + private final boolean simulateDynamicTest; + + public BeanPointcutExpression(String beanNamePattern, boolean simulateDynamicTest) { + this.beanNamePattern = beanNamePattern; + this.simulateDynamicTest = simulateDynamicTest; + } + + + public boolean couldMatchJoinPointsInType(Class aClass) { + return true; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.tools.ContextBasedMatcher#couldMatchJoinPointsInType(java.lang.Class) + */ + public boolean couldMatchJoinPointsInType(Class aClass, MatchingContext context) { + if (this.beanNamePattern.equals(context.getBinding("beanName"))) { + return true; + } else { + return false; + } + } + + + /* (non-Javadoc) + * @see org.aspectj.weaver.tools.ContextBasedMatcher#mayNeedDynamicTest() + */ + public boolean mayNeedDynamicTest() { + return this.simulateDynamicTest; + } + + + public FuzzyBoolean matchesStatically(MatchingContext matchContext) { + if (this.simulateDynamicTest) return FuzzyBoolean.MAYBE; + if (this.beanNamePattern.equals(matchContext.getBinding("beanName"))) { + return FuzzyBoolean.YES; + } else { + return FuzzyBoolean.NO; + } + } + + + /* (non-Javadoc) + * @see org.aspectj.weaver.tools.ContextBasedMatcher#matchesDynamically(org.aspectj.weaver.tools.MatchingContext) + */ + public boolean matchesDynamically(MatchingContext matchContext) { + return this.beanNamePattern.equals(matchContext.getBinding("beanName")); + } + } +} diff --git a/weaver/src/test/java/org/aspectj/weaver/tools/PointcutExpressionTest.java b/weaver/src/test/java/org/aspectj/weaver/tools/PointcutExpressionTest.java new file mode 100644 index 000000000..46189fd32 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/tools/PointcutExpressionTest.java @@ -0,0 +1,596 @@ +/******************************************************************************* + * Copyright (c) 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.aspectj.weaver.tools; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import junit.framework.TestCase; + +import org.aspectj.util.LangUtil; + +public class PointcutExpressionTest extends TestCase { + + PointcutParser p; + Constructor asCons; + Constructor bsCons; + Constructor bsStringCons; + Constructor clientCons; + Method a; + Method aa; + Method aaa; + Field x; + Field y; + Method b; + Method bsaa; + Field n; + Method foo; + Method bar; + + public void testMatchesMethodCall() { + PointcutExpression ex = p.parsePointcutExpression("call(* *..A.a*(..))"); + assertTrue("Should match call to A.a()", ex.matchesMethodCall(a, a).alwaysMatches()); + assertTrue("Should match call to A.aaa()", ex.matchesMethodCall(aaa, a).alwaysMatches()); + assertTrue("Should match call to B.aa()", ex.matchesMethodCall(bsaa, a).alwaysMatches()); + assertTrue("Should not match call to B.b()", ex.matchesMethodCall(b, a).neverMatches()); + ex = p.parsePointcutExpression("call(* *..A.a*(int))"); + assertTrue("Should match call to A.aa()", ex.matchesMethodCall(aa, a).alwaysMatches()); + assertTrue("Should not match call to A.a()", ex.matchesMethodCall(a, a).neverMatches()); + ex = p.parsePointcutExpression("call(void aaa(..)) && this(org.aspectj.weaver.tools.PointcutExpressionTest.Client)"); + assertTrue("Should match call to A.aaa() from Client", ex.matchesMethodCall(aaa, foo).alwaysMatches()); + ex = p.parsePointcutExpression("call(void aaa(..)) && this(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); + assertTrue("Should match call to A.aaa() from B", ex.matchesMethodCall(aaa, b).alwaysMatches()); + assertTrue("May match call to A.aaa() from A", ex.matchesMethodCall(aaa, a).maybeMatches()); + assertFalse("May match call to A.aaa() from A", ex.matchesMethodCall(aaa, a).alwaysMatches()); + ex = p.parsePointcutExpression("execution(* *.*(..))"); + assertTrue("Should not match call to A.aa", ex.matchesMethodCall(aa, a).neverMatches()); + // this + ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.Client)"); + assertTrue("Should match Client", ex.matchesMethodCall(a, foo).alwaysMatches()); + assertTrue("Should not match A", ex.matchesMethodCall(a, a).neverMatches()); + ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); + assertTrue("Should maybe match B", ex.matchesMethodCall(bsaa, a).maybeMatches()); + assertFalse("Should maybe match B", ex.matchesMethodCall(bsaa, a).alwaysMatches()); + // target + ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.Client)"); + assertTrue("Should not match Client", ex.matchesMethodCall(a, a).neverMatches()); + ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.A)"); + assertTrue("Should match A", ex.matchesMethodCall(a, a).alwaysMatches()); + ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); + assertTrue("Should maybe match A", ex.matchesMethodCall(aa, a).maybeMatches()); + assertFalse("Should maybe match A", ex.matchesMethodCall(aa, a).alwaysMatches()); + // test args + ex = p.parsePointcutExpression("args(..,int)"); + assertTrue("Should match A.aa", ex.matchesMethodCall(aa, a).alwaysMatches()); + assertTrue("Should match A.aaa", ex.matchesMethodCall(aaa, a).alwaysMatches()); + assertTrue("Should not match A.a", ex.matchesMethodCall(a, a).neverMatches()); + // within + ex = p.parsePointcutExpression("within(*..A)"); + assertTrue("Matches in class A", ex.matchesMethodCall(a, a).alwaysMatches()); + assertTrue("Does not match in class B", ex.matchesMethodCall(a, b).neverMatches()); + assertTrue("Matches in class A", ex.matchesMethodCall(a, A.class).alwaysMatches()); + assertTrue("Does not match in class B", ex.matchesMethodCall(a, B.class).neverMatches()); + // withincode + ex = p.parsePointcutExpression("withincode(* a*(..))"); + assertTrue("Should match", ex.matchesMethodCall(b, bsaa).alwaysMatches()); + assertTrue("Should not match", ex.matchesMethodCall(b, b).neverMatches()); + } + + public void testMatchesMethodExecution() { + PointcutExpression ex = p.parsePointcutExpression("execution(* *..A.aa(..))"); + assertTrue("Should match execution of A.aa", ex.matchesMethodExecution(aa).alwaysMatches()); + assertTrue("Should match execution of B.aa", ex.matchesMethodExecution(bsaa).alwaysMatches()); + assertTrue("Should not match execution of A.a", ex.matchesMethodExecution(a).neverMatches()); + ex = p.parsePointcutExpression("call(* *..A.a*(int))"); + assertTrue("Should not match execution of A.a", ex.matchesMethodExecution(a).neverMatches()); + + // test this + ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.A)"); + assertTrue("Should match A", ex.matchesMethodExecution(a).alwaysMatches()); + ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); + assertTrue("Maybe matches B", ex.matchesMethodExecution(a).maybeMatches()); + assertFalse("Maybe matches B", ex.matchesMethodExecution(a).alwaysMatches()); + + // test target + ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.A)"); + assertTrue("Should match A", ex.matchesMethodExecution(a).alwaysMatches()); + ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); + assertTrue("Maybe matches B", ex.matchesMethodExecution(a).maybeMatches()); + assertFalse("Maybe matches B", ex.matchesMethodExecution(a).alwaysMatches()); + + // test args + ex = p.parsePointcutExpression("args(..,int)"); + assertTrue("Should match A.aa", ex.matchesMethodExecution(aa).alwaysMatches()); + assertTrue("Should match A.aaa", ex.matchesMethodExecution(aaa).alwaysMatches()); + assertTrue("Should not match A.a", ex.matchesMethodExecution(a).neverMatches()); + + // within + ex = p.parsePointcutExpression("within(*..A)"); + assertTrue("Matches in class A", ex.matchesMethodExecution(a).alwaysMatches()); + assertTrue("Does not match in class B", ex.matchesMethodExecution(bsaa).neverMatches()); + + // withincode + ex = p.parsePointcutExpression("withincode(* a*(..))"); + assertTrue("Should not match", ex.matchesMethodExecution(a).neverMatches()); + } + + public void testMatchesConstructorCall() { + PointcutExpression ex = p.parsePointcutExpression("call(new(String))"); + assertTrue("Should match A(String)", ex.matchesConstructorCall(asCons, b).alwaysMatches()); + assertTrue("Should match B(String)", ex.matchesConstructorCall(bsStringCons, b).alwaysMatches()); + assertTrue("Should not match B()", ex.matchesConstructorCall(bsCons, foo).neverMatches()); + ex = p.parsePointcutExpression("call(*..A.new(String))"); + assertTrue("Should match A(String)", ex.matchesConstructorCall(asCons, b).alwaysMatches()); + assertTrue("Should not match B(String)", ex.matchesConstructorCall(bsStringCons, foo).neverMatches()); + // this + ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.Client)"); + assertTrue("Should match Client", ex.matchesConstructorCall(asCons, foo).alwaysMatches()); + assertTrue("Should not match A", ex.matchesConstructorCall(asCons, a).neverMatches()); + ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); + assertTrue("Should maybe match B", ex.matchesConstructorCall(asCons, a).maybeMatches()); + assertFalse("Should maybe match B", ex.matchesConstructorCall(asCons, a).alwaysMatches()); + // target + ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.Client)"); + assertTrue("Should not match Client", ex.matchesConstructorCall(asCons, foo).neverMatches()); + ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.A)"); + assertTrue("Should not match A (no target)", ex.matchesConstructorCall(asCons, a).neverMatches()); + // args + ex = p.parsePointcutExpression("args(String)"); + assertTrue("Should match A(String)", ex.matchesConstructorCall(asCons, b).alwaysMatches()); + assertTrue("Should match B(String)", ex.matchesConstructorCall(bsStringCons, foo).alwaysMatches()); + assertTrue("Should not match B()", ex.matchesConstructorCall(bsCons, foo).neverMatches()); + // within + ex = p.parsePointcutExpression("within(*..A)"); + assertTrue("Matches in class A", ex.matchesConstructorCall(asCons, a).alwaysMatches()); + assertTrue("Does not match in class B", ex.matchesConstructorCall(asCons, b).neverMatches()); + // withincode + ex = p.parsePointcutExpression("withincode(* a*(..))"); + assertTrue("Should match", ex.matchesConstructorCall(bsCons, aa).alwaysMatches()); + assertTrue("Should not match", ex.matchesConstructorCall(bsCons, b).neverMatches()); + } + + public void testMatchesConstructorExecution() { + PointcutExpression ex = p.parsePointcutExpression("execution(new(String))"); + assertTrue("Should match A(String)", ex.matchesConstructorExecution(asCons).alwaysMatches()); + assertTrue("Should match B(String)", ex.matchesConstructorExecution(bsStringCons).alwaysMatches()); + assertTrue("Should not match B()", ex.matchesConstructorExecution(bsCons).neverMatches()); + ex = p.parsePointcutExpression("execution(*..A.new(String))"); + assertTrue("Should match A(String)", ex.matchesConstructorExecution(asCons).alwaysMatches()); + assertTrue("Should not match B(String)", ex.matchesConstructorExecution(bsStringCons).neverMatches()); + + // test this + ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.A)"); + assertTrue("Should match A", ex.matchesConstructorExecution(asCons).alwaysMatches()); + ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); + assertTrue("Maybe matches B", ex.matchesConstructorExecution(asCons).maybeMatches()); + assertFalse("Maybe matches B", ex.matchesConstructorExecution(asCons).alwaysMatches()); + assertTrue("Should match B", ex.matchesConstructorExecution(bsCons).alwaysMatches()); + assertTrue("Does not match client", ex.matchesConstructorExecution(clientCons).neverMatches()); + + // test target + ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.A)"); + assertTrue("Should match A", ex.matchesConstructorExecution(asCons).alwaysMatches()); + ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); + assertTrue("Maybe matches B", ex.matchesConstructorExecution(asCons).maybeMatches()); + assertFalse("Maybe matches B", ex.matchesConstructorExecution(asCons).alwaysMatches()); + assertTrue("Should match B", ex.matchesConstructorExecution(bsCons).alwaysMatches()); + assertTrue("Does not match client", ex.matchesConstructorExecution(clientCons).neverMatches()); + + // within + ex = p.parsePointcutExpression("within(*..A)"); + assertTrue("Matches in class A", ex.matchesConstructorExecution(asCons).alwaysMatches()); + assertTrue("Does not match in class B", ex.matchesConstructorExecution(bsCons).neverMatches()); + + // withincode + ex = p.parsePointcutExpression("withincode(* a*(..))"); + assertTrue("Does not match", ex.matchesConstructorExecution(bsCons).neverMatches()); + + // args + ex = p.parsePointcutExpression("args(String)"); + assertTrue("Should match A(String)", ex.matchesConstructorExecution(asCons).alwaysMatches()); + assertTrue("Should match B(String)", ex.matchesConstructorExecution(bsStringCons).alwaysMatches()); + assertTrue("Should not match B()", ex.matchesConstructorExecution(bsCons).neverMatches()); + } + + public void testMatchesAdviceExecution() { + PointcutExpression ex = p.parsePointcutExpression("adviceexecution()"); + assertTrue("Should match (advice) A.a", ex.matchesAdviceExecution(a).alwaysMatches()); + // test this + ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.Client)"); + assertTrue("Should match Client", ex.matchesAdviceExecution(foo).alwaysMatches()); + ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); + assertTrue("Maybe matches B", ex.matchesAdviceExecution(a).maybeMatches()); + assertFalse("Maybe matches B", ex.matchesAdviceExecution(a).alwaysMatches()); + assertTrue("Does not match client", ex.matchesAdviceExecution(foo).neverMatches()); + + // test target + ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.Client)"); + assertTrue("Should match Client", ex.matchesAdviceExecution(foo).alwaysMatches()); + ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); + assertTrue("Maybe matches B", ex.matchesAdviceExecution(a).maybeMatches()); + assertFalse("Maybe matches B", ex.matchesAdviceExecution(a).alwaysMatches()); + assertTrue("Does not match client", ex.matchesAdviceExecution(foo).neverMatches()); + + // test within + ex = p.parsePointcutExpression("within(*..A)"); + assertTrue("Matches in class A", ex.matchesAdviceExecution(a).alwaysMatches()); + assertTrue("Does not match in class B", ex.matchesAdviceExecution(b).neverMatches()); + + // withincode + ex = p.parsePointcutExpression("withincode(* a*(..))"); + assertTrue("Does not match", ex.matchesAdviceExecution(a).neverMatches()); + + // test args + ex = p.parsePointcutExpression("args(..,int)"); + assertTrue("Should match A.aa", ex.matchesAdviceExecution(aa).alwaysMatches()); + assertTrue("Should match A.aaa", ex.matchesAdviceExecution(aaa).alwaysMatches()); + assertTrue("Should not match A.a", ex.matchesAdviceExecution(a).neverMatches()); + } + + public void testMatchesHandler() { + PointcutExpression ex = p.parsePointcutExpression("handler(Exception)"); + assertTrue("Should match catch(Exception)", ex.matchesHandler(Exception.class, Client.class).alwaysMatches()); + assertTrue("Should not match catch(Throwable)", ex.matchesHandler(Throwable.class, Client.class).neverMatches()); + // test this + ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.Client)"); + assertTrue("Should match Client", ex.matchesHandler(Exception.class, foo).alwaysMatches()); + ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); + assertTrue("Maybe matches B", ex.matchesHandler(Exception.class, a).maybeMatches()); + assertFalse("Maybe matches B", ex.matchesHandler(Exception.class, a).alwaysMatches()); + assertTrue("Does not match client", ex.matchesHandler(Exception.class, foo).neverMatches()); + // target - no target for exception handlers + ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.Client)"); + assertTrue("Should match Client", ex.matchesHandler(Exception.class, foo).neverMatches()); + // args + ex = p.parsePointcutExpression("args(Exception)"); + assertTrue("Should match Exception", ex.matchesHandler(Exception.class, foo).alwaysMatches()); + assertTrue("Should match RuntimeException", ex.matchesHandler(RuntimeException.class, foo).alwaysMatches()); + assertTrue("Should not match String", ex.matchesHandler(String.class, foo).neverMatches()); + assertTrue("Maybe matches Throwable", ex.matchesHandler(Throwable.class, foo).maybeMatches()); + assertFalse("Maybe matches Throwable", ex.matchesHandler(Throwable.class, foo).alwaysMatches()); + // within + ex = p.parsePointcutExpression("within(*..Client)"); + assertTrue("Matches in class Client", ex.matchesHandler(Exception.class, foo).alwaysMatches()); + assertTrue("Does not match in class B", ex.matchesHandler(Exception.class, b).neverMatches()); + // withincode + ex = p.parsePointcutExpression("withincode(* a*(..))"); + assertTrue("Matches within aa", ex.matchesHandler(Exception.class, aa).alwaysMatches()); + assertTrue("Does not match within b", ex.matchesHandler(Exception.class, b).neverMatches()); + } + + public void testMatchesInitialization() { + PointcutExpression ex = p.parsePointcutExpression("initialization(new(String))"); + assertTrue("Should match A(String)", ex.matchesInitialization(asCons).alwaysMatches()); + assertTrue("Should match B(String)", ex.matchesInitialization(bsStringCons).alwaysMatches()); + assertTrue("Should not match B()", ex.matchesInitialization(bsCons).neverMatches()); + ex = p.parsePointcutExpression("initialization(*..A.new(String))"); + assertTrue("Should match A(String)", ex.matchesInitialization(asCons).alwaysMatches()); + assertTrue("Should not match B(String)", ex.matchesInitialization(bsStringCons).neverMatches()); + // test this + ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.A)"); + assertTrue("Should match A", ex.matchesInitialization(asCons).alwaysMatches()); + ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); + assertTrue("Maybe matches B", ex.matchesInitialization(asCons).maybeMatches()); + assertFalse("Maybe matches B", ex.matchesInitialization(asCons).alwaysMatches()); + + // test target + ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.A)"); + assertTrue("Should match A", ex.matchesInitialization(asCons).alwaysMatches()); + ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); + assertTrue("Maybe matches B", ex.matchesInitialization(asCons).maybeMatches()); + assertFalse("Maybe matches B", ex.matchesInitialization(asCons).alwaysMatches()); + // within + ex = p.parsePointcutExpression("within(*..A)"); + assertTrue("Matches in class A", ex.matchesInitialization(asCons).alwaysMatches()); + assertTrue("Does not match in class B", ex.matchesInitialization(bsCons).neverMatches()); + // withincode + ex = p.parsePointcutExpression("withincode(* a*(..))"); + assertTrue("Does not match", ex.matchesInitialization(bsCons).neverMatches()); + // args + ex = p.parsePointcutExpression("args(String)"); + assertTrue("Should match A(String)", ex.matchesInitialization(asCons).alwaysMatches()); + assertTrue("Should match B(String)", ex.matchesInitialization(bsStringCons).alwaysMatches()); + assertTrue("Should not match B()", ex.matchesInitialization(bsCons).neverMatches()); + } + + public void testMatchesPreInitialization() { + PointcutExpression ex = p.parsePointcutExpression("preinitialization(new(String))"); + assertTrue("Should match A(String)", ex.matchesPreInitialization(asCons).alwaysMatches()); + assertTrue("Should match B(String)", ex.matchesPreInitialization(bsStringCons).alwaysMatches()); + assertTrue("Should not match B()", ex.matchesPreInitialization(bsCons).neverMatches()); + ex = p.parsePointcutExpression("preinitialization(*..A.new(String))"); + assertTrue("Should match A(String)", ex.matchesPreInitialization(asCons).alwaysMatches()); + assertTrue("Should not match B(String)", ex.matchesPreInitialization(bsStringCons).neverMatches()); + // test this + ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.A)"); + assertTrue("No match, no this at preinit", ex.matchesPreInitialization(asCons).neverMatches()); + + // test target + ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.A)"); + assertTrue("No match, no target at preinit", ex.matchesPreInitialization(asCons).neverMatches()); + + // within + ex = p.parsePointcutExpression("within(*..A)"); + assertTrue("Matches in class A", ex.matchesPreInitialization(asCons).alwaysMatches()); + assertTrue("Does not match in class B", ex.matchesPreInitialization(bsCons).neverMatches()); + // withincode + ex = p.parsePointcutExpression("withincode(* a*(..))"); + assertTrue("Does not match", ex.matchesPreInitialization(bsCons).neverMatches()); + // args + ex = p.parsePointcutExpression("args(String)"); + assertTrue("Should match A(String)", ex.matchesPreInitialization(asCons).alwaysMatches()); + assertTrue("Should match B(String)", ex.matchesPreInitialization(bsStringCons).alwaysMatches()); + assertTrue("Should not match B()", ex.matchesPreInitialization(bsCons).neverMatches()); + } + + public void testMatchesStaticInitialization() { + // staticinit + PointcutExpression ex = p.parsePointcutExpression("staticinitialization(*..A+)"); + assertTrue("Matches A", ex.matchesStaticInitialization(A.class).alwaysMatches()); + assertTrue("Matches B", ex.matchesStaticInitialization(B.class).alwaysMatches()); + assertTrue("Doesn't match Client", ex.matchesStaticInitialization(Client.class).neverMatches()); + // this + ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.A)"); + assertTrue("No this", ex.matchesStaticInitialization(A.class).neverMatches()); + // target + ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.A)"); + assertTrue("No target", ex.matchesStaticInitialization(A.class).neverMatches()); + + // args + ex = p.parsePointcutExpression("args()"); + assertTrue("No args", ex.matchesStaticInitialization(A.class).alwaysMatches()); + ex = p.parsePointcutExpression("args(String)"); + assertTrue("No args", ex.matchesStaticInitialization(A.class).neverMatches()); + + // within + ex = p.parsePointcutExpression("within(*..A)"); + assertTrue("Matches in class A", ex.matchesStaticInitialization(A.class).alwaysMatches()); + assertTrue("Does not match in class B", ex.matchesStaticInitialization(B.class).neverMatches()); + + // withincode + ex = p.parsePointcutExpression("withincode(* a*(..))"); + assertTrue("Does not match", ex.matchesStaticInitialization(A.class).neverMatches()); + } + + public void testMatchesFieldSet() { + PointcutExpression ex = p.parsePointcutExpression("set(* *..A+.*)"); + assertTrue("matches x", ex.matchesFieldSet(x, a).alwaysMatches()); + assertTrue("matches y", ex.matchesFieldSet(y, foo).alwaysMatches()); + assertTrue("does not match n", ex.matchesFieldSet(n, foo).neverMatches()); + // this + ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.Client)"); + assertTrue("matches Client", ex.matchesFieldSet(x, foo).alwaysMatches()); + assertTrue("does not match A", ex.matchesFieldSet(n, a).neverMatches()); + ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); + assertTrue("maybe matches A", ex.matchesFieldSet(x, a).maybeMatches()); + assertFalse("maybe matches A", ex.matchesFieldSet(x, a).alwaysMatches()); + // target + ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); + assertTrue("matches B", ex.matchesFieldSet(y, foo).alwaysMatches()); + assertTrue("maybe matches A", ex.matchesFieldSet(x, foo).maybeMatches()); + assertFalse("maybe matches A", ex.matchesFieldSet(x, foo).alwaysMatches()); + // args + ex = p.parsePointcutExpression("args(int)"); + assertTrue("matches x", ex.matchesFieldSet(x, a).alwaysMatches()); + assertTrue("matches y", ex.matchesFieldSet(y, a).alwaysMatches()); + assertTrue("does not match n", ex.matchesFieldSet(n, a).neverMatches()); + // within + ex = p.parsePointcutExpression("within(*..A)"); + assertTrue("Matches in class A", ex.matchesFieldSet(x, a).alwaysMatches()); + assertTrue("Does not match in class B", ex.matchesFieldSet(x, b).neverMatches()); + // withincode + ex = p.parsePointcutExpression("withincode(* a*(..))"); + assertTrue("Should match", ex.matchesFieldSet(x, aa).alwaysMatches()); + assertTrue("Should not match", ex.matchesFieldSet(x, b).neverMatches()); + } + + public void testMatchesFieldGet() { + PointcutExpression ex = p.parsePointcutExpression("get(* *..A+.*)"); + assertTrue("matches x", ex.matchesFieldGet(x, a).alwaysMatches()); + assertTrue("matches y", ex.matchesFieldGet(y, foo).alwaysMatches()); + assertTrue("does not match n", ex.matchesFieldGet(n, foo).neverMatches()); + // this + ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.Client)"); + assertTrue("matches Client", ex.matchesFieldGet(x, foo).alwaysMatches()); + assertTrue("does not match A", ex.matchesFieldGet(n, a).neverMatches()); + ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); + assertTrue("maybe matches A", ex.matchesFieldGet(x, a).maybeMatches()); + assertFalse("maybe matches A", ex.matchesFieldGet(x, a).alwaysMatches()); + // target + ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); + assertTrue("matches B", ex.matchesFieldGet(y, foo).alwaysMatches()); + assertTrue("maybe matches A", ex.matchesFieldGet(x, foo).maybeMatches()); + assertFalse("maybe matches A", ex.matchesFieldGet(x, foo).alwaysMatches()); + // args - no args at get join point + ex = p.parsePointcutExpression("args(int)"); + assertTrue("matches x", ex.matchesFieldGet(x, a).neverMatches()); + // within + ex = p.parsePointcutExpression("within(*..A)"); + assertTrue("Matches in class A", ex.matchesFieldGet(x, a).alwaysMatches()); + assertTrue("Does not match in class B", ex.matchesFieldGet(x, b).neverMatches()); + // withincode + ex = p.parsePointcutExpression("withincode(* a*(..))"); + assertTrue("Should match", ex.matchesFieldGet(x, aa).alwaysMatches()); + assertTrue("Should not match", ex.matchesFieldGet(x, b).neverMatches()); + } + + public void testArgsMatching() { + // too few args + PointcutExpression ex = p.parsePointcutExpression("args(*,*,*,*)"); + assertTrue("Too few args", ex.matchesMethodExecution(foo).neverMatches()); + assertTrue("Matching #args", ex.matchesMethodExecution(bar).alwaysMatches()); + // one too few + ellipsis + ex = p.parsePointcutExpression("args(*,*,*,..)"); + assertTrue("Matches with ellipsis", ex.matchesMethodExecution(foo).alwaysMatches()); + // exact number + ellipsis + assertTrue("Matches with ellipsis", ex.matchesMethodExecution(bar).alwaysMatches()); + assertTrue("Does not match with ellipsis", ex.matchesMethodExecution(a).neverMatches()); + // too many + ellipsis + ex = p.parsePointcutExpression("args(*,..,*)"); + assertTrue("Matches with ellipsis", ex.matchesMethodExecution(bar).alwaysMatches()); + assertTrue("Does not match with ellipsis", ex.matchesMethodExecution(a).neverMatches()); + assertTrue("Matches with ellipsis", ex.matchesMethodExecution(aaa).alwaysMatches()); + // exact match + ex = p.parsePointcutExpression("args(String,int,Number)"); + assertTrue("Matches exactly", ex.matchesMethodExecution(foo).alwaysMatches()); + // maybe match + ex = p.parsePointcutExpression("args(String,int,Double)"); + assertTrue("Matches maybe", ex.matchesMethodExecution(foo).maybeMatches()); + assertFalse("Matches maybe", ex.matchesMethodExecution(foo).alwaysMatches()); + // never match + ex = p.parsePointcutExpression("args(String,Integer,Number)"); + if (LangUtil.is15VMOrGreater()) { + assertTrue("matches", ex.matchesMethodExecution(foo).alwaysMatches()); + } else { + assertTrue("Does not match", ex.matchesMethodExecution(foo).neverMatches()); + } + } + + // public void testMatchesDynamically() { + // // everything other than this,target,args should just return true + // PointcutExpression ex = p.parsePointcutExpression("call(* *.*(..)) && execution(* *.*(..)) &&" + + // "get(* *) && set(* *) && initialization(new(..)) && preinitialization(new(..)) &&" + + // "staticinitialization(X) && adviceexecution() && within(Y) && withincode(* *.*(..)))"); + // assertTrue("Matches dynamically",ex.matchesDynamically(a,b,new Object[0])); + // // this + // ex = p.parsePointcutExpression("this(String)"); + // assertTrue("String matches",ex.matchesDynamically("",this,new Object[0])); + // assertFalse("Object doesn't match",ex.matchesDynamically(new Object(),this,new Object[0])); + // ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.A)"); + // assertTrue("A matches",ex.matchesDynamically(new A(""),this,new Object[0])); + // assertTrue("B matches",ex.matchesDynamically(new B(""),this,new Object[0])); + // // target + // ex = p.parsePointcutExpression("target(String)"); + // assertTrue("String matches",ex.matchesDynamically(this,"",new Object[0])); + // assertFalse("Object doesn't match",ex.matchesDynamically(this,new Object(),new Object[0])); + // ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.A)"); + // assertTrue("A matches",ex.matchesDynamically(this,new A(""),new Object[0])); + // assertTrue("B matches",ex.matchesDynamically(this,new B(""),new Object[0])); + // // args + // ex = p.parsePointcutExpression("args(*,*,*,*)"); + // assertFalse("Too few args",ex.matchesDynamically(null,null,new Object[]{a,b})); + // assertTrue("Matching #args",ex.matchesDynamically(null,null,new Object[]{a,b,aa,aaa})); + // // one too few + ellipsis + // ex = p.parsePointcutExpression("args(*,*,*,..)"); + // assertTrue("Matches with ellipsis",ex.matchesDynamically(null,null,new Object[]{a,b,aa,aaa})); + // // exact number + ellipsis + // assertTrue("Matches with ellipsis",ex.matchesDynamically(null,null,new Object[]{a,b,aa})); + // assertFalse("Does not match with ellipsis",ex.matchesDynamically(null,null,new Object[]{a,b})); + // // too many + ellipsis + // ex = p.parsePointcutExpression("args(*,..,*)"); + // assertTrue("Matches with ellipsis",ex.matchesDynamically(null,null,new Object[]{a,b,aa,aaa})); + // assertFalse("Does not match with ellipsis",ex.matchesDynamically(null,null,new Object[]{a})); + // assertTrue("Matches with ellipsis",ex.matchesDynamically(null,null,new Object[]{a,b})); + // // exact match + // ex = p.parsePointcutExpression("args(String,int,Number)"); + // assertTrue("Matches exactly",ex.matchesDynamically(null,null,new Object[]{"",new Integer(5),new Double(5.0)})); + // ex = p.parsePointcutExpression("args(String,Integer,Number)"); + // assertTrue("Matches exactly",ex.matchesDynamically(null,null,new Object[]{"",new Integer(5),new Double(5.0)})); + // // never match + // ex = p.parsePointcutExpression("args(String,Integer,Number)"); + // assertFalse("Does not match",ex.matchesDynamically(null,null,new Object[]{a,b,aa})); + // } + + public void testGetPointcutExpression() { + PointcutExpression ex = p.parsePointcutExpression("staticinitialization(*..A+)"); + assertEquals("staticinitialization(*..A+)", ex.getPointcutExpression()); + } + + public void testCouldMatchJoinPointsInType() { + PointcutExpression ex = p.parsePointcutExpression("execution(* org.aspectj.weaver.tools.PointcutExpressionTest.B.*(..))"); + assertFalse("Could maybe match String (as best we know at this point)", ex.couldMatchJoinPointsInType(String.class)); + assertTrue("Will always match B", ex.couldMatchJoinPointsInType(B.class)); + ex = p.parsePointcutExpression("within(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); + assertFalse("Will never match String", ex.couldMatchJoinPointsInType(String.class)); + assertTrue("Will always match B", ex.couldMatchJoinPointsInType(B.class)); + } + + public void testMayNeedDynamicTest() { + PointcutExpression ex = p.parsePointcutExpression("execution(* org.aspectj.weaver.tools.PointcutExpressionTest.B.*(..))"); + assertFalse("No dynamic test needed", ex.mayNeedDynamicTest()); + ex = p + .parsePointcutExpression("execution(* org.aspectj.weaver.tools.PointcutExpressionTest.B.*(..)) && args(org.aspectj.weaver.tools.PointcutExpressionTest.X)"); + assertTrue("Dynamic test needed", ex.mayNeedDynamicTest()); + } + + protected void setUp() throws Exception { + super.setUp(); + p = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingSpecifiedClassloaderForResolution(this.getClass() + .getClassLoader()); + asCons = A.class.getConstructor(new Class[] { String.class }); + bsCons = B.class.getConstructor(new Class[0]); + bsStringCons = B.class.getConstructor(new Class[] { String.class }); + a = A.class.getMethod("a", new Class[0]); + aa = A.class.getMethod("aa", new Class[] { int.class }); + aaa = A.class.getMethod("aaa", new Class[] { String.class, int.class }); + x = A.class.getDeclaredField("x"); + y = B.class.getDeclaredField("y"); + b = B.class.getMethod("b", new Class[0]); + bsaa = B.class.getMethod("aa", new Class[] { int.class }); + clientCons = Client.class.getConstructor(new Class[0]); + n = Client.class.getDeclaredField("n"); + foo = Client.class.getDeclaredMethod("foo", new Class[] { String.class, int.class, Number.class }); + bar = Client.class.getDeclaredMethod("bar", new Class[] { String.class, int.class, Integer.class, Number.class }); + } + + static class A { + public A(String s) { + } + + public void a() { + } + + public void aa(int i) { + } + + public void aaa(String s, int i) { + } + + int x; + } + + static class B extends A { + public B() { + super(""); + } + + public B(String s) { + super(s); + } + + public String b() { + return null; + } + + public void aa(int i) { + } + + int y; + } + + static class Client { + public Client() { + } + + Number n; + + public void foo(String s, int i, Number n) { + } + + public void bar(String s, int i, Integer i2, Number n) { + } + } + + static class X { + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/tools/PointcutParserTest.java b/weaver/src/test/java/org/aspectj/weaver/tools/PointcutParserTest.java new file mode 100644 index 000000000..4654b049d --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/tools/PointcutParserTest.java @@ -0,0 +1,391 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM Corporation. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * ******************************************************************/ +package org.aspectj.weaver.tools; + +import java.util.HashSet; +import java.util.Properties; +import java.util.Set; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import org.aspectj.bridge.AbortException; +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.IMessage.Kind; +import org.aspectj.bridge.IMessageHandler; +import org.aspectj.util.LangUtil; +import org.aspectj.weaver.patterns.PatternParser; +import org.aspectj.weaver.patterns.Pointcut; +import org.aspectj.weaver.patterns.PointcutRewriter; + +/** + * Test cases for the PointcutParser class + */ +public class PointcutParserTest extends TestCase { + + private boolean needToSkip = false; + + /** this condition can occur on the build machine only, and is way too complex to fix right now... */ + private boolean needToSkipPointcutParserTests() { + if (!LangUtil.is15VMOrGreater()) { + return false; + } + try { + Class.forName("org.aspectj.weaver.reflect.Java15ReflectionBasedReferenceTypeDelegate", false, this.getClass() + .getClassLoader());// ReflectionBasedReferenceTypeDelegate.class.getClassLoader()); + } catch (ClassNotFoundException cnfEx) { + return true; + } + return false; + } + + protected void setUp() throws Exception { + super.setUp(); + needToSkip = needToSkipPointcutParserTests(); + } + + public void testGetAllSupportedPointcutPrimitives() { + if (needToSkip) { + return; + } + + Set s = PointcutParser.getAllSupportedPointcutPrimitives(); + assertEquals("Should be 21 elements in the set", 21, s.size()); + assertFalse("Should not contain if pcd", s.contains(PointcutPrimitive.IF)); + assertFalse("Should not contain cflow pcd", s.contains(PointcutPrimitive.CFLOW)); + assertFalse("Should not contain cflowbelow pcd", s.contains(PointcutPrimitive.CFLOW_BELOW)); + } + + public void testEmptyConstructor() { + if (needToSkip) { + return; + } + + PointcutParser parser = PointcutParser + .getPointcutParserSupportingAllPrimitivesAndUsingSpecifiedClassloaderForResolution(this.getClass().getClassLoader()); + Set s = parser.getSupportedPrimitives(); + assertEquals("Should be 21 elements in the set", 21, s.size()); + assertFalse("Should not contain if pcd", s.contains(PointcutPrimitive.IF)); + assertFalse("Should not contain cflow pcd", s.contains(PointcutPrimitive.CFLOW)); + assertFalse("Should not contain cflowbelow pcd", s.contains(PointcutPrimitive.CFLOW_BELOW)); + } + + public void testSetConstructor() { + if (needToSkip) { + return; + } + + Set p = PointcutParser.getAllSupportedPointcutPrimitives(); + PointcutParser parser = PointcutParser + .getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(p, this.getClass() + .getClassLoader()); + assertEquals("Should use the set we pass in", p, parser.getSupportedPrimitives()); + Set q = new HashSet(); + q.add(PointcutPrimitive.ARGS); + parser = PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(q, this + .getClass().getClassLoader()); + assertEquals("Should have only one element in set", 1, parser.getSupportedPrimitives().size()); + assertEquals("Should only have ARGS pcd", PointcutPrimitive.ARGS, parser.getSupportedPrimitives().iterator().next()); + } + + public void testParsePointcutExpression() { + if (needToSkip) { + return; + } + + PointcutParser p = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingSpecifiedClassloaderForResolution(this + .getClass().getClassLoader()); + IMessageHandler current = p.setCustomMessageHandler(new IgnoreWarningsMessageHandler()); + try { + p.parsePointcutExpression("(adviceexecution() || execution(* *.*(..)) || handler(Exception) || " + + "call(Foo Bar+.*(Goo)) || get(* foo) || set(Foo+ (Goo||Moo).s*) || " + + "initialization(Foo.new(..)) || preinitialization(*.new(Foo,..)) || " + + "staticinitialization(org.xzy.abc..*)) && (this(Foo) || target(Boo) ||" + "args(A,B,C)) && !handler(X)"); + } finally { + p.setCustomMessageHandler(current); + } + try { + p.parsePointcutExpression("gobble-de-gook()"); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + } + } + + public void testParseExceptionErrorMessages() { + if (needToSkip) { + return; + } + + PointcutParser p = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingSpecifiedClassloaderForResolution(this + .getClass().getClassLoader()); + try { + p.parsePointcutExpression("execution(int Foo.*(..) && args(Double)"); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + assertTrue("Pointcut is not well-formed message", + ex.getMessage().startsWith("Pointcut is not well-formed: expecting ')' at character position 24")); + } + } + + public void testOperatorPrecedence_319190() throws Exception { + if (needToSkip) { + return; + } + + String s = null; + Pointcut p = null; + + s = "(execution(* A.credit(float)) || execution(* A.debit(float))) && this(acc) && args(am) || execution(* C.*(Account, float)) && args(acc, am)"; + p = new PatternParser(s).parsePointcut(); + Assert.assertEquals( + "(((execution(* A.credit(float)) || execution(* A.debit(float))) && (this(acc) && args(am))) || (execution(* C.*(Account, float)) && args(acc, am)))", + p.toString()); + + s = "(if(true) || if(false)) && this(acc) && args(am) || if(true) && args(acc, am)"; + p = new PatternParser(s).parsePointcut(); + // bugged was: ((if(true) || if(false)) && (this(acc) && (args(am) || (if(true) && args(acc, am))))) + Assert.assertEquals("(((if(true) || if(false)) && (this(acc) && args(am))) || (if(true) && args(acc, am)))", p.toString()); + p = new PointcutRewriter().rewrite(p); + Assert.assertEquals("(((this(acc) && args(am)) && if(true)) || (args(acc, am) && if(true)))", p.toString()); + + s = "if(true) && if(false) || if(true)"; + p = new PatternParser(s).parsePointcut(); + assertEquals("((if(true) && if(false)) || if(true))", p.toString()); + p = new PointcutRewriter().rewrite(p); + Assert.assertEquals("if(true)", p.toString()); + } + + public void testParseIfPCD() { + if (needToSkip) { + return; + } + + PointcutParser p = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingSpecifiedClassloaderForResolution(this + .getClass().getClassLoader()); + try { + p.parsePointcutExpression("if(true)"); + fail("Expected UnsupportedPointcutPrimitiveException"); + } catch (UnsupportedPointcutPrimitiveException ex) { + assertEquals("Should not support IF", PointcutPrimitive.IF, ex.getUnsupportedPrimitive()); + } + } + + public void testParseCflowPCDs() { + if (needToSkip) { + return; + } + + PointcutParser p = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingSpecifiedClassloaderForResolution(this + .getClass().getClassLoader()); + try { + p.parsePointcutExpression("cflow(this(t))"); + fail("Expected UnsupportedPointcutPrimitiveException"); + } catch (UnsupportedPointcutPrimitiveException ex) { + assertEquals("Should not support CFLOW", PointcutPrimitive.CFLOW, ex.getUnsupportedPrimitive()); + } + try { + p.parsePointcutExpression("cflowbelow(this(t))"); + fail("Expected UnsupportedPointcutPrimitiveException"); + } catch (UnsupportedPointcutPrimitiveException ex) { + assertEquals("Should not support CFLOW_BELOW", PointcutPrimitive.CFLOW_BELOW, ex.getUnsupportedPrimitive()); + } + } + + public void testParseReferencePCDs() { + if (needToSkip) { + return; + } + + Set pcKinds = PointcutParser.getAllSupportedPointcutPrimitives(); + pcKinds.remove(PointcutPrimitive.REFERENCE); + PointcutParser p = PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution( + pcKinds, this.getClass().getClassLoader()); + try { + p.parsePointcutExpression("bananas(String)"); + fail("Expected UnsupportedPointcutPrimitiveException"); + } catch (UnsupportedPointcutPrimitiveException ex) { + assertTrue(ex.getUnsupportedPrimitive() == PointcutPrimitive.REFERENCE); + } + } + + public void testParseUnsupportedPCDs() { + if (needToSkip) { + return; + } + + Set s = new HashSet(); + PointcutParser p = PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution( + s, this.getClass().getClassLoader()); + try { + p.parsePointcutExpression("args(x)"); + fail("Expected UnsupportedPointcutPrimitiveException"); + } catch (UnsupportedPointcutPrimitiveException ex) { + assertEquals("Args", PointcutPrimitive.ARGS, ex.getUnsupportedPrimitive()); + } + try { + p.parsePointcutExpression("within(x)"); + fail("Expected UnsupportedPointcutPrimitiveException"); + } catch (UnsupportedPointcutPrimitiveException ex) { + assertEquals("Within", PointcutPrimitive.WITHIN, ex.getUnsupportedPrimitive()); + } + try { + p.parsePointcutExpression("withincode(new(..))"); + fail("Expected UnsupportedPointcutPrimitiveException"); + } catch (UnsupportedPointcutPrimitiveException ex) { + assertEquals("Withincode", PointcutPrimitive.WITHIN_CODE, ex.getUnsupportedPrimitive()); + } + try { + p.parsePointcutExpression("handler(Exception)"); + fail("Expected UnsupportedPointcutPrimitiveException"); + } catch (UnsupportedPointcutPrimitiveException ex) { + assertEquals("handler", PointcutPrimitive.HANDLER, ex.getUnsupportedPrimitive()); + } + try { + p.parsePointcutExpression("this(X)"); + fail("Expected UnsupportedPointcutPrimitiveException"); + } catch (UnsupportedPointcutPrimitiveException ex) { + assertEquals("this", PointcutPrimitive.THIS, ex.getUnsupportedPrimitive()); + } + try { + p.parsePointcutExpression("target(X)"); + fail("Expected UnsupportedPointcutPrimitiveException"); + } catch (UnsupportedPointcutPrimitiveException ex) { + assertEquals("target", PointcutPrimitive.TARGET, ex.getUnsupportedPrimitive()); + } + try { + p.parsePointcutExpression("this(X) && target(Y)"); + fail("Expected UnsupportedPointcutPrimitiveException"); + } catch (UnsupportedPointcutPrimitiveException ex) { + assertEquals("This", PointcutPrimitive.THIS, ex.getUnsupportedPrimitive()); + } + try { + p.parsePointcutExpression("this(X) || target(Y)"); + fail("Expected UnsupportedPointcutPrimitiveException"); + } catch (UnsupportedPointcutPrimitiveException ex) { + assertEquals("This", PointcutPrimitive.THIS, ex.getUnsupportedPrimitive()); + } + try { + p.parsePointcutExpression("!this(X)"); + fail("Expected UnsupportedPointcutPrimitiveException"); + } catch (UnsupportedPointcutPrimitiveException ex) { + assertEquals("This", PointcutPrimitive.THIS, ex.getUnsupportedPrimitive()); + } + try { + p.parsePointcutExpression("call(* *.*(..))"); + fail("Expected UnsupportedPointcutPrimitiveException"); + } catch (UnsupportedPointcutPrimitiveException ex) { + assertEquals("Call", PointcutPrimitive.CALL, ex.getUnsupportedPrimitive()); + } + try { + p.parsePointcutExpression("execution(* *.*(..))"); + fail("Expected UnsupportedPointcutPrimitiveException"); + } catch (UnsupportedPointcutPrimitiveException ex) { + assertEquals("Execution", PointcutPrimitive.EXECUTION, ex.getUnsupportedPrimitive()); + } + try { + p.parsePointcutExpression("get(* *)"); + fail("Expected UnsupportedPointcutPrimitiveException"); + } catch (UnsupportedPointcutPrimitiveException ex) { + assertEquals("Get", PointcutPrimitive.GET, ex.getUnsupportedPrimitive()); + } + try { + p.parsePointcutExpression("set(* *)"); + fail("Expected UnsupportedPointcutPrimitiveException"); + } catch (UnsupportedPointcutPrimitiveException ex) { + assertEquals("Set", PointcutPrimitive.SET, ex.getUnsupportedPrimitive()); + } + try { + p.parsePointcutExpression("initialization(new(..))"); + fail("Expected UnsupportedPointcutPrimitiveException"); + } catch (UnsupportedPointcutPrimitiveException ex) { + assertEquals("Initialization", PointcutPrimitive.INITIALIZATION, ex.getUnsupportedPrimitive()); + } + try { + p.parsePointcutExpression("preinitialization(new(..))"); + fail("Expected UnsupportedPointcutPrimitiveException"); + } catch (UnsupportedPointcutPrimitiveException ex) { + assertEquals("Prc-init", PointcutPrimitive.PRE_INITIALIZATION, ex.getUnsupportedPrimitive()); + } + try { + p.parsePointcutExpression("staticinitialization(T)"); + fail("Expected UnsupportedPointcutPrimitiveException"); + } catch (UnsupportedPointcutPrimitiveException ex) { + assertEquals("Staticinit", PointcutPrimitive.STATIC_INITIALIZATION, ex.getUnsupportedPrimitive()); + } + } + + public void testFormals() { + if (needToSkip) { + return; + } + + PointcutParser parser = PointcutParser + .getPointcutParserSupportingAllPrimitivesAndUsingSpecifiedClassloaderForResolution(this.getClass().getClassLoader()); + PointcutParameter param = parser.createPointcutParameter("x", String.class); + PointcutExpression pc = parser.parsePointcutExpression("args(x)", null, new PointcutParameter[] { param }); + assertEquals("args(x)", pc.getPointcutExpression()); + + try { + pc = parser.parsePointcutExpression("args(String)", null, new PointcutParameter[] { param }); + fail("Expecting IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + assertTrue("formal unbound", ex.getMessage().indexOf("formal unbound") != -1); + } + + try { + pc = parser.parsePointcutExpression("args(y)"); + fail("Expecting IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + assertTrue("no match for type name", ex.getMessage().indexOf("warning no match for this type name: y") != -1); + } + } + + public void testXLintConfiguration() { + if (needToSkip) { + return; + } + + PointcutParser p = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingSpecifiedClassloaderForResolution(this + .getClass().getClassLoader()); + try { + p.parsePointcutExpression("this(FooBar)"); + } catch (IllegalArgumentException ex) { + assertTrue("should have xlint:invalidAbsoluteTypeName", ex.getMessage().indexOf("Xlint:invalidAbsoluteTypeName") != -1); + } + Properties props = new Properties(); + props.put("invalidAbsoluteTypeName", "ignore"); + p.setLintProperties(props); + p.parsePointcutExpression("this(FooBar)"); + } + + private static class IgnoreWarningsMessageHandler implements IMessageHandler { + + public boolean handleMessage(IMessage message) throws AbortException { + if (message.getKind() != IMessage.WARNING) { + throw new RuntimeException("unexpected message: " + message.toString()); + } + return true; + } + + public boolean isIgnoring(Kind kind) { + if (kind != IMessage.ERROR) { + return true; + } + return false; + } + + public void dontIgnore(Kind kind) { + } + + public void ignore(Kind kind) { + } + + } +} diff --git a/weaver/src/test/java/org/aspectj/weaver/tools/ReadingAttributesTest.java b/weaver/src/test/java/org/aspectj/weaver/tools/ReadingAttributesTest.java new file mode 100644 index 000000000..5814aa90f --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/tools/ReadingAttributesTest.java @@ -0,0 +1,66 @@ +/* ******************************************************************* + * Copyright (c) 2009 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement + * ******************************************************************/ +package org.aspectj.weaver.tools; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; + +import junit.framework.TestCase; + +import org.aspectj.apache.bcel.classfile.Attribute; +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.classfile.Unknown; +import org.aspectj.apache.bcel.util.ClassPath; +import org.aspectj.apache.bcel.util.SyntheticRepository; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverStateInfo; + +public class ReadingAttributesTest extends TestCase { + + public void testSkip() {} // Review what to do about these tests + + public void xtestWeaverStateInfo() throws ClassNotFoundException, IOException { + + JavaClass jc = getClassFrom(new File("n:/temp"), "com.springsource.petclinic.domain.Visit"); + assertNotNull(jc); + Attribute[] attrs = jc.getAttributes(); + for (int i = 0; i < attrs.length; i++) { + System.out.println(attrs[i].getName()); + if (attrs[i].getName().endsWith("WeaverState")) { + Unknown u = (Unknown) attrs[i]; + VersionedDataInputStream vdis = new VersionedDataInputStream(new ByteArrayInputStream(u.getBytes()), null); + // WeaverStateInfo wsi = + WeaverStateInfo.read(vdis, null); + // System.out.println(wsi); + } + } + // Method[] meths = jc.getMethods(); + // Method oneWeWant = null; + // for (int i = 0; i < meths.length && oneWeWant == null; i++) { + // Method method = meths[i]; + // if (method.getName().equals("main")) { + // oneWeWant = meths[i]; + // } + // } + } + + public SyntheticRepository createRepos(File cpentry) { + ClassPath cp = new ClassPath(cpentry + File.pathSeparator + System.getProperty("java.class.path")); + return SyntheticRepository.getInstance(cp); + } + + protected JavaClass getClassFrom(File where, String clazzname) throws ClassNotFoundException { + SyntheticRepository repos = createRepos(where); + return repos.loadClass(clazzname); + } +} diff --git a/weaver/src/test/java/org/aspectj/weaver/tools/TypePatternMatcherTest.java b/weaver/src/test/java/org/aspectj/weaver/tools/TypePatternMatcherTest.java new file mode 100644 index 000000000..523ee4a83 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/tools/TypePatternMatcherTest.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.aspectj.weaver.tools; + +import java.util.HashMap; +import java.util.Map; + +import org.aspectj.util.LangUtil; + +import junit.framework.TestCase; + +public class TypePatternMatcherTest extends TestCase { + + TypePatternMatcher tpm; + + private boolean needToSkip = false; + + /** this condition can occur on the build machine only, and is way too complex to fix right now... */ + private boolean needToSkipPointcutParserTests() { + if (!LangUtil.is15VMOrGreater()) return false; + try { + Class.forName("org.aspectj.weaver.reflect.Java15ReflectionBasedReferenceTypeDelegate",false,this.getClass().getClassLoader());//ReflectionBasedReferenceTypeDelegate.class.getClassLoader()); + } catch (ClassNotFoundException cnfEx) { + return true; + } + return false; + } + + public void testMatching() { + if (needToSkip) return; + + assertTrue("Map+ matches Map",tpm.matches(Map.class)); + assertTrue("Map+ matches HashMap",tpm.matches(HashMap.class)); + assertFalse("Map+ does not match String",tpm.matches(String.class)); + } + + protected void setUp() throws Exception { + super.setUp(); + needToSkip = needToSkipPointcutParserTests(); + if (needToSkip) return; + PointcutParser pp = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingSpecifiedClassloaderForResolution(this.getClass().getClassLoader()); + tpm = pp.parseTypePattern("java.util.Map+"); + } + + + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/tools/cache/AbstractCacheBackingTestSupport.java b/weaver/src/test/java/org/aspectj/weaver/tools/cache/AbstractCacheBackingTestSupport.java new file mode 100644 index 000000000..5ad69a91d --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/tools/cache/AbstractCacheBackingTestSupport.java @@ -0,0 +1,379 @@ +/******************************************************************************* + * Copyright (c) 2012 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Lyor Goldstein (vmware) add support for weaved class being re-defined + *******************************************************************************/ +package org.aspectj.weaver.tools.cache; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.StreamCorruptedException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.security.CodeSource; +import java.security.ProtectionDomain; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + +import junit.framework.TestCase; + +import org.aspectj.util.FileUtil; +import org.aspectj.util.LangUtil; +import org.aspectj.weaver.tools.cache.AbstractIndexedFileCacheBacking.IndexEntry; + +/** + */ +public abstract class AbstractCacheBackingTestSupport extends TestCase { + public static final String JAR_FILE_SUFFIX=".jar"; + /** + * Prefix used in URL(s) that reference a resource inside a JAR + */ + public static final String JAR_URL_PREFIX="jar:"; + /** + * Separator used in URL(s) that reference a resource inside a JAR + * to denote the sub-path inside the JAR + */ + public static final char RESOURCE_SUBPATH_SEPARATOR='!'; + + private File targetFolder; + private File testTempFolder; + protected File root; + + public static final String TEMP_SUBFOLDER_NAME="temp"; + + protected AbstractCacheBackingTestSupport() { + super(); + } + + protected AbstractCacheBackingTestSupport(String name) { + super(name); + } + + @Override + public void setUp() throws Exception { + super.setUp(); + if (root == null) { + root = createTempFile("aspectj", "testdir"); + FileUtil.deleteContents(root); + } + } + + @Override + public void tearDown() throws Exception { + if (root != null) { + FileUtil.deleteContents(root); + root = null; + } + + if (targetFolder != null) { + FileUtil.deleteContents(targetFolder); + } + + super.tearDown(); + } + + protected File ensureTempFolderExists () throws IllegalStateException { + synchronized(TEMP_SUBFOLDER_NAME) { + if (testTempFolder == null) { + File parent=detectTargetFolder(); + testTempFolder = new File(parent, TEMP_SUBFOLDER_NAME); + } + } + + return ensureFolderExists(testTempFolder); + } + + protected File detectTargetFolder () throws IllegalStateException { + synchronized(TEMP_SUBFOLDER_NAME) { + if (targetFolder == null) { + if ((targetFolder=detectTargetFolder(getClass())) == null) { + throw new IllegalStateException("Failed to detect target folder"); + } + } + } + + return targetFolder; + } + + protected File createTempFile (String prefix, String suffix) throws IOException { + File destFolder=ensureTempFolderExists(); + return File.createTempFile(prefix, suffix, destFolder); + } + + public static final File ensureFolderExists (File folder) throws IllegalStateException { + if (folder == null) { + throw new IllegalArgumentException("No folder to ensure existence"); + } + + if ((!folder.exists()) && (!folder.mkdirs())) { + throw new IllegalStateException("Failed to create " + folder.getAbsolutePath()); + } + + return folder; + } + /** + * @param anchor An anchor {@link Class} whose container we want to use + * as the starting point for the "target" folder lookup up the + * hierarchy + * @return The "target" folder - null if not found + * @see #detectTargetFolder(File) + */ + public static final File detectTargetFolder (Class anchor) { + return detectTargetFolder(getClassContainerLocationFile(anchor)); + } + + /** + * @param anchorFile An anchor {@link File) we want to use + * as the starting point for the "target" folder lookup up the + * hierarchy + * @return The "target" folder - null if not found + */ + public static final File detectTargetFolder (File anchorFile) { + for (File file=anchorFile; file != null; file=file.getParentFile()) { + if (!file.isDirectory()) { + continue; + } + + String name=file.getName(); + if ("target".equals(name) || "bin".equals(name) || "src".equals(name)) { + File parent=file.getParentFile(); + return new File(parent, "target2"); + } + } + + return null; + } + + /** + * @param clazz A {@link Class} object + * @return A {@link File} of the location of the class bytes container + * - e.g., the root folder, the containing JAR, etc.. Returns + * null if location could not be resolved + * @throws IllegalArgumentException If location is not a valid + * {@link File} location + * @see #getClassContainerLocationURI(Class) + * @see File#File(URI) + */ + public static File getClassContainerLocationFile (Class clazz) + throws IllegalArgumentException { + try { + URI uri=getClassContainerLocationURI(clazz); + return (uri == null) ? null : new File(uri); + } catch(URISyntaxException e) { + throw new IllegalArgumentException(e.getClass().getSimpleName() + ": " + e.getMessage(), e); + } + } + + /** + * @param clazz A {@link Class} object + * @return A {@link URI} to the location of the class bytes container + * - e.g., the root folder, the containing JAR, etc.. Returns + * null if location could not be resolved + * @throws URISyntaxException if location is not a valid URI + * @see #getClassContainerLocationURL(Class) + */ + public static URI getClassContainerLocationURI (Class clazz) throws URISyntaxException { + URL url=getClassContainerLocationURL(clazz); + return (url == null) ? null : url.toURI(); + } + + /** + * @param clazz A {@link Class} object + * @return A {@link URL} to the location of the class bytes container + * - e.g., the root folder, the containing JAR, etc.. Returns + * null if location could not be resolved + */ + public static URL getClassContainerLocationURL (Class clazz) { + ProtectionDomain pd=clazz.getProtectionDomain(); + CodeSource cs=(pd == null) ? null : pd.getCodeSource(); + URL url=(cs == null) ? null : cs.getLocation(); + if (url == null) { + ClassLoader cl=getDefaultClassLoader(clazz); + String className=clazz.getName().replace('.', '/') + ".class"; + if ((url=cl.getResource(className)) == null) { + return null; + } + + String srcForm=getURLSource(url); + if (LangUtil.isEmpty(srcForm)) { + return null; + } + + try { + url = new URL(srcForm); + } catch(MalformedURLException e) { + throw new IllegalArgumentException("getClassContainerLocationURL(" + clazz.getName() + ")" + + "Failed to create URL=" + srcForm + " from " + url.toExternalForm() + + ": " + e.getMessage()); + } + } + + return url; + } + /** + * @param anchor An "anchor" {@link Class} to be used in case + * no thread context loader is available + * @return A {@link ClassLoader} to be used by the caller. The loader is + * resolved in the following manner:


+ *
    + *
  • + * If a non-null loader is returned from the + * {@link Thread#getContextClassLoader()} call then use it. + *
  • + * + *
  • + * Otherwise, use the same loader that was used to load the anchor class. + *
  • + *
+ * @throws IllegalArgumentException if no anchor class provided (regardless of + * whether it is used or not) + */ + public static ClassLoader getDefaultClassLoader(Class anchor) { + if (anchor == null) { + throw new IllegalArgumentException("No anchor class provided"); + } + + Thread t=Thread.currentThread(); + ClassLoader cl=t.getContextClassLoader(); + if (cl == null) { + // No thread context class loader -> use class loader of this class. + cl = anchor.getClassLoader(); + } + + if (cl == null) { // no class loader - assume system + cl = ClassLoader.getSystemClassLoader(); + } + + return cl; + + } + public static final String getURLSource (File file) { + return getURLSource((file == null) ? null : file.toURI()); + } + + public static final String getURLSource (URI uri) { + return getURLSource((uri == null) ? null : uri.toString()); + } + + /** + * @param url The {@link URL} value - ignored if null + * @return The URL(s) source path where {@link #JAR_URL_PREFIX} and + * any sub-resource are stripped + * @see #getURLSource(String) + */ + public static final String getURLSource (URL url) { + return getURLSource((url == null) ? null : url.toExternalForm()); + } + + /** + * @param externalForm The {@link URL#toExternalForm()} string - ignored if + * null/empty + * @return The URL(s) source path where {@link #JAR_URL_PREFIX} and + * any sub-resource are stripped + */ + public static final String getURLSource (String externalForm) { + String url=externalForm; + if (LangUtil.isEmpty(url)) { + return url; + } + + url = stripJarURLPrefix(externalForm); + if (LangUtil.isEmpty(url)){ + return url; + } + + int sepPos=url.indexOf(RESOURCE_SUBPATH_SEPARATOR); + if (sepPos < 0) { + return adjustURLPathValue(url); + } else { + return adjustURLPathValue(url.substring(0, sepPos)); + } + } + + /** + * @param path A URL path value + * @return The path after stripping any trailing '/' provided the path + * is not '/' itself + */ + public static final String adjustURLPathValue(final String path) { + final int pathLen=LangUtil.isEmpty(path) ? 0 : path.length(); + if ((pathLen <= 1) || (path.charAt(pathLen - 1) != '/')) { + return path; + } + + return path.substring(0, pathLen - 1); + } + + public static final String adjustURLPathValue(URL url) { + return adjustURLPathValue((url == null) ? null : url.getPath()); + } + + public static String stripJarURLPrefix(String externalForm) { + String url=externalForm; + if (LangUtil.isEmpty(url)) { + return url; + } + + if (url.startsWith(JAR_URL_PREFIX)) { + return url.substring(JAR_URL_PREFIX.length()); + } + + return url; + } + + protected static final void writeIndex (File indexFile, IndexEntry ... entries) throws IOException { + writeIndex(indexFile, LangUtil.isEmpty(entries) ? Collections.emptyList() : Arrays.asList(entries)); + } + + protected static final void writeIndex (File indexFile, Collection entries) throws IOException { + File indexDir=indexFile.getParentFile(); + if ((!indexDir.exists()) && (!indexDir.mkdirs())) { + throw new IOException("Failed to create path to " + indexFile.getAbsolutePath()); + } + + int numEntries=LangUtil.isEmpty(entries) ? 0 : entries.size(); + IndexEntry[] entryValues=(numEntries <= 0) ? null : entries.toArray(new IndexEntry[numEntries]); + // if no entries, simply delete the index file + if (LangUtil.isEmpty(entryValues)) { + if (indexFile.exists() && (!indexFile.delete())) { + throw new StreamCorruptedException("Failed to clean up index file at " + indexFile.getAbsolutePath()); + } + + return; + } + + ObjectOutputStream oos=new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(indexFile), 4096)); + try { + oos.writeObject(entryValues); + } finally { + oos.close(); + } + } + + public static final void assertArrayEquals (String msg, byte[] expected, byte[] actual) { + int eLen=LangUtil.isEmpty(expected) ? 0 : expected.length; + int aLen=LangUtil.isEmpty(actual) ? 0 : expected.length; + assertEquals(msg + "[mismatched length]", eLen, aLen); + + for (int index=0; index < eLen; index++) { + byte eb=expected[index], ab=actual[index]; + if (eb != ab) { + fail(msg + ": Mismatched value at index=" + index + + " - " + ab + " instead of " + eb + + ": expected=" + Arrays.toString(expected) + ", actual=" + Arrays.toString(actual)); + } + } + } +} diff --git a/weaver/src/test/java/org/aspectj/weaver/tools/cache/AsynchronousFileCacheBackingTestSupport.java b/weaver/src/test/java/org/aspectj/weaver/tools/cache/AsynchronousFileCacheBackingTestSupport.java new file mode 100644 index 000000000..7d1b66407 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/tools/cache/AsynchronousFileCacheBackingTestSupport.java @@ -0,0 +1,193 @@ +/******************************************************************************* + * Copyright (c) 2012 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Lyor Goldstein (vmware) add support for weaved class being re-defined + *******************************************************************************/ +package org.aspectj.weaver.tools.cache; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Random; +import java.util.TreeMap; + +import org.aspectj.util.FileUtil; +import org.aspectj.util.LangUtil; +import org.aspectj.weaver.tools.cache.AbstractIndexedFileCacheBacking.IndexEntry; + +/** + */ +public abstract class AsynchronousFileCacheBackingTestSupport + extends AbstractCacheBackingTestSupport { + private File cacheDir, indexFile; + protected final byte[] bytes=new byte[Byte.MAX_VALUE]; + protected final Random random=new Random(System.nanoTime()); + + protected AsynchronousFileCacheBackingTestSupport() { + super(); + } + + protected AsynchronousFileCacheBackingTestSupport(String name) { + super(name); + } + + @Override + public void setUp () throws Exception { + super.setUp(); + cleanupCache(); + + random.nextBytes(bytes); + } + + @Override + public void tearDown () throws Exception { + cleanupCache(); + super.tearDown(); + } + + protected void cleanupCache() { + if (indexFile != null) { + if (FileUtil.deleteContents(indexFile) > 0) { + System.out.println("Deleted " + indexFile); + } + indexFile = null; + } + + if (cacheDir != null) { + if (FileUtil.deleteContents(cacheDir) > 0) { + System.out.println("Deleted " + cacheDir); + } + cacheDir = null; + } + } + + protected File getIndexFile () { + if (indexFile == null) { + File parent=getCacheDir(); + indexFile=new File(parent, AbstractIndexedFileCacheBacking.INDEX_FILE); + } + + return indexFile; + } + + protected File getCacheDir () { + if (cacheDir == null) { + File targetDir=detectTargetFolder(); + cacheDir = new File(targetDir, "dir-" + String.valueOf(Math.random())); + } + + return ensureFolderExists(cacheDir); + } + + protected abstract AsynchronousFileCacheBacking createFileBacking (File dir); + + public void testDeleteIndexFileOnEmptyIndex () throws Exception { + IndexEntry[] entries={ + createIndexEntry("weaved-empty", false, false, bytes, bytes), + createIndexEntry("generated-empty", true, false, bytes, bytes) + }; + File cacheIndex=getIndexFile(); + writeIndex(cacheIndex, entries); + assertTrue("No initial index file available: " + cacheIndex, cacheIndex.canRead()); + + AsynchronousFileCacheBacking cache=createFileBacking(getCacheDir()); + // the call should read an empty index since no data files exist + Map indexMap=cache.getIndexMap(); + assertEquals("Mismatched index size", 0, indexMap.size()); + + // no data files were created + Map bytesMap=cache.getBytesMap(); + assertEquals("Mismatched bytes size", 0, bytesMap.size()); + + writeIndex(cache.getIndexFile(), cache.getIndexEntries()); + + assertFalse("Index file still available: " + cacheIndex, cacheIndex.canRead()); + } + + protected long generateNewBytes () { + final long CRC=AbstractCacheBacking.crc(bytes); + long crc=CRC; + // 8 tries should be enough to find a non-matching CRC... + for (int index=0; (index < Byte.SIZE) && (CRC == crc) && (crc != -1L); index++) { + random.nextBytes(bytes); + crc = AbstractCacheBacking.crc(bytes); + } + assertTrue("Could not generate different CRC for " + CRC, crc != CRC); + + return crc; + } + + protected Map createDataFiles (IndexEntry ... entries) throws IOException { + return createDataFiles(LangUtil.isEmpty(entries) ? Collections.emptyList() : Arrays.asList(entries)); + } + + protected Map createDataFiles (Collection entries) throws IOException { + if (LangUtil.isEmpty(entries)) { + return Collections.emptyMap(); + } + + Map files=new TreeMap(); + for (IndexEntry entry : entries) { + File file=createDataFile(entry); + if (file != null) { + files.put(entry.key, file); + } + } + + return files; + } + + protected File createDataFile (IndexEntry entry) throws IOException { + return createDataFile(entry, entry.ignored ? null : bytes); + } + + protected File createDataFile (IndexEntry entry, byte[] dataBytes) throws IOException { + return createDataFile(entry.key, dataBytes); + } + + protected File createDataFile (String key, byte[] dataBytes) throws IOException { + if (LangUtil.isEmpty(dataBytes)) { + return null; + } + + File parent=getCacheDir(), file=new File(parent, key); + OutputStream out=new FileOutputStream(file); + try { + out.write(dataBytes); + } finally { + out.close(); + } + + return file; + } + + protected static final IndexEntry createIgnoredEntry (String key) { + return createIndexEntry(key, false, true, null, null); + } + + protected static final IndexEntry createIndexEntry (String key, boolean generated, boolean ignored, byte[] bytes, byte[] originalBytes) { + IndexEntry entry=new IndexEntry(); + entry.key = key; + entry.generated = generated; + entry.ignored = ignored; + if (ignored) { + assertFalse(key + " ignored cannot be generated", generated); + } else { + entry.crcClass = AbstractCacheBacking.crc(originalBytes); + entry.crcWeaved = AbstractCacheBacking.crc(bytes); + } + + return entry; + } +} diff --git a/weaver/src/test/java/org/aspectj/weaver/tools/cache/DefaultCacheKeyResolverTest.java b/weaver/src/test/java/org/aspectj/weaver/tools/cache/DefaultCacheKeyResolverTest.java new file mode 100644 index 000000000..139488b3c --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/tools/cache/DefaultCacheKeyResolverTest.java @@ -0,0 +1,95 @@ +/******************************************************************************* + * Copyright (c) 2012 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * John Kew (vmware) initial implementation + *******************************************************************************/ + +package org.aspectj.weaver.tools.cache; + +import junit.framework.TestCase; + +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Arrays; +import java.util.Collections; + +/** + */ +public class DefaultCacheKeyResolverTest extends TestCase { + byte[] FAKE_BYTES = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + String FAKE_CLASS = "com.example.foo.Bar"; + + DefaultCacheKeyResolver resolver = new DefaultCacheKeyResolver(); + + class BasicTestCL extends ClassLoader { + public BasicTestCL () { + super(); + } + } + + class URLTestCL extends URLClassLoader { + public URLTestCL(URL... urls) { + super(urls); + } + } + + + public void testNonURLClassLoaderScope() throws Exception { + String scope = resolver.createClassLoaderScope(new BasicTestCL(), Collections.emptyList()); + assertTrue(scope.startsWith(BasicTestCL.class.getSimpleName())); + } + + public void testCreateURLClassLoaderScope() throws Exception { + URL testURLA = new URL("http://example.com"); + URL testURLB = new URL("file:///tmp"); + URL testURLC = new URL("ftp://ftp.example.com"); + URLTestCL A = new URLTestCL(testURLA); + URLTestCL AB = new URLTestCL(testURLA, testURLB); + URLTestCL BC = new URLTestCL(testURLB, testURLC); + URLTestCL BC2 = new URLTestCL(testURLC, testURLB); + String[] a = {"one", "two", "three", "four"}; + String[] a2 = {"one", "two", "three"}; + String scopeAa = resolver.createClassLoaderScope(A, Arrays.asList(a)); + String scopeABa = resolver.createClassLoaderScope(AB, Arrays.asList(a)); + String scopeBCa = resolver.createClassLoaderScope(BC, Arrays.asList(a)); + String scopeBC2a = resolver.createClassLoaderScope(BC2, Arrays.asList(a)); + String scopeAa2 = resolver.createClassLoaderScope(A, Arrays.asList(a2)); + String scopeABa2 = resolver.createClassLoaderScope(AB, Arrays.asList(a2)); + String scopeBCa2 = resolver.createClassLoaderScope(BC, Arrays.asList(a2)); + String scopeBC2a2 = resolver.createClassLoaderScope(BC2, Arrays.asList(a2)); + + assertFalse(scopeAa.equals(scopeABa)); + assertFalse(scopeAa.equals(scopeBCa)); + assertFalse(scopeABa.equals(scopeBCa)); + assertTrue(scopeBC2a.equals(scopeBCa)); + assertFalse(scopeAa.equals(scopeAa2)); + assertFalse(scopeABa.equals(scopeABa2)); + assertFalse(scopeBCa.equals(scopeBCa2)); + assertFalse(scopeBC2a.equals(scopeBC2a2)); + + + } + + + public void testCreateGeneratedCacheKey() throws Exception { + CachedClassReference ref = resolver.generatedKey(FAKE_CLASS); + assertTrue(ref.getKey().startsWith(FAKE_CLASS)); + assertTrue(ref.getKey().matches(resolver.getGeneratedRegex())); + assertEquals(FAKE_CLASS, resolver.keyToClass(ref.getKey())); + } + + public void testCreateCacheKey() throws Exception { + // crc hashing + CachedClassReference ref = resolver.weavedKey(FAKE_CLASS, FAKE_BYTES); + assertTrue("key " + ref.getKey() + " does not match " + resolver.getWeavedRegex(), ref.getKey().matches(resolver.getWeavedRegex())); + String className = resolver.keyToClass(ref.getKey()); + assertEquals("class " + FAKE_CLASS + " != " + className, FAKE_CLASS, className); + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/tools/cache/DefaultFileCacheBackingTest.java b/weaver/src/test/java/org/aspectj/weaver/tools/cache/DefaultFileCacheBackingTest.java new file mode 100644 index 000000000..2d5ec0c77 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/tools/cache/DefaultFileCacheBackingTest.java @@ -0,0 +1,179 @@ +/******************************************************************************* + * Copyright (c) 2012 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * John Kew (vmware) initial implementation + * Lyor Goldstein (vmware) add support for weaved class being re-defined + *******************************************************************************/ + +package org.aspectj.weaver.tools.cache; + +import java.io.File; +import java.util.zip.CRC32; + +import org.aspectj.util.LangUtil; +import org.aspectj.weaver.tools.cache.AbstractIndexedFileCacheBacking.IndexEntry; + +/** + */ +public class DefaultFileCacheBackingTest extends AbstractCacheBackingTestSupport { + private final byte[] FAKE_BYTES = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + private final String FAKE_CLASS = "com.example.foo.Bar"; + private final CacheKeyResolver resolver = new DefaultCacheKeyResolver(); + private final CachedClassReference fakeRef = resolver.weavedKey(FAKE_CLASS, FAKE_BYTES); + private final String fakeKey=fakeRef.getKey(); + + public DefaultFileCacheBackingTest () { + super(); + } + + public void testCreateBacking() throws Exception { + CacheBacking backing = DefaultFileCacheBacking.createBacking(root); + assertNotNull(backing); + assertTrue("Root folder not created: " + root, root.exists()); + assertTrue("Root folder not a directory: " + root, root.isDirectory()); + } + + public void testClear() { + CacheBacking backing = DefaultFileCacheBacking.createBacking(root); + backing.put(new CachedClassEntry(fakeRef, FAKE_BYTES, CachedClassEntry.EntryType.WEAVED), FAKE_BYTES); + assertNotNull(backing.get(fakeRef, FAKE_BYTES)); + backing.clear(); + assertNull(backing.get(fakeRef, FAKE_BYTES)); + } + + private CachedClassEntry createTestEntry(String key) { + return new CachedClassEntry(new CachedClassReference(key, key), FAKE_BYTES, CachedClassEntry.EntryType.WEAVED); + } + + public void testGetKeys() throws Exception { + CacheBacking backing = DefaultFileCacheBacking.createBacking(root); + backing.put(createTestEntry("apple"), FAKE_BYTES); + backing.put(createTestEntry("apply"), FAKE_BYTES); + backing.put(createTestEntry("orange"), FAKE_BYTES); + String[] matches = backing.getKeys("app.*"); + assertEquals(2, matches.length); + matches = backing.getKeys("orange"); + assertEquals(1, matches.length); + assertEquals("orange", matches[0]); + } + + public void testPut() throws Exception { + CacheBacking backing = DefaultFileCacheBacking.createBacking(root); + backing.put(new CachedClassEntry(fakeRef, FAKE_BYTES, CachedClassEntry.EntryType.WEAVED), FAKE_BYTES); + File cachedFile = new File(root, fakeKey); + assertTrue(cachedFile.exists()); + assertTrue(cachedFile.isFile()); + assertEquals(FAKE_BYTES.length, cachedFile.length()); + } + + public void testGet() throws Exception { + DefaultFileCacheBacking backing = DefaultFileCacheBacking.createBacking(root); + assertNull(backing.get(fakeRef, FAKE_BYTES)); + backing.put(new CachedClassEntry(fakeRef, FAKE_BYTES, CachedClassEntry.EntryType.WEAVED), FAKE_BYTES); + File cachedFile = new File(root, fakeKey); + assertTrue(cachedFile.isFile()); + assertEquals(FAKE_BYTES.length, cachedFile.length()); + CRC32 expectedCRC = new CRC32(); + expectedCRC.update(FAKE_BYTES); + assertTrue(indexEntryExists(backing, fakeKey, expectedCRC.getValue())); + CachedClassEntry entry = backing.get(fakeRef, FAKE_BYTES); + assertEquals(FAKE_BYTES.length, entry.getBytes().length); + } + + public void testRemove() throws Exception { + DefaultFileCacheBacking backing = DefaultFileCacheBacking.createBacking(root); + backing.put(new CachedClassEntry(fakeRef, FAKE_BYTES, CachedClassEntry.EntryType.WEAVED), FAKE_BYTES); + File cachedFile = new File(root, fakeKey); + assertTrue("Cached file not found: " + cachedFile, cachedFile.exists()); + assertTrue("Cached file not a file: " + cachedFile, cachedFile.isFile()); + CRC32 expectedCRC = new CRC32(); + expectedCRC.update(FAKE_BYTES); + assertTrue("Cached entry index not found", indexEntryExists(backing, fakeKey, expectedCRC.getValue())); + backing.remove(fakeRef); + + assertFalse("CacheFile Still exists: " + cachedFile, cachedFile.exists()); + assertFalse("Cached file is a file: " + cachedFile, cachedFile.isFile()); + assertFalse("Cached entry index not removed", indexEntryExists(backing, fakeKey, expectedCRC.getValue())); + } + + public void testMultiFile() throws Exception { + CachedClassEntry entry; + File cachedFile; + CRC32 expectedCRC = new CRC32(); + expectedCRC.update(FAKE_BYTES); + DefaultFileCacheBacking backing = DefaultFileCacheBacking.createBacking(root); + // add weaved + CachedClassReference wref = resolver.weavedKey(FAKE_CLASS + "WEAVED", FAKE_BYTES); + entry = new CachedClassEntry(wref, FAKE_BYTES, CachedClassEntry.EntryType.WEAVED); + backing.put(entry, FAKE_BYTES); + cachedFile = new File(root, wref.getKey()); + assertTrue(cachedFile.exists()); + assertTrue(cachedFile.isFile()); + assertTrue(indexEntryExists(backing, wref.getKey(), expectedCRC.getValue())); + + // add generated + CachedClassReference gref = resolver.generatedKey(FAKE_CLASS + "GENERATED"); + entry = new CachedClassEntry(gref, FAKE_BYTES, CachedClassEntry.EntryType.GENERATED); + backing.put(entry, FAKE_BYTES); + cachedFile = new File(root, gref.getKey()); + assertTrue(cachedFile.exists()); + assertTrue(cachedFile.isFile()); + assertTrue(indexEntryExists(backing, gref.getKey(), expectedCRC.getValue())); + + // add ignored + CachedClassReference iref = resolver.generatedKey(FAKE_CLASS + "IGNORED"); + entry = new CachedClassEntry(iref, FAKE_BYTES, CachedClassEntry.EntryType.IGNORED); + backing.put(entry, FAKE_BYTES); + cachedFile = new File(root, iref.getKey()); + assertFalse(cachedFile.exists()); + assertTrue(indexEntryExists(backing, iref.getKey(), expectedCRC.getValue())); + + backing.remove(wref); + backing.remove(gref); + backing.remove(iref); + } + + public void testOriginalClassBytesChanged () { + DefaultFileCacheBacking backing = DefaultFileCacheBacking.createBacking(root); + backing.put(new CachedClassEntry(fakeRef, FAKE_BYTES, CachedClassEntry.EntryType.WEAVED), FAKE_BYTES); + + CachedClassEntry entry = backing.get(fakeRef, FAKE_BYTES); + assertNotNull("No initial entry", entry); + + byte[] newBytes=new byte[FAKE_BYTES.length]; + for (int index=0; index < FAKE_BYTES.length; index++) { + newBytes[index] = (byte) (0 - FAKE_BYTES[index]); + } + + entry = backing.get(fakeRef, newBytes); + assertNull("Unexpected modified bytes entry: " + entry, entry); + + File cachedFile = new File(root, fakeKey); + assertFalse("Cache file not removed", cachedFile.exists()); + } + + private boolean indexEntryExists(AbstractIndexedFileCacheBacking cache, String key, long expectedCRC) throws Exception { + long storedCRC = 0L; + IndexEntry[] index = cache.readIndex(new File(root, AbstractIndexedFileCacheBacking.INDEX_FILE)); + if (LangUtil.isEmpty(index)) { + return false; + } + + for (IndexEntry ie : index) { + if (ie.key.equals(key)) { + storedCRC = ie.crcWeaved; + if (!ie.ignored) { + assertEquals(expectedCRC, storedCRC); + } + return true; + } + } + return false; + } +} diff --git a/weaver/src/test/java/org/aspectj/weaver/tools/cache/FlatFileCacheBackingTest.java b/weaver/src/test/java/org/aspectj/weaver/tools/cache/FlatFileCacheBackingTest.java new file mode 100644 index 000000000..87daff796 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/tools/cache/FlatFileCacheBackingTest.java @@ -0,0 +1,147 @@ +/******************************************************************************* + * Copyright (c) 2012 VMware, Inc. + * + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Lyor Goldstein + *******************************************************************************/ + +package org.aspectj.weaver.tools.cache; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Map; +import java.util.TreeMap; + +import org.aspectj.weaver.tools.cache.AbstractIndexedFileCacheBacking.IndexEntry; + +/** + * @author Lyor Goldstein + */ +public class FlatFileCacheBackingTest extends AsynchronousFileCacheBackingTestSupport { + public FlatFileCacheBackingTest() { + super(); + } + + @Override + protected FlatFileCacheBacking createFileBacking(File dir) { + return new FlatFileCacheBacking(dir); + } + + public void testReadIndex() throws IOException { + IndexEntry[] entries = { createIgnoredEntry("ignored"), createIndexEntry("weaved", false, false, bytes, bytes), + createIndexEntry("generated", true, false, bytes, bytes) }; + File indexFile = getIndexFile(); + writeIndex(indexFile, entries); + Map dataFiles = createDataFiles(entries); + + File cacheDir = getCacheDir(); + AsynchronousFileCacheBacking cache = createFileBacking(cacheDir); + Map indexMap = cache.getIndexMap(); + assertEquals("Mismatched index size", entries.length, indexMap.size()); + + Map bytesMap = cache.getBytesMap(); + assertEquals("Mismatched bytes size", dataFiles.size() /* the ignored one has no file */, bytesMap.size()); + + for (IndexEntry entry : entries) { + String key = entry.key; + assertNotNull("Missing entry for key=" + key, indexMap.get(key)); + + if (entry.ignored) { + assertNull("Unexpected bytes for ignored key=" + key, bytesMap.get(key)); + } else { + assertArrayEquals("Mismatched contents for key=" + key, bytes, bytesMap.get(key)); + } + } + } + + public void testIgnoredBadCrcDataFiles() throws Exception { + IndexEntry[] entries = { createIndexEntry("weaved-goodData", false, false, bytes, bytes), + createIndexEntry("badData-weaved", false, false, bytes, bytes), + createIndexEntry("generated-goodData", true, false, bytes, bytes), + createIndexEntry("badData-generated", true, false, bytes, bytes) }; + File indexFile = getIndexFile(); + writeIndex(indexFile, entries); + + Map dataFiles = createDataFiles(entries); + long newCrc = generateNewBytes(); + assertTrue("Bad new CRC", newCrc != (-1L)); + + Map badFiles = new TreeMap(); + for (IndexEntry entry : entries) { + String key = entry.key; + if (key.startsWith("badData")) { + File file = dataFiles.get(key); + OutputStream out = new FileOutputStream(file); + try { + out.write(bytes); + } finally { + out.close(); + } + dataFiles.remove(key); + badFiles.put(key, file); + } + } + + File cacheDir = getCacheDir(); + FlatFileCacheBacking cache = createFileBacking(cacheDir); + Map indexMap = cache.getIndexMap(); + assertEquals("Mismatched index size", dataFiles.size(), indexMap.size()); + + Map bytesMap = cache.getBytesMap(); + assertEquals("Mismatched bytes size", dataFiles.size(), bytesMap.size()); + + for (Map.Entry badEntry : badFiles.entrySet()) { + String key = badEntry.getKey(); + assertFalse("Unexpectedly indexed: " + key, indexMap.containsKey(key)); + assertFalse("Unexpectedly loaded: " + key, bytesMap.containsKey(key)); + + File file = badEntry.getValue(); + assertFalse("Unexpectedly still readable: " + key, file.canRead()); + } + } + + public void testSkipMissingDataFileOnReadIndex() throws IOException { + IndexEntry[] entries = { createIndexEntry("weaved-noData", false, false, null, null), + createIndexEntry("withData-weaved", false, false, bytes, bytes), + createIndexEntry("generated-noData", true, false, null, null), + createIndexEntry("withData-generated", true, false, bytes, bytes) }; + File indexFile = getIndexFile(); + writeIndex(indexFile, entries); + + Map dataFiles = new TreeMap(); + for (IndexEntry entry : entries) { + String key = entry.key; + if (key.startsWith("withData")) { + dataFiles.put(key, createDataFile(entry, bytes)); + } + } + + File cacheDir = getCacheDir(); + FlatFileCacheBacking cache = createFileBacking(cacheDir); + Map indexMap = cache.getIndexMap(); + assertEquals("Mismatched index size", dataFiles.size(), indexMap.size()); + + Map bytesMap = cache.getBytesMap(); + assertEquals("Mismatched bytes size", dataFiles.size(), bytesMap.size()); + + for (IndexEntry entry : entries) { + String key = entry.key; + if (key.startsWith("withData")) { + assertTrue("Not indexed: " + key, indexMap.containsKey(key)); + assertTrue("Not loaded: " + key, bytesMap.containsKey(key)); + } else { + assertFalse("Unexpectedly indexed: " + key, indexMap.containsKey(key)); + assertFalse("Unexpectedly loaded: " + key, bytesMap.containsKey(key)); + } + } + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/tools/cache/SimpleClassCacheTest.java b/weaver/src/test/java/org/aspectj/weaver/tools/cache/SimpleClassCacheTest.java new file mode 100644 index 000000000..68fac6913 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/tools/cache/SimpleClassCacheTest.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2012 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Abraham Nevado (lucierna) initial implementation + ********************************************************************************/ + +package org.aspectj.weaver.tools.cache; + +import java.io.File; + +import junit.framework.TestCase; + +/** + */ +public class SimpleClassCacheTest extends TestCase { + byte[] FAKE_BYTES_V1 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + byte[] FAKE_BYTES_V2 = {1, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + byte[] FAKE_WOVEN_BYTES_V1 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10}; + byte[] FAKE_WOVEN_BYTES_V2 = {1, 1, 2, 3, 4, 5, 6, 7, 8, 9,10}; + + + private SimpleCache createCache() throws Exception { + return new SimpleCache(System.getProperty("java.io.tmpdir"),true); + } + + + public void testCache() throws Exception { + String classA = "com.generated.A"; + SimpleCache cache = createCache(); + + cache.put(classA, FAKE_BYTES_V1, FAKE_WOVEN_BYTES_V1); + + + // Test the returned woven bytes are the original one + byte result[] = cache.getAndInitialize(classA, FAKE_BYTES_V1, null, null); + for(int i = 0; i < result.length; i ++){ + assertEquals("Cached version byte[" +i+"] should be equal to the original woven classe",result[i],FAKE_WOVEN_BYTES_V1[i]); + } + + // Assure the class is properly backed up in the backing folder + File f = new File (System.getProperty("java.io.tmpdir") + File.separator + "com.generated.A-1164760902"); + assertTrue("Class should be backed up to backing folder, with te CRC:1164760902 ",f.exists()); + + } + + public void testDifferentVersionCache() throws Exception { + String classA = "com.generated.A"; + SimpleCache cache = createCache(); + cache.put(classA, FAKE_BYTES_V1, FAKE_WOVEN_BYTES_V1); + cache.put(classA, FAKE_BYTES_V2, FAKE_WOVEN_BYTES_V2); + + // Test the returned woven bytes are the original one for v1 + byte result[] = cache.getAndInitialize(classA, FAKE_BYTES_V1, null, null); + for(int i = 0; i < result.length; i ++){ + assertEquals("Cached version v1 byte[" +i+"] should be equal to the original woven classe",result[i],FAKE_WOVEN_BYTES_V1[i]); + } + + // Test the returned woven bytes are the original one for v2 + result = cache.getAndInitialize(classA, FAKE_BYTES_V2, null, null); + for(int i = 0; i < result.length; i ++){ + assertEquals("Cached version v2 byte[" +i+"] should be equal to the original woven classe",result[i],FAKE_WOVEN_BYTES_V2[i]); + } + } +} \ No newline at end of file diff --git a/weaver/src/test/java/org/aspectj/weaver/tools/cache/WeavedClassCacheTest.java b/weaver/src/test/java/org/aspectj/weaver/tools/cache/WeavedClassCacheTest.java new file mode 100644 index 000000000..a02400eb8 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/tools/cache/WeavedClassCacheTest.java @@ -0,0 +1,152 @@ +/******************************************************************************* + * Copyright (c) 2012 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * John Kew (vmware) initial implementation + *******************************************************************************/ + +package org.aspectj.weaver.tools.cache; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import org.aspectj.bridge.AbortException; +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.IMessageHandler; +import org.aspectj.weaver.tools.GeneratedClassHandler; + +/** + */ +public class WeavedClassCacheTest extends AbstractCacheBackingTestSupport { + String FAKE_CLASS = "com.example.foo.Bar"; + byte[] FAKE_BYTES = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + public WeavedClassCacheTest () { + super(); + } + + public class MemoryCacheBacking implements CacheBacking { + HashMap cache = new HashMap(); + + public String[] getKeys(String regex) { + Set keys = cache.keySet(); + List matches = new LinkedList(); + for (String key : keys) { + if (key.matches(regex)) { + matches.add(key); + } + } + return matches.toArray(new String[0]); + } + + public void remove(CachedClassReference ref) { + cache.remove(ref.getKey()); + } + + public void clear() { + cache.clear(); + } + + public CachedClassEntry get(CachedClassReference ref, byte[] originalBytes) { + return cache.get(ref.getKey()); + } + + public void put(CachedClassEntry entry, byte[] originalBytes) { + assertNotNull("put(" + entry + ") no original bytes", originalBytes); + cache.put(entry.getKey(), entry); + } + } + + MemoryCacheBacking memoryBacking = new MemoryCacheBacking(); + + IMessageHandler messageHandler = new IMessageHandler() { + public boolean handleMessage(IMessage message) throws AbortException { + return true; + } + + public boolean isIgnoring(IMessage.Kind kind) { + return true; + } + + public void dontIgnore(IMessage.Kind kind) { + // do nothing + } + + public void ignore(IMessage.Kind kind) { + // do nothing + } + }; + + public class TestGeneratedClassHandler implements GeneratedClassHandler { + public int accepts = 0; + public List classesISaw = new LinkedList(); + + public void acceptClass (String name, byte[] originalBytes, byte[] wovenBytes) { + accepts++; + classesISaw.add(name); + } + } + + TestGeneratedClassHandler generatedClassHandler = new TestGeneratedClassHandler(); + + CacheKeyResolver resolver = new DefaultCacheKeyResolver(); + + private WeavedClassCache createCache() throws Exception { + return new WeavedClassCache(generatedClassHandler, messageHandler, "test", memoryBacking, resolver); + } + + private void reset() throws Exception { + memoryBacking.cache.clear(); + generatedClassHandler.accepts = 0; + generatedClassHandler.classesISaw.clear(); + } + + public void testGetCachingClassHandler() throws Exception { + WeavedClassCache cache = createCache(); + GeneratedClassHandler newHandle = cache.getCachingClassHandler(); + assertTrue(generatedClassHandler != newHandle); + assertTrue(newHandle instanceof GeneratedCachedClassHandler); + } + + public void testCache() throws Exception { + reset(); + WeavedClassCache cache = createCache(); + CacheStatistics stats = cache.getStats(); + CachedClassReference ref = cache.createCacheKey(FAKE_CLASS, FAKE_BYTES); + assertNull(cache.get(ref, FAKE_BYTES)); + cache.put(ref, FAKE_BYTES, FAKE_BYTES); + assertNotNull(cache.get(ref, FAKE_BYTES)); + + assertEquals(new String(FAKE_BYTES), new String(cache.get(ref, FAKE_BYTES).getBytes())); + + ref = cache.createGeneratedCacheKey(FAKE_CLASS); + assertNull(cache.get(ref, FAKE_BYTES)); + cache.put(ref, FAKE_BYTES, FAKE_BYTES); + assertNotNull(cache.get(ref, FAKE_BYTES)); + assertEquals(new String(FAKE_BYTES), new String(cache.get(ref, FAKE_BYTES).getBytes())); + + assertEquals(4, stats.getHits()); + assertEquals(2, stats.getMisses()); + + + } + + public void testRemove() throws Exception { + reset(); + WeavedClassCache cache = createCache(); + CachedClassReference ref = cache.createCacheKey(FAKE_CLASS, FAKE_BYTES); + assertNull(cache.get(ref, FAKE_BYTES)); + cache.put(ref, FAKE_BYTES, FAKE_BYTES); + assertNotNull(cache.get(ref, FAKE_BYTES)); + cache.remove(ref); + assertNull(cache.get(ref, FAKE_BYTES)); + } + +} diff --git a/weaver/src/test/java/org/aspectj/weaver/tools/cache/ZippedFileCacheBackingTest.java b/weaver/src/test/java/org/aspectj/weaver/tools/cache/ZippedFileCacheBackingTest.java new file mode 100644 index 000000000..4c41c1807 --- /dev/null +++ b/weaver/src/test/java/org/aspectj/weaver/tools/cache/ZippedFileCacheBackingTest.java @@ -0,0 +1,154 @@ +/** + * Copyright (c) 2012 VMware, Inc. + * + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Lyor Goldstein + */ + +package org.aspectj.weaver.tools.cache; + +import java.io.File; +import java.io.IOException; +import java.util.Map; +import java.util.TreeMap; + +import org.aspectj.util.FileUtil; +import org.aspectj.weaver.tools.cache.AbstractIndexedFileCacheBacking.IndexEntry; + +/** + * + */ +public class ZippedFileCacheBackingTest extends AsynchronousFileCacheBackingTestSupport { + private File zipTestFile; + + public ZippedFileCacheBackingTest() { + super(); + } + + public void testReadIndex () throws Exception { + IndexEntry[] entries={ + createIgnoredEntry("ignored"), + createIndexEntry("weaved", false, false, bytes, bytes), + createIndexEntry("generated", true, false, bytes, bytes) + }; + File indexFile=getIndexFile(); + writeIndex(indexFile, entries); + + Map entriesMap=new TreeMap(); + for (IndexEntry ie : entries) { + if (ie.ignored) { + continue; + } + + entriesMap.put(ie.key, bytes); + } + + File zipFile=getZipFile(); + ZippedFileCacheBacking.writeZipClassBytes(zipFile, entriesMap); + + File cacheDir=getCacheDir(); + AsynchronousFileCacheBacking cache=createFileBacking(cacheDir); + Map indexMap=cache.getIndexMap(); + assertEquals("Mismatched index size", entries.length, indexMap.size()); + + Map bytesMap=cache.getBytesMap(); + assertEquals("Mismatched bytes size", entriesMap.size() /* the ignored one has no file */, bytesMap.size()); + + for (IndexEntry entry : entries) { + String key=entry.key; + assertNotNull("Missing entry for key=" + key, indexMap.get(key)); + + if (entry.ignored) { + assertNull("Unexpected bytes for ignored key=" + key, bytesMap.get(key)); + } else { + assertArrayEquals("Mismatched contents for key=" + key, bytes, bytesMap.get(key)); + } + } + } + + public void testReadWriteZipClassBytes () throws IOException { + Map entriesMap=new TreeMap(); + for (int index=0; index < Byte.SIZE; index++) { + String name="classBytes#" + index; + random.nextBytes(bytes); + entriesMap.put(name, bytes); + } + + File zipFile=getZipFile(); + ZippedFileCacheBacking.writeZipClassBytes(zipFile, entriesMap); + + Map bytesMap=ZippedFileCacheBacking.readZipClassBytes(zipFile); + assertEquals("Mismatched recovered entries size", entriesMap.size(), bytesMap.size()); + for (Map.Entry bytesEntry : entriesMap.entrySet()) { + String key=bytesEntry.getKey(); + byte[] expected=bytesEntry.getValue(), actual=bytesMap.get(key); + assertArrayEquals("Mismatched data for " + key, expected, actual); + } + } + + public void testReadClassBytes () throws IOException { + IndexEntry[] entries={ + createIgnoredEntry("ignoredReadClassBytes"), + createIndexEntry("weavedReadClassBytes", false, false, bytes, bytes), + createIndexEntry("generatedReadClassBytes", true, false, bytes, bytes) + }; + File indexFile=getIndexFile(); + writeIndex(indexFile, entries); + + long newCrc=generateNewBytes(); + assertTrue("Bad new CRC", newCrc != (-1L)); + + Map entriesMap=new TreeMap(); + for (IndexEntry ie : entries) { + if (ie.ignored) { + continue; + } + + entriesMap.put(ie.key, bytes); + } + + File zipFile=getZipFile(); + ZippedFileCacheBacking.writeZipClassBytes(zipFile, entriesMap); + + File cacheDir=getCacheDir(); + AsynchronousFileCacheBacking cache=createFileBacking(cacheDir); + Map indexMap=cache.getIndexMap(); + assertEquals("Mismatched index size", 1 /* only the ignored entry */, indexMap.size()); + + Map bytesMap=cache.getBytesMap(); + assertEquals("Non empty data bytes", 0, bytesMap.size()); + assertFalse("Zip file not deleted: " + zipFile, zipFile.canRead()); + } + + protected File getZipFile () { + if (zipTestFile == null) { + File cacheDir=getCacheDir(); + zipTestFile = new File(cacheDir, ZippedFileCacheBacking.ZIP_FILE); + } + + return zipTestFile; + } + + @Override + protected void cleanupCache() { + if (zipTestFile != null) { + if (FileUtil.deleteContents(zipTestFile) > 0) { + System.out.println("Deleted " + zipTestFile); + } + zipTestFile = null; + } + + super.cleanupCache(); + } + + @Override + protected ZippedFileCacheBacking createFileBacking(File dir) { + return new ZippedFileCacheBacking(dir); + } +} \ No newline at end of file diff --git a/weaver/src/test/java/reflect/tests/C.java b/weaver/src/test/java/reflect/tests/C.java new file mode 100644 index 000000000..f52043b5a --- /dev/null +++ b/weaver/src/test/java/reflect/tests/C.java @@ -0,0 +1,33 @@ +/* ******************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package reflect.tests; + +/** + * @author colyer + * Part of the testdata for the org.aspectj.weaver.reflect tests + */ +public class C { + + public String foo(Object a) throws Exception { + return null; + } + + private void bar() {} + + public int f; + private String s; +} + +class D extends C implements java.io.Serializable { + public int getNumberOfThingies() { return 0; } + private Object o; +} \ No newline at end of file diff --git a/weaver/src/test/java/test/A.java b/weaver/src/test/java/test/A.java new file mode 100644 index 000000000..12959cd51 --- /dev/null +++ b/weaver/src/test/java/test/A.java @@ -0,0 +1,27 @@ +/* ******************************************************************* + * Copyright (c) 2008 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors + * Andy Clement + * ******************************************************************/ +package test; + +public class A { + public void a(String s) {} + public void b(@A1 String s) {} + public void c(@A1 @A2 String s) {} + public void d(@A1 String s,@A2 String t) {} + + public void e(A1AnnotatedType s) {} + public void f(A2AnnotatedType s) {} + public void g(@A2 A1AnnotatedType s) {} + public void h(@A1 A1AnnotatedType s) {} + public void i(A1AnnotatedType s,@A2 String t) {} + public void j(@A1 @A2 String s) {} + +} diff --git a/weaver/src/test/java/test/A1.java b/weaver/src/test/java/test/A1.java new file mode 100644 index 000000000..616708345 --- /dev/null +++ b/weaver/src/test/java/test/A1.java @@ -0,0 +1,20 @@ +/* ******************************************************************* + * Copyright (c) 2008 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors + * Andy Clement + * ******************************************************************/ +package test; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface A1 { + +} diff --git a/weaver/src/test/java/test/A1AnnotatedType.java b/weaver/src/test/java/test/A1AnnotatedType.java new file mode 100644 index 000000000..e40addbf5 --- /dev/null +++ b/weaver/src/test/java/test/A1AnnotatedType.java @@ -0,0 +1,17 @@ +/* ******************************************************************* + * Copyright (c) 2008 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors + * Andy Clement + * ******************************************************************/ +package test; + +@A1 +public class A1AnnotatedType { + +} diff --git a/weaver/src/test/java/test/A2.java b/weaver/src/test/java/test/A2.java new file mode 100644 index 000000000..48749a3d4 --- /dev/null +++ b/weaver/src/test/java/test/A2.java @@ -0,0 +1,20 @@ +/* ******************************************************************* + * Copyright (c) 2008 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors + * Andy Clement + * ******************************************************************/ +package test; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface A2 { + +} diff --git a/weaver/src/test/java/test/A2AnnotatedType.java b/weaver/src/test/java/test/A2AnnotatedType.java new file mode 100644 index 000000000..0fa3b5c8a --- /dev/null +++ b/weaver/src/test/java/test/A2AnnotatedType.java @@ -0,0 +1,17 @@ +/* ******************************************************************* + * Copyright (c) 2008 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors + * Andy Clement + * ******************************************************************/ +package test; + +@A2 +public class A2AnnotatedType { + +} diff --git a/weaver/src/test/java/test/A3.java b/weaver/src/test/java/test/A3.java new file mode 100644 index 000000000..ab54388ae --- /dev/null +++ b/weaver/src/test/java/test/A3.java @@ -0,0 +1,19 @@ +/* ******************************************************************* + * Copyright (c) 2008 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors + * Andy Clement + * ******************************************************************/ +package test; + +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +public @interface A3 { + Color value() default Color.RED; +} diff --git a/weaver/src/test/java/test/AnnoValues.java b/weaver/src/test/java/test/AnnoValues.java new file mode 100644 index 000000000..08301d1a0 --- /dev/null +++ b/weaver/src/test/java/test/AnnoValues.java @@ -0,0 +1,20 @@ +/* ******************************************************************* + * Copyright (c) 2008 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors + * Andy Clement + * ******************************************************************/ +package test; + +public class AnnoValues { + public void none() {} + @A3 public void defaultMethod() {} + @A3(Color.GREEN) public void greenMethod() {} + @A3(Color.RED) public void redMethod() {} + @A3(Color.BLUE) public void blueMethod() {} +} diff --git a/weaver/src/test/java/test/Color.java b/weaver/src/test/java/test/Color.java new file mode 100644 index 000000000..dea2593ac --- /dev/null +++ b/weaver/src/test/java/test/Color.java @@ -0,0 +1,14 @@ +package test; +/* ******************************************************************* + * Copyright (c) 2008 Contributors + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors + * Andy Clement + * ******************************************************************/ + +public enum Color { RED, GREEN, BLUE } \ No newline at end of file diff --git a/weaver/testdata/logging.properties b/weaver/testdata/logging.properties new file mode 100644 index 000000000..b65bfeaa5 --- /dev/null +++ b/weaver/testdata/logging.properties @@ -0,0 +1,60 @@ +############################################################ +# Default Logging Configuration File +# +# You can use a different file by specifying a filename +# with the java.util.logging.config.file system property. +# For example java -Djava.util.logging.config.file=myfile +############################################################ + +############################################################ +# Global properties +############################################################ + +# "handlers" specifies a comma separated list of log Handler +# classes. These handlers will be installed during VM startup. +# Note that these classes must be on the system classpath. +# By default we only configure a ConsoleHandler, which will only +# show messages at the INFO and above levels. +#handlers= java.util.logging.ConsoleHandler + +# To also add the FileHandler, use the following line instead. +#handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler +handlers= java.util.logging.FileHandler + +# Default global logging level. +# This specifies which kinds of events are logged across +# all loggers. For any given facility this global level +# can be overriden by a facility specific level +# Note that the ConsoleHandler also has a separate level +# setting to limit messages printed to the console. +.level= INFO + +############################################################ +# Handler specific properties. +# Describes specific configuration info for Handlers. +############################################################ + +# default file output is in user's home directory. +java.util.logging.FileHandler.pattern = %h/java%u.log +#java.util.logging.FileHandler.limit = 50000 +java.util.logging.FileHandler.count = 1 +#java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter +java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter +java.util.logging.FileHandler.level = FINER + +# Limit the message that are printed on the console to INFO and above. +java.util.logging.ConsoleHandler.level = FINER +java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter + + +############################################################ +# Facility specific properties. +# Provides extra control for each logger. +############################################################ + +# For example, set the com.xyz.foo logger to only log SEVERE +# messages: +#com.xyz.foo.level = SEVERE +org.aspectj.weaver.level = FINER +org.aspectj.weaver.loadtime.level = FINER +org.aspectj.weaver.weaver.level = FINER diff --git a/weaver/testsrc/fluffy/Aspect.java b/weaver/testsrc/fluffy/Aspect.java deleted file mode 100644 index 401ce9ba8..000000000 --- a/weaver/testsrc/fluffy/Aspect.java +++ /dev/null @@ -1,29 +0,0 @@ -package fluffy; -import org.aspectj.runtime.internal.AroundClosure; - -public class Aspect { - - public static void ignoreMe() {} - - public static void before_method_call() { - System.out.println("before"); - } - - public static void afterReturning_method_call() { - System.out.println("afterReturning"); - } - - public static void afterThrowing_method_execution(Throwable t) { - System.out.println("afterThrowing " + t); - t.printStackTrace(); - } - - public static Object aroundFun(AroundClosure c) { - System.out.println("around"); - try { - return c.run(new Object[0]); - } catch (Throwable t) { - return null; - } - } -} diff --git a/weaver/testsrc/fluffy/Base.java b/weaver/testsrc/fluffy/Base.java deleted file mode 100644 index 4cdb1f772..000000000 --- a/weaver/testsrc/fluffy/Base.java +++ /dev/null @@ -1,18 +0,0 @@ -package fluffy; - -public class Base { - - public static void onlyBase() {} - public static void both() {} - - public void onlyBaseNonStatic() {} - public void bothNonStatic() {} - - public int onlyBase; - public int both; - - public Base() {} - public Base(int i) {} - - public void m() throws CloneNotSupportedException {} -} diff --git a/weaver/testsrc/fluffy/Derived.java b/weaver/testsrc/fluffy/Derived.java deleted file mode 100644 index e6d848cfb..000000000 --- a/weaver/testsrc/fluffy/Derived.java +++ /dev/null @@ -1,22 +0,0 @@ -package fluffy; - -import java.io.IOException; - -import org.aspectj.weaver.testcode.Base; - -public class Derived extends Base { - - public static void onlyDerived() throws IOException, CloneNotSupportedException {} - public static void both() {} - - public void onlyDerivedNonStatic() {} - public void bothNonStatic() {} - - public int onlyDerived; - public int both; - - public Derived() {} - - public void m() {} - -} diff --git a/weaver/testsrc/org/aspectj/weaver/AbstractTraceTest.java b/weaver/testsrc/org/aspectj/weaver/AbstractTraceTest.java deleted file mode 100644 index 334690b62..000000000 --- a/weaver/testsrc/org/aspectj/weaver/AbstractTraceTest.java +++ /dev/null @@ -1,153 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2006 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Matthew Webster - initial implementation - *******************************************************************************/ -package org.aspectj.weaver; - -import java.util.ArrayList; - -import junit.framework.TestCase; - -import org.aspectj.weaver.tools.AbstractTrace; -import org.aspectj.weaver.tools.DefaultTrace; -import org.aspectj.weaver.tools.Traceable; - -public abstract class AbstractTraceTest extends TestCase { - - protected AbstractTrace trace; - - public void testIsTraceEnabled() { - DefaultTrace trace = new DefaultTrace(getClass()); - assertFalse(trace.isTraceEnabled()); - } - - public void testEnterWithThisAndArgs() { - trace.enter("testEnterWithThisAndArgs",this,new Object[] { "arg1", "arg2" }); - } - - public void testEnterWithThisAndArray() { - Object arg1 = new String[] { "s1", "s2" }; - Object arg2 = new char[] { 'a', 'b', 'c' }; - trace.enter(getName(),this,new Object[] { arg1, arg2 }); - } - - public void testEnterWithThisAndCollection() { - Object arg1 = new ArrayList(); - trace.enter(getName(),this,new Object[] { arg1 }); - } - - public void testEnterWithThisAndTraceable () { - Object arg1 = new Traceable() { - - public String toTraceString() { - return getClass().getName() + "[Traceable]"; - } - - }; - trace.enter(getName(),this,new Object[] { arg1 }); - } - - public void testEnterWithThisAndToStringException () { - Object arg1 = new Object() { - - public String toString() { - throw new RuntimeException("toString() can throw an Exception"); - } - - }; - trace.enter(getName(),this,new Object[] { arg1 }); - } - - public void testEnterWithThisAndHashCodeException () { - Object arg1 = new Object() { - - public int hashCode() { - throw new RuntimeException("hashCode can throw an Exception"); - } - - }; - trace.enter(getName(),this,new Object[] { arg1 }); - } - - public void testEnterWithThisAndClassLoader () { - Object arg1 = new ClassLoader() { - - public String toString() { - throw new Error("Don't call ClassLoader.toString()"); - } - - }; - trace.enter(getName(),this,new Object[] { arg1 }); - } - - public void testEnterWithThis() { - trace.enter("testEnterWithThis",this); - } - - public void testEnter() { - trace.enter("testEnter"); - } - - public void testExitWithReturn() { - trace.exit("testExitWithReturn","ret"); - } - - public void testExitWithThrowable() { - trace.exit("testExitWithThrowable",new RuntimeException()); - } - - public void testExit() { - trace.exit("testExit"); - } - - public void testEvent() { - trace.event("testEvent"); - } - - public void testEventWithThisAndArgs() { - trace.event("testEventWithThisAndArgs",this,new Object[] { "arg1", "arg2" }); - } - - public void testEventWithThisAndArg() { - trace.event("testEventWithThisAndArg",this,"arg1"); - } - - public void testDebug() { - trace.debug("debug"); - } - - public void testInfo() { - trace.info("information"); - } - - public void testWarn() { - trace.warn("warning"); - } - - public void testWarnWithException() { - trace.warn("warning",new RuntimeException("warning")); - } - - public void testError() { - trace.error("error"); - } - - public void testErrorWithException() { - trace.error("error",new RuntimeException("error")); - } - - public void testFatal() { - trace.fatal("fatal"); - } - - public void testFatalWithException() { - trace.fatal("fatal",new RuntimeException("fatal")); - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/AllTracingTests.java b/weaver/testsrc/org/aspectj/weaver/AllTracingTests.java deleted file mode 100644 index 81a952903..000000000 --- a/weaver/testsrc/org/aspectj/weaver/AllTracingTests.java +++ /dev/null @@ -1,30 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2006 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Matthew Webster - initial implementation - *******************************************************************************/ -package org.aspectj.weaver; - -import junit.framework.Test; -import junit.framework.TestSuite; - -public class AllTracingTests { - - public static Test suite() { - TestSuite suite = new TestSuite(AllTracingTests.class.getName()); - //$JUnit-BEGIN$ - suite.addTestSuite(TraceFactoryTest.class); - suite.addTestSuite(DefaultTraceFactoryTest.class); - suite.addTestSuite(DefaultTraceTest.class); - suite.addTestSuite(CommonsTraceFactoryTest.class); - suite.addTestSuite(CommonsTraceTest.class); - //$JUnit-END$ - return suite; - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/BcweaverModuleTests.java b/weaver/testsrc/org/aspectj/weaver/BcweaverModuleTests.java deleted file mode 100644 index b0e019bbc..000000000 --- a/weaver/testsrc/org/aspectj/weaver/BcweaverModuleTests.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.aspectj.weaver; - -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - -// default package -import junit.framework.Test; -import junit.framework.TestCase; -import junit.framework.TestSuite; - -import org.aspectj.weaver.tools.ToolsTests; -import org.aspectj.weaver.tools.cache.CacheTests; - -public class BcweaverModuleTests extends TestCase { - - public static Test suite() { - TestSuite suite = new TestSuite(BcweaverModuleTests.class.getName()); - suite.addTest(org.aspectj.weaver.bcel.BcelTests.suite()); - suite.addTest(org.aspectj.weaver.BcweaverTests.suite()); - suite.addTest(org.aspectj.weaver.patterns.bcel.BcelPatternsTests.suite()); - suite.addTestSuite(LocaleTest.class); - suite.addTestSuite(GenericSignatureParserTest.class); - suite.addTest(ToolsTests.suite()); - suite.addTest(CacheTests.suite()); - return suite; - } - - public BcweaverModuleTests(String name) { - super(name); - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/BcweaverTests.java b/weaver/testsrc/org/aspectj/weaver/BcweaverTests.java deleted file mode 100644 index c14b59f32..000000000 --- a/weaver/testsrc/org/aspectj/weaver/BcweaverTests.java +++ /dev/null @@ -1,64 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - -package org.aspectj.weaver; - -import java.io.File; - -import junit.framework.Test; -import junit.framework.TestCase; -import junit.framework.TestSuite; - -import org.aspectj.util.FileUtil; - -public class BcweaverTests extends TestCase { - - public static final String TESTDATA_PATH = "../weaver/testdata"; - public static final String OUTDIR_PATH = "../weaver/out"; - - /** @return File outDir (writable) or null if unable to write */ - public static File getOutdir() { - File result = new File(OUTDIR_PATH); - if (result.mkdirs() || (result.canWrite() && result.isDirectory())) { - return result; - } - return null; - } - - /** best efforts to delete the output directory and any contents */ - public static void removeOutDir() { - File outDir = getOutdir(); - if (null != outDir) { - FileUtil.deleteContents(outDir); - outDir.delete(); - } - } - - public static Test suite() { - TestSuite suite = new TestSuite(BcweaverTests.class.getName()); - // abstract - // suite.addTestSuite(AbstractWorldTestCase.class); - // $JUnit-BEGIN$ - suite.addTestSuite(MemberTestCase.class); - suite.addTestSuite(TypeXTestCase.class); - suite.addTestSuite(WeaverMessagesTestCase.class); - suite.addTestSuite(DumpTestCase.class); - suite.addTest(AllTracingTests.suite()); - // $JUnit-END$ - return suite; - } - - public BcweaverTests(String name) { - super(name); - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/BoundedReferenceTypeTestCase.java b/weaver/testsrc/org/aspectj/weaver/BoundedReferenceTypeTestCase.java deleted file mode 100644 index 403b2ecb1..000000000 --- a/weaver/testsrc/org/aspectj/weaver/BoundedReferenceTypeTestCase.java +++ /dev/null @@ -1,106 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2005 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * Adrian Colyer Initial implementation - * ******************************************************************/ -package org.aspectj.weaver; - -import junit.framework.TestCase; - -import org.aspectj.weaver.bcel.BcelWorld; - -public class BoundedReferenceTypeTestCase extends TestCase { - - ReferenceType javaLangClass; - ReferenceType javaLangObject; - BoundedReferenceType extendsClass; - BoundedReferenceType superClass; - BoundedReferenceType extendsWithExtras; - - public void testSignature() { - String extendsSig = extendsClass.getSignature(); - assertEquals("+Ljava/lang/Class;", extendsSig); - assertEquals("-Ljava/lang/Class;", superClass.getSignature()); - } - - public void testExtendsBounds() { - assertFalse("has no lower bound", extendsClass.hasLowerBound()); - assertNull("no lower bound", extendsClass.getLowerBound()); - assertEquals(javaLangClass, extendsClass.getUpperBound()); - assertEquals("no interface bounds", 0, extendsClass.getAdditionalBounds().length); - } - - public void testSuperBounds() { - assertTrue("has lower bound", superClass.hasLowerBound()); - assertEquals(javaLangClass, superClass.getLowerBound()); - assertEquals("Ljava/lang/Object;", superClass.getUpperBound().getSignature()); - assertEquals("no interface bounds", 0, superClass.getAdditionalBounds().length); - } - - public void testIsExtends() { - assertTrue(extendsClass.kind == BoundedReferenceType.EXTENDS); - assertFalse(superClass.kind == BoundedReferenceType.EXTENDS); - } - - public void testIsSuper() { - assertTrue(superClass.kind == BoundedReferenceType.SUPER); - assertFalse(extendsClass.kind == BoundedReferenceType.SUPER); - } - - public void testGetDeclaredInterfacesNoAdditions() { - ResolvedType[] rt1 = extendsClass.getDeclaredInterfaces(); - ResolvedType[] rt2 = javaLangClass.getDeclaredInterfaces(); - assertEquals("same length", rt1.length, rt2.length); - for (int i = 0; i < rt2.length; i++) { - assertEquals("same methods", rt1[i], rt2[i]); - } - } - - public void testGetDeclaredInterfacesWithInterfaceBounds() { - ResolvedType[] rt1 = extendsWithExtras.getDeclaredInterfaces(); - ResolvedType[] rt2 = javaLangClass.getDeclaredInterfaces(); - assertEquals("one extra interface", rt1.length, rt2.length + 1); - for (int i = 0; i < rt2.length; i++) { - assertEquals("same methods", rt1[i], rt2[i]); - } - assertEquals("Ljava/util/List;", rt1[rt1.length - 1].getSignature()); - } - - // all other methods in signature are delegated to upper bound... - // representative test - public void testGetDeclaredMethodsExtends() { - ResolvedMember[] rm1 = extendsClass.getDeclaredMethods(); - ResolvedMember[] rm2 = javaLangClass.getDeclaredMethods(); - assertEquals("same length", rm1.length, rm2.length); - for (int i = 0; i < rm2.length; i++) { - assertEquals("same methods", rm1[i], rm2[i]); - } - } - - public void testGetDeclaredMethodsSuper() { - ResolvedMember[] rm1 = superClass.getDeclaredMethods(); - ResolvedMember[] rm2 = javaLangObject.getDeclaredMethods(); - assertEquals("same length", rm1.length, rm2.length); - for (int i = 0; i < rm2.length; i++) { - assertEquals("same methods", rm1[i], rm2[i]); - } - } - - @Override - protected void setUp() throws Exception { - super.setUp(); - BcelWorld world = new BcelWorld(); - javaLangClass = (ReferenceType) world.resolve(UnresolvedType.forName("java/lang/Class")); - javaLangObject = (ReferenceType) world.resolve(UnresolvedType.OBJECT); - extendsClass = new BoundedReferenceType(javaLangClass, true, world); - superClass = new BoundedReferenceType(javaLangClass, false, world); - extendsWithExtras = new BoundedReferenceType(javaLangClass, true, world, new ReferenceType[] { (ReferenceType) world - .resolve(UnresolvedType.forName("java/util/List")) }); - } -} diff --git a/weaver/testsrc/org/aspectj/weaver/CommonsTraceFactoryTest.java b/weaver/testsrc/org/aspectj/weaver/CommonsTraceFactoryTest.java deleted file mode 100644 index ecdaf63de..000000000 --- a/weaver/testsrc/org/aspectj/weaver/CommonsTraceFactoryTest.java +++ /dev/null @@ -1,26 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2006 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Matthew Webster - initial implementation - *******************************************************************************/ -package org.aspectj.weaver; - -import junit.framework.TestCase; - -import org.aspectj.weaver.tools.CommonsTraceFactory; -import org.aspectj.weaver.tools.Trace; - -public class CommonsTraceFactoryTest extends TestCase { - - public void testGetTraceFactory() { - CommonsTraceFactory factory = new CommonsTraceFactory(); - Trace trace = factory.getTrace(getClass()); - assertFalse("Tracing should be disbled by default",trace.isTraceEnabled()); - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/CommonsTraceTest.java b/weaver/testsrc/org/aspectj/weaver/CommonsTraceTest.java deleted file mode 100644 index ac6a8cec3..000000000 --- a/weaver/testsrc/org/aspectj/weaver/CommonsTraceTest.java +++ /dev/null @@ -1,35 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2006 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Matthew Webster - initial implementation - *******************************************************************************/ -package org.aspectj.weaver; - -import org.aspectj.weaver.tools.CommonsTrace; - -public class CommonsTraceTest extends AbstractTraceTest { - - protected void setUp() throws Exception { - super.setUp(); - trace = new CommonsTrace(getClass()); - trace.setTraceEnabled(true); - } - - public void testCommonsTrace() { -// CommonsTrace trace = - new CommonsTrace(getClass()); - } - - public void testSetTraceEnabled() { - CommonsTrace trace = new CommonsTrace(getClass()); - trace.setTraceEnabled(true); - /* XXX Need to find out how to turn tracing on */ -// assertTrue(trace.isTraceEnabled()); - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/DefaultTraceFactoryTest.java b/weaver/testsrc/org/aspectj/weaver/DefaultTraceFactoryTest.java deleted file mode 100644 index 16d14bfb6..000000000 --- a/weaver/testsrc/org/aspectj/weaver/DefaultTraceFactoryTest.java +++ /dev/null @@ -1,30 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2006 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Matthew Webster - initial implementation - *******************************************************************************/ -package org.aspectj.weaver; - -import org.aspectj.weaver.tools.DefaultTraceFactory; -import org.aspectj.weaver.tools.Trace; - -import junit.framework.TestCase; - -public class DefaultTraceFactoryTest extends TestCase { - - public void testGetTrace() { - DefaultTraceFactory factory = new DefaultTraceFactory(); - Trace trace = factory.getTrace(getClass()); - assertFalse("Tracing should be disbled by default",trace.isTraceEnabled()); - } - -// public void testIsEnabled() { -// fail("Not yet implemented"); -// } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/DefaultTraceTest.java b/weaver/testsrc/org/aspectj/weaver/DefaultTraceTest.java deleted file mode 100644 index a8313c4fb..000000000 --- a/weaver/testsrc/org/aspectj/weaver/DefaultTraceTest.java +++ /dev/null @@ -1,38 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2006 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Matthew Webster - initial implementation - *******************************************************************************/ -package org.aspectj.weaver; - -import org.aspectj.weaver.tools.DefaultTrace; - -public class DefaultTraceTest extends AbstractTraceTest { - - protected void setUp() throws Exception { - super.setUp(); - trace = new DefaultTrace(getClass()); - trace.setTraceEnabled(true); - } - - public void testDefaultTrace() { -// DefaultTrace trace = - new DefaultTrace(getClass()); - } - - public void testSetTraceEnabled() { - DefaultTrace trace = new DefaultTrace(getClass()); - trace.setTraceEnabled(true); - assertTrue(trace.isTraceEnabled()); - } - - public void testSetPrintStream () { - DefaultTrace trace = new DefaultTrace(getClass()); - trace.setPrintStream(System.out); - } -} diff --git a/weaver/testsrc/org/aspectj/weaver/DumpTestCase.java b/weaver/testsrc/org/aspectj/weaver/DumpTestCase.java deleted file mode 100644 index affd39c2c..000000000 --- a/weaver/testsrc/org/aspectj/weaver/DumpTestCase.java +++ /dev/null @@ -1,147 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Matthew Webster - *******************************************************************************/ -package org.aspectj.weaver; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; - -import junit.framework.TestCase; - -import org.aspectj.bridge.IMessage; -import org.aspectj.bridge.IMessageHolder; -import org.aspectj.bridge.Message; -import org.aspectj.bridge.MessageHandler; - -/** - * @author websterm - * - * Test Dump facility. Ensure it can be configured and files contain expected contents. Testcase - * returns Dump configuration to orginal state. - */ -public class DumpTestCase extends TestCase { - - private File dumpFile; - private IMessage.Kind savedDumpCondition; - - public DumpTestCase(String name) { - super(name); - } - - protected void setUp() throws Exception { - super.setUp(); - - dumpFile = null; - savedDumpCondition = Dump.getDumpOnExit(); - } - - protected void tearDown() throws Exception { - super.tearDown(); - - if (dumpFile != null && dumpFile.exists()) { - boolean deleted = dumpFile.delete(); - assertTrue("Dump file '" + dumpFile.getPath() + "' could not be deleted",deleted); - } - Dump.setDumpOnExit(savedDumpCondition); - } - - public void testSetDumpOnException () { - Dump.setDumpOnException(true); - assertTrue("DumpOnException should be true",Dump.getDumpOnException()); - } - - public void testSetDumpOnExit () { - assertTrue("Should be able to set condition 'error'",Dump.setDumpOnExit("error")); - assertTrue("Should be able to set condition 'warning'",Dump.setDumpOnExit("warning")); - assertFalse("Should not be able to set condition 'junk'",Dump.setDumpOnExit("junk")); - } - - public void testDump () { - String fileName = Dump.dump("testDump()"); - dumpFile = new File(fileName); - assertTrue("Dump file '" + fileName + "' should exist",dumpFile.exists()); - } - - public void testDumpWithException () { - String message = "testDumpWithException()"; - String fileName = recursiveCall(message,100); - dumpFile = new File(fileName); - assertContents(dumpFile,"Exception Information",message); - } - - public void testDumpOnExit () { - Dump.setDumpOnExit("abort"); - Dump.saveMessageHolder(null); - String fileName = Dump.dumpOnExit(); - dumpFile = new File(fileName); - assertTrue("Dump file '" + fileName + "' should exist",dumpFile.exists()); - } - - public void testDumpOnExitExcluded () { - Dump.setDumpOnExit("abort"); - IMessageHolder holder = new MessageHandler(); - Dump.saveMessageHolder(holder); - holder.handleMessage(new Message("testDumpOnExitExcluded()",IMessage.ERROR,null,null)); - String fileName = Dump.dumpOnExit(); - dumpFile = new File(fileName); - assertEquals("Dump '" + fileName + "' should be excluded",Dump.DUMP_EXCLUDED,fileName); - } - - public void testDumpOnExitIncluded () { - Dump.setDumpOnExit("error"); - IMessageHolder holder = new MessageHandler(); - Dump.saveMessageHolder(holder); - IMessage error = new Message("testDumpOnExitIncluded()",IMessage.ERROR,null,null); - holder.handleMessage(error); - String fileName = Dump.dumpOnExit(); - dumpFile = new File(fileName); - assertContents(dumpFile,"Compiler Messages",error.getMessage()); - } - - /* Ensure dump file exists and contains certain contents under a given heading */ - public static void assertContents (File dumpFile, String heading, String contents) { - assertTrue("Dump file '" + dumpFile.getPath() + "' should exist",dumpFile.exists()); - assertTrue("Dump file '" + dumpFile.getPath()+ "' should contain '" + contents + "'",fileContains(dumpFile,heading,contents)); - } - - private static boolean fileContains (File dumpFile, String heading, String contents) { - boolean result = false; - - try { - BufferedReader reader = new BufferedReader(new FileReader(dumpFile)); - String currentHeading = ""; - String record; - while ((null != (record = reader.readLine())) && (result == false)) { - if (record.startsWith("----")) currentHeading = record; - else if ((record.indexOf(contents) != -1) && currentHeading.indexOf(heading) != -1) result = true; - } - reader.close(); - } - catch (IOException ex) { - fail(ex.toString()); - } - - return result; - } - - /* Generate a big stack trace */ - private String recursiveCall (String message, int depth) { - if (depth == 0) { - Throwable th = new RuntimeException(message); - return Dump.dumpWithException(th); - } - else { - return recursiveCall(message,--depth); - } - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/GenericSignatureParserTest.java b/weaver/testsrc/org/aspectj/weaver/GenericSignatureParserTest.java deleted file mode 100644 index e5948eb40..000000000 --- a/weaver/testsrc/org/aspectj/weaver/GenericSignatureParserTest.java +++ /dev/null @@ -1,63 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 1999-2001 Xerox Corporation, - * 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * ******************************************************************/ - package org.aspectj.weaver; - -import junit.framework.TestCase; - -import org.aspectj.apache.bcel.classfile.JavaClass; -import org.aspectj.apache.bcel.classfile.Method; -import org.aspectj.apache.bcel.util.SyntheticRepository; -import org.aspectj.util.GenericSignatureParser; - -/** - * @author Adrian Colyer - * @author Andy Clement - */ -public class GenericSignatureParserTest extends TestCase { - - GenericSignatureParser parser; - - protected void setUp() throws Exception { - super.setUp(); - parser = new GenericSignatureParser(); - } - - public void testClassSignatureParsingInJDK() throws Exception { - SyntheticRepository repository = SyntheticRepository.getInstance(); - String[] testClasses = new String[] { "java.lang.Comparable", "java.lang.Iterable", "java.lang.Class", "java.lang.Enum", - "java.lang.InheritableThreadLocal", "java.lang.ThreadLocal", "java.util.Collection", "java.util.Comparator", - "java.util.Enumeration", "java.util.Iterator", "java.util.List", "java.util.ListIterator", "java.util.Map", - "java.util.Map$Entry", "java.util.Queue", "java.util.Set", "java.util.SortedMap", "java.util.SortedSet" }; - for (int i = 0; i < testClasses.length; i++) { - JavaClass jc = repository.loadClass(testClasses[i]); - String sig = jc.getGenericSignature(); - parser.parseAsClassSignature(sig); - } - } - - public void testMethodSignatureParsingInJDK() throws Exception { - SyntheticRepository repository = SyntheticRepository.getInstance(); - String[] testClasses = new String[] { "java.lang.Comparable", "java.lang.Iterable", "java.lang.Class", "java.lang.Enum", - "java.lang.InheritableThreadLocal", "java.lang.ThreadLocal", "java.util.Collection", "java.util.Comparator", - "java.util.Enumeration", "java.util.Iterator", "java.util.List", "java.util.ListIterator", "java.util.Map", - "java.util.Map$Entry", "java.util.Queue", "java.util.Set", "java.util.SortedMap", "java.util.SortedSet" }; - for (int i = 0; i < testClasses.length; i++) { - JavaClass jc = repository.loadClass(testClasses[i]); - Method[] methods = jc.getMethods(); - for (int j = 0; j < methods.length; j++) { - String sig = methods[j].getGenericSignature(); - if (sig != null) - parser.parseAsMethodSignature(sig); - } - } - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/LocaleTest.java b/weaver/testsrc/org/aspectj/weaver/LocaleTest.java deleted file mode 100644 index e69a4df93..000000000 --- a/weaver/testsrc/org/aspectj/weaver/LocaleTest.java +++ /dev/null @@ -1,53 +0,0 @@ -/* Copyright (c) 2002 Contributors. - * - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - */ -package org.aspectj.weaver; - -import java.io.IOException; -import java.util.Locale; - -import junit.framework.TestCase; - -import org.aspectj.apache.bcel.generic.Instruction; -import org.aspectj.apache.bcel.util.ByteSequence; - -public class LocaleTest extends TestCase { - - public LocaleTest(String name) { - super(name); - } - - public void testNormalLocale() { - doBipush(); - } - - public void testTurkishLocale() { - Locale def = Locale.getDefault(); - Locale.setDefault(new Locale("tr", "")); - try { - doBipush(); - } finally { - Locale.setDefault(def); - } - } - - private static void doBipush() { - try { - Instruction.readInstruction( - new ByteSequence(new byte[] { - (byte)16, // bipush - (byte) 3 // data for bipush - })); - } catch (IOException e) { - throw new RuntimeException(e.getMessage()); - } - } -} - diff --git a/weaver/testsrc/org/aspectj/weaver/MemberTestCase.java b/weaver/testsrc/org/aspectj/weaver/MemberTestCase.java deleted file mode 100644 index c4755a3db..000000000 --- a/weaver/testsrc/org/aspectj/weaver/MemberTestCase.java +++ /dev/null @@ -1,183 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * 2005 contributors - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * Adrian Colyer, canBeParameterized tests - * ******************************************************************/ - -package org.aspectj.weaver; - -import java.lang.reflect.Modifier; - -import junit.framework.TestCase; - -import org.aspectj.testing.util.TestUtil; - -/** - * This is a test case for all the portions of Member that don't require a world. - */ -public class MemberTestCase extends TestCase { - - public MemberTestCase(String name) { - super(name); - } - - public void testMethodConstruction() { - Member s = TestUtils.methodFromString("void Foo.goo(int)"); - Member t = MemberImpl.method(UnresolvedType.forName("Foo"), 0, "goo", "(I)V"); - Member u = TestUtils.methodFromString("void Foo1.goo(int)"); - Member v = TestUtils.methodFromString("int Foo.goo(int)"); - - TestUtil.assertCommutativeEquals(s, s, true); - TestUtil.assertCommutativeEquals(t, t, true); - TestUtil.assertCommutativeEquals(u, u, true); - TestUtil.assertCommutativeEquals(v, v, true); - TestUtil.assertCommutativeEquals(s, t, true); - TestUtil.assertCommutativeEquals(s, u, false); - TestUtil.assertCommutativeEquals(s, v, false); - TestUtil.assertCommutativeEquals(t, u, false); - TestUtil.assertCommutativeEquals(t, v, false); - TestUtil.assertCommutativeEquals(u, v, false); - - s = TestUtils.fieldFromString("int Foo.goo"); - t = MemberImpl.field("Foo", 0, "goo", "I"); - u = TestUtils.fieldFromString("int Foo.goo1"); - v = TestUtils.fieldFromString("long Foo.goo"); - - TestUtil.assertCommutativeEquals(s, s, true); - TestUtil.assertCommutativeEquals(t, t, true); - TestUtil.assertCommutativeEquals(u, u, true); - TestUtil.assertCommutativeEquals(v, v, true); - TestUtil.assertCommutativeEquals(s, t, true); - TestUtil.assertCommutativeEquals(s, u, false); - TestUtil.assertCommutativeEquals(s, v, false); - TestUtil.assertCommutativeEquals(t, u, false); - TestUtil.assertCommutativeEquals(t, v, false); - TestUtil.assertCommutativeEquals(u, v, false); - } - - public void testMethodContents() { - Member m = TestUtils.methodFromString("void Foo.goo(int)"); - kindTest(m, Member.METHOD); - declaringTypeTest(m, "Foo"); - nameTest(m, "goo"); - parameterTypesTest(m, new UnresolvedType[] { UnresolvedType.INT }); - returnTypeTest(m, UnresolvedType.VOID); - isInterfaceTest(m, false); - isPrivateTest(m, false); - isConstructorTest(m, false); - isStaticTest(m, false); - - m = TestUtils.methodFromString("interface java.lang.Object java.util.Iterator.next()"); - kindTest(m, Member.METHOD); - declaringTypeTest(m, "java.util.Iterator"); - nameTest(m, "next"); - parameterTypesTest(m, UnresolvedType.NONE); - returnTypeTest(m, UnresolvedType.OBJECT); - isInterfaceTest(m, true); - isPrivateTest(m, false); - isConstructorTest(m, false); - isStaticTest(m, false); - - m = TestUtils.methodFromString("void Foo.(int, java.lang.Object)"); - kindTest(m, Member.CONSTRUCTOR); - declaringTypeTest(m, "Foo"); - nameTest(m, ""); - parameterTypesTest(m, new UnresolvedType[] { UnresolvedType.INT, UnresolvedType.OBJECT }); - returnTypeTest(m, UnresolvedType.VOID); - isInterfaceTest(m, false); - isPrivateTest(m, false); - isConstructorTest(m, true); - isStaticTest(m, false); - - m = TestUtils.methodFromString("private double Foo.sqrt(double)"); - kindTest(m, Member.METHOD); - declaringTypeTest(m, "Foo"); - nameTest(m, "sqrt"); - parameterTypesTest(m, new UnresolvedType[] { UnresolvedType.DOUBLE }); - returnTypeTest(m, UnresolvedType.DOUBLE); - isInterfaceTest(m, false); - isPrivateTest(m, true); - isConstructorTest(m, false); - isStaticTest(m, false); - - m = TestUtils.methodFromString("static int java.lang.Math.max(int, int)"); - kindTest(m, Member.METHOD); - declaringTypeTest(m, "java.lang.Math"); - nameTest(m, "max"); - parameterTypesTest(m, new UnresolvedType[] { UnresolvedType.INT, UnresolvedType.INT }); - returnTypeTest(m, UnresolvedType.INT); - isInterfaceTest(m, false); - isPrivateTest(m, false); - isConstructorTest(m, false); - isStaticTest(m, true); - } - - public void testFieldContents() { - Member m = TestUtils.fieldFromString("int Foo.goo"); - kindTest(m, Member.FIELD); - declaringTypeTest(m, "Foo"); - nameTest(m, "goo"); - parameterTypesTest(m, UnresolvedType.NONE); - returnTypeTest(m, UnresolvedType.INT); - isInterfaceTest(m, false); - isPrivateTest(m, false); - isConstructorTest(m, false); - isStaticTest(m, false); - - m = TestUtils.fieldFromString("static java.util.Iterator goo.Bar.i"); - kindTest(m, Member.FIELD); - declaringTypeTest(m, "goo.Bar"); - nameTest(m, "i"); - parameterTypesTest(m, UnresolvedType.NONE); - returnTypeTest(m, UnresolvedType.forName("java.util.Iterator")); - isInterfaceTest(m, false); - isPrivateTest(m, false); - isConstructorTest(m, false); - isStaticTest(m, true); - } - - private void isStaticTest(Member m, boolean b) { - assertEquals(m + " is static", b, Modifier.isStatic(m.getModifiers())); - } - - private void isConstructorTest(Member m, boolean b) { - assertEquals(m + " is constructor", b, m.getKind() == Member.CONSTRUCTOR); - } - - private void isPrivateTest(Member m, boolean b) { - assertEquals(m + " is private", b, Modifier.isPrivate(m.getModifiers())); - } - - private void isInterfaceTest(Member m, boolean b) { - assertEquals(m + " is interface", b, Modifier.isInterface(m.getModifiers())); - } - - private void returnTypeTest(Member m, UnresolvedType returnType) { - assertEquals(m + " return type", returnType, m.getReturnType()); - } - - private void parameterTypesTest(Member m, UnresolvedType[] paramTypes) { - TestUtil.assertArrayEquals(m + " parameters", paramTypes, m.getParameterTypes()); - } - - private void nameTest(Member m, String name) { - assertEquals(m + " name", name, m.getName()); - } - - private void declaringTypeTest(Member m, String declaringName) { - assertEquals(m + " declared in", UnresolvedType.forName(declaringName), m.getDeclaringType()); - } - - private void kindTest(Member m, MemberKind kind) { - assertEquals(m + " kind", kind, m.getKind()); - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/MemberTestCase15.java b/weaver/testsrc/org/aspectj/weaver/MemberTestCase15.java deleted file mode 100644 index 8014289e7..000000000 --- a/weaver/testsrc/org/aspectj/weaver/MemberTestCase15.java +++ /dev/null @@ -1,81 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2005 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * Adrian Colyer Initial implementation - * ******************************************************************/ -package org.aspectj.weaver; - -import org.aspectj.weaver.bcel.BcelWorld; - -import junit.framework.TestCase; - -/** - * @author colyer - * - */ -public class MemberTestCase15 extends TestCase { - - public void testCanBeParameterizedRegularMethod() { - BcelWorld world = new BcelWorld(); - ResolvedType javaLangClass = world.resolve(UnresolvedType.forName("java/lang/Class")); - ResolvedMember[] methods = javaLangClass.getDeclaredMethods(); - ResolvedMember getAnnotations = null; - for (int i = 0; i < methods.length; i++) { - if (methods[i].getName().equals("getAnnotations")) { - getAnnotations = methods[i]; - break; - } - } - if (getAnnotations != null) { // so can run on non-Java 5 -// System.out.println("got it"); - assertFalse(getAnnotations.canBeParameterized()); - } - } - - public void testCanBeParameterizedGenericMethod() { - BcelWorld world = new BcelWorld(); - world.setBehaveInJava5Way(true); - ResolvedType javaLangClass = world.resolve(UnresolvedType.forName("java.lang.Class")); - javaLangClass = javaLangClass.getGenericType(); - if (javaLangClass == null) return; // for < 1.5 - ResolvedMember[] methods = javaLangClass.getDeclaredMethods(); - ResolvedMember asSubclass = null; - for (int i = 0; i < methods.length; i++) { - if (methods[i].getName().equals("asSubclass")) { - asSubclass = methods[i]; - break; - } - } - if (asSubclass != null) { // so can run on non-Java 5 -// System.out.println("got it"); - assertTrue(asSubclass.canBeParameterized()); - } - } - - public void testCanBeParameterizedMethodInGenericType() { - BcelWorld world = new BcelWorld(); - world.setBehaveInJava5Way(true); - ResolvedType javaUtilList = world.resolve(UnresolvedType.forName("java.util.List")); - javaUtilList = javaUtilList.getGenericType(); - if (javaUtilList == null) return; // for < 1.5 - ResolvedMember[] methods = javaUtilList.getDeclaredMethods(); - ResolvedMember add = null; - for (int i = 0; i < methods.length; i++) { - if (methods[i].getName().equals("add")) { - add = methods[i]; - break; - } - } - if (add != null) { // so can run on non-Java 5 -// System.out.println("got it"); - assertTrue(add.canBeParameterized()); - } - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/ParameterizedReferenceTypeTestCase.java b/weaver/testsrc/org/aspectj/weaver/ParameterizedReferenceTypeTestCase.java deleted file mode 100644 index 77379318d..000000000 --- a/weaver/testsrc/org/aspectj/weaver/ParameterizedReferenceTypeTestCase.java +++ /dev/null @@ -1,81 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2005 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * Adrian Colyer Initial implementation - * ******************************************************************/ -package org.aspectj.weaver; - -import org.aspectj.weaver.bcel.BcelWorld; - -import junit.framework.TestCase; - -/** - * @author colyer - * For a parameterized reference type, the methods that return members - * - getDeclaredFields - * - getDeclaredMethods - * - getDeclaredInterfaces - * - getDeclaredPointcuts - * should have any type variables substituted by the given type parameter before - * being returned. - */ -public class ParameterizedReferenceTypeTestCase extends TestCase { - - BcelWorld world; - ReferenceType listOfString; - - public void testDeclaredMethodWithParameter() { - ResolvedMember[] methods = listOfString.getDeclaredMethods(); - ResolvedMember add = null; - for (int i = 0; i < methods.length; i++) { - if (methods[i].getName().equals("add")) { - if (methods[i].getParameterTypes().length == 1) { - add = methods[i]; - break; - } - } - } - UnresolvedType parameterType = add.getParameterTypes()[0]; - assertEquals("Ljava/lang/String;",parameterType.getSignature()); - - ResolvedMember get = null; - for (int i = 0; i < methods.length; i++) { - if (methods[i].getName().equals("get")) { - if (methods[i].getParameterTypes().length == 1) { - get = methods[i]; - break; - } - } - } - UnresolvedType returnType = get.getReturnType(); - assertEquals("Ljava/lang/String;",returnType.getSignature()); - - } - - public void testDeclaredMethodWithParameterizedReturnType() { - ResolvedMember[] methods = listOfString.getDeclaredMethods(); - ResolvedMember iterator = null; - for (int i = 0; i < methods.length; i++) { - if (methods[i].getName().equals("iterator")) { - iterator = methods[i]; - break; - } - } - UnresolvedType returnType = iterator.getReturnType(); - assertEquals("Pjava/util/Iterator;",returnType.getSignature()); - - } - - protected void setUp() throws Exception { - super.setUp(); - world = new BcelWorld(); - listOfString = (ReferenceType) - TypeFactory.createTypeFromSignature("Pjava/util/List;").resolve(world); - } -} diff --git a/weaver/testsrc/org/aspectj/weaver/ResolvedMemberSignaturesTestCase15.java b/weaver/testsrc/org/aspectj/weaver/ResolvedMemberSignaturesTestCase15.java deleted file mode 100644 index 95ce63e3f..000000000 --- a/weaver/testsrc/org/aspectj/weaver/ResolvedMemberSignaturesTestCase15.java +++ /dev/null @@ -1,286 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2005 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * Adrian Colyer Initial implementation - * ******************************************************************/ -package org.aspectj.weaver; - -import java.lang.reflect.Modifier; - -import org.aspectj.weaver.bcel.BcelWorld; - -import junit.framework.TestCase; - -public class ResolvedMemberSignaturesTestCase15 extends TestCase { - - World world; - UnresolvedType baseType; - UnresolvedType derivedType; - - // STATIC METHODS - - public void testBaseOnlyStaticMethod() { - Member toFind = new MemberImpl(Member.METHOD,baseType, - (Modifier.PUBLIC | Modifier.STATIC), - UnresolvedType.forSignature("V"), - "onlyBase", - new UnresolvedType[0] - ); - ResolvedMember[] foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); - assertEquals("found 1 member",1,foundMembers.length); - assertEquals("Lfluffy/Base;",foundMembers[0].getDeclaringType().getSignature()); - - toFind = new MemberImpl(Member.METHOD,derivedType, - (Modifier.PUBLIC | Modifier.STATIC), - UnresolvedType.forSignature("V"), - "onlyBase", - new UnresolvedType[0] - ); - foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); - // this looks odd but we need both because of the way calls to inherited static methods - // are rendered in bytecode when written as obj.foo(); - the bytecode says it is a call - // to obj.getClass().foo() even if the static method is defined in a super type. - assertEquals("found 2 members",2,foundMembers.length); - assertEquals("Lfluffy/Derived;",foundMembers[0].getDeclaringType().getSignature()); - assertEquals("Lfluffy/Base;",foundMembers[1].getDeclaringType().getSignature()); - } - - public void testBothStaticMethod() { - Member toFind = new MemberImpl(Member.METHOD,baseType, - (Modifier.PUBLIC | Modifier.STATIC), - UnresolvedType.forSignature("V"), - "both", - new UnresolvedType[0] - ); - ResolvedMember[] foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); - assertEquals("found 1 member",1,foundMembers.length); - assertEquals("Lfluffy/Base;",foundMembers[0].getDeclaringType().getSignature()); - - toFind = new MemberImpl(Member.METHOD,derivedType, - (Modifier.PUBLIC | Modifier.STATIC), - UnresolvedType.forSignature("V"), - "both", - new UnresolvedType[0] - ); - foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); - assertEquals("found 1 members",1,foundMembers.length); - assertEquals("Lfluffy/Derived;",foundMembers[0].getDeclaringType().getSignature()); - } - - public void testDerivedStaticMethod() { - Member toFind = new MemberImpl(Member.METHOD,baseType, - (Modifier.PUBLIC | Modifier.STATIC), - UnresolvedType.forSignature("V"), - "onlyDerived", - new UnresolvedType[0] - ); - - ResolvedMember[] foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); - assertEquals("found nothing",0,foundMembers.length); - - toFind = new MemberImpl(Member.METHOD,derivedType, - (Modifier.PUBLIC | Modifier.STATIC), - UnresolvedType.forSignature("V"), - "onlyDerived", - new UnresolvedType[0] - ); - foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); - assertEquals("found 1 members",1,foundMembers.length); - assertEquals("Lfluffy/Derived;",foundMembers[0].getDeclaringType().getSignature()); - } - - // NON-STATIC METHODS - - public void testBaseOnlyMethod() { - Member toFind = new MemberImpl(Member.METHOD,baseType, - Modifier.PUBLIC, - UnresolvedType.forSignature("V"), - "onlyBaseNonStatic", - new UnresolvedType[0] - ); - ResolvedMember[] foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); - assertEquals("found 1 member",1,foundMembers.length); - assertEquals("Lfluffy/Base;",foundMembers[0].getDeclaringType().getSignature()); - - toFind = new MemberImpl(Member.METHOD,derivedType, - Modifier.PUBLIC, - UnresolvedType.forSignature("V"), - "onlyBaseNonStatic", - new UnresolvedType[0] - ); - foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); - assertEquals("found 2 members",2,foundMembers.length); - assertEquals("Lfluffy/Derived;",foundMembers[0].getDeclaringType().getSignature()); - assertEquals("Lfluffy/Base;",foundMembers[1].getDeclaringType().getSignature()); - - } - - public void testBothMethod() { - Member toFind = new MemberImpl(Member.METHOD,baseType, - Modifier.PUBLIC, - UnresolvedType.forSignature("V"), - "bothNonStatic", - new UnresolvedType[0] - ); - ResolvedMember[] foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); - assertEquals("found 1 member",1,foundMembers.length); - assertEquals("Lfluffy/Base;",foundMembers[0].getDeclaringType().getSignature()); - - toFind = new MemberImpl(Member.METHOD,derivedType, - Modifier.PUBLIC, - UnresolvedType.forSignature("V"), - "bothNonStatic", - new UnresolvedType[0] - ); - foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); - assertEquals("found 2 members",2,foundMembers.length); - assertEquals("Lfluffy/Derived;",foundMembers[0].getDeclaringType().getSignature()); - assertEquals("Lfluffy/Base;",foundMembers[1].getDeclaringType().getSignature()); - } - - public void testDerivedMethod() { - Member toFind = new MemberImpl(Member.METHOD,baseType, - Modifier.PUBLIC, - UnresolvedType.forSignature("V"), - "onlyDerivedNonStatic", - new UnresolvedType[0] - ); - - ResolvedMember[] foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); - assertEquals("found nothing",0,foundMembers.length); - - toFind = new MemberImpl(Member.METHOD,derivedType, - Modifier.PUBLIC, - UnresolvedType.forSignature("V"), - "onlyDerivedNonStatic", - new UnresolvedType[0] - ); - foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); - assertEquals("found 1 members",1,foundMembers.length); - assertEquals("Lfluffy/Derived;",foundMembers[0].getDeclaringType().getSignature()); - } - - public void testChangingThrowsClause() { - Member toFind = new MemberImpl(Member.METHOD,derivedType, - Modifier.PUBLIC, - UnresolvedType.forSignature("V"), - "m", - new UnresolvedType[0] - ); - - ResolvedMember[] foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); - assertEquals("found 2 members",2,foundMembers.length); - assertEquals("Lfluffy/Derived;",foundMembers[0].getDeclaringType().getSignature()); - assertEquals("Lfluffy/Base;",foundMembers[1].getDeclaringType().getSignature()); - - assertEquals("throws CloneNotSupported",1,foundMembers[1].getExceptions().length); - assertEquals("doesn't throw anything",0,foundMembers[0].getExceptions().length); - } - - // CONSTRUCTORS - - public void testNoWalkUpMatchingConstructor() { - Member toFind = new MemberImpl(Member.CONSTRUCTOR,derivedType, - Modifier.PUBLIC, - UnresolvedType.forSignature("V"), - "", - new UnresolvedType[0] - ); - ResolvedMember[] foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); - assertEquals("found 1 members",1,foundMembers.length); - assertEquals("Lfluffy/Derived;",foundMembers[0].getDeclaringType().getSignature()); - } - - public void testNoWalkUpNoMatchingConstructor() { - Member toFind = new MemberImpl(Member.CONSTRUCTOR,derivedType, - Modifier.PUBLIC, - UnresolvedType.forSignature("V"), - "", - new UnresolvedType[] {UnresolvedType.forSignature("I")} - ); - ResolvedMember[] foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); - assertEquals("No matches",0,foundMembers.length); - } - - // FIELDS - - public void testBaseOnlyField() { - Member toFind = new MemberImpl(Member.FIELD,baseType, - Modifier.PUBLIC, - UnresolvedType.forSignature("I"), - "onlyBase", - new UnresolvedType[0] - ); - ResolvedMember[] foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); - assertEquals("found 1 member",1,foundMembers.length); - assertEquals("Lfluffy/Base;",foundMembers[0].getDeclaringType().getSignature()); - - toFind = new MemberImpl(Member.FIELD,derivedType, - Modifier.PUBLIC, - UnresolvedType.forSignature("I"), - "onlyBase", - new UnresolvedType[0] - ); - foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); - assertEquals("found 2 members",2,foundMembers.length); - assertEquals("Lfluffy/Derived;",foundMembers[0].getDeclaringType().getSignature()); - assertEquals("Lfluffy/Base;",foundMembers[1].getDeclaringType().getSignature()); - } - - public void testBothField() { - Member toFind = new MemberImpl(Member.FIELD,baseType, - Modifier.PUBLIC, - UnresolvedType.forSignature("I"), - "both", - new UnresolvedType[0] - ); - ResolvedMember[] foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); - assertEquals("found 1 member",1,foundMembers.length); - assertEquals("Lfluffy/Base;",foundMembers[0].getDeclaringType().getSignature()); - - toFind = new MemberImpl(Member.FIELD,derivedType, - Modifier.PUBLIC, - UnresolvedType.forSignature("I"), - "both", - new UnresolvedType[0] - ); - foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); - assertEquals("found 1 members",1,foundMembers.length); - assertEquals("Lfluffy/Derived;",foundMembers[0].getDeclaringType().getSignature()); - } - - public void testDerivedField() { - Member toFind = new MemberImpl(Member.FIELD,baseType, - Modifier.PUBLIC, - UnresolvedType.forSignature("I"), - "onlyDerived", - new UnresolvedType[0] - ); - - ResolvedMember[] foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); - assertEquals("found nothing",0,foundMembers.length); - - toFind = new MemberImpl(Member.FIELD,derivedType, - Modifier.PUBLIC, - UnresolvedType.forSignature("I"), - "onlyDerived", - new UnresolvedType[0] - ); - foundMembers = ResolvedMemberImpl.getJoinPointSignatures(toFind, world); - assertEquals("found 1 members",1,foundMembers.length); - assertEquals("Lfluffy/Derived;",foundMembers[0].getDeclaringType().getSignature()); - } - - protected void setUp() throws Exception { - world = new BcelWorld(); - baseType = UnresolvedType.forSignature("Lfluffy/Base;"); - derivedType = UnresolvedType.forSignature("Lfluffy/Derived;"); - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/TestShadow.java b/weaver/testsrc/org/aspectj/weaver/TestShadow.java deleted file mode 100644 index 2cdfd84bf..000000000 --- a/weaver/testsrc/org/aspectj/weaver/TestShadow.java +++ /dev/null @@ -1,131 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - -package org.aspectj.weaver; - -import org.aspectj.bridge.ISourceLocation; -import org.aspectj.weaver.ast.Var; - -public class TestShadow extends Shadow { - - private final World world; - private final UnresolvedType thisType; - - public TestShadow(Kind kind, Member signature, UnresolvedType thisType, World world) { - super(kind, signature, null); - this.world = world; - this.thisType = thisType; - } - - public World getIWorld() { - return world; - } - - /** this is subtly wrong. ha ha */ - public UnresolvedType getEnclosingType() { - return thisType; - } - - public Var getThisVar() { - // we should thorw if we don't have a this - return new Var(getThisType().resolve(world)); - } - - public Var getTargetVar() { - if (!hasTarget()) - throw new RuntimeException("bad"); - return new Var(getTargetType().resolve(world)); - } - - public Var getArgVar(int i) { - return new Var(getArgType(i).resolve(world)); - } - - public Var getThisEnclosingJoinPointStaticPartVar() { - throw new RuntimeException("unimplemented"); - } - - public Var getThisJoinPointStaticPartVar() { - throw new RuntimeException("unimplemented"); - } - - public Var getThisAspectInstanceVar(ResolvedType aspectType) { - throw new RuntimeException("unimplemented"); - } - - public Var getThisJoinPointVar() { - throw new RuntimeException("unimplemented"); - } - - public ISourceLocation getSourceLocation() { - throw new RuntimeException("unimplemented"); - } - - public Member getEnclosingCodeSignature() { - throw new RuntimeException("unimplemented"); - } - - /* - * (non-Javadoc) - * - * @see org.aspectj.weaver.Shadow#getKindedAnnotationVar() - */ - public Var getKindedAnnotationVar(UnresolvedType annotationType) { - throw new RuntimeException("unimplemented"); - } - - /* - * (non-Javadoc) - * - * @see org.aspectj.weaver.Shadow#getWithinAnnotationVar() - */ - public Var getWithinAnnotationVar(UnresolvedType annotationType) { - throw new RuntimeException("unimplemented"); - } - - /* - * (non-Javadoc) - * - * @see org.aspectj.weaver.Shadow#getWithinCodeAnnotationVar() - */ - public Var getWithinCodeAnnotationVar(UnresolvedType annotationType) { - throw new RuntimeException("unimplemented"); - } - - /* - * (non-Javadoc) - * - * @see org.aspectj.weaver.Shadow#getThisAnnotationVar() - */ - public Var getThisAnnotationVar(UnresolvedType annotationType) { - throw new RuntimeException("unimplemented"); - } - - /* - * (non-Javadoc) - * - * @see org.aspectj.weaver.Shadow#getTargetAnnotationVar() - */ - public Var getTargetAnnotationVar(UnresolvedType annotationType) { - throw new RuntimeException("unimplemented"); - } - - /* - * (non-Javadoc) - * - * @see org.aspectj.weaver.Shadow#getArgAnnotationVar(int) - */ - public Var getArgAnnotationVar(int i, UnresolvedType annotationType) { - throw new RuntimeException("unimplemented"); - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/TraceFactoryTest.java b/weaver/testsrc/org/aspectj/weaver/TraceFactoryTest.java deleted file mode 100644 index 24ec997fe..000000000 --- a/weaver/testsrc/org/aspectj/weaver/TraceFactoryTest.java +++ /dev/null @@ -1,31 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2006 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Matthew Webster - initial implementation - *******************************************************************************/ -package org.aspectj.weaver; - -import org.aspectj.weaver.tools.Trace; -import org.aspectj.weaver.tools.TraceFactory; - -import junit.framework.TestCase; - -public class TraceFactoryTest extends TestCase { - - public void testGetTraceFactory() { - TraceFactory traceFactory = TraceFactory.getTraceFactory(); - assertNotNull(traceFactory); - } - - public void testGetTrace() { - TraceFactory traceFactory = TraceFactory.getTraceFactory(); - Trace trace = traceFactory.getTrace(getClass()); - assertNotNull(trace); - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/TypeVariableReferenceTypeTestCase.java b/weaver/testsrc/org/aspectj/weaver/TypeVariableReferenceTypeTestCase.java deleted file mode 100644 index 9e4985491..000000000 --- a/weaver/testsrc/org/aspectj/weaver/TypeVariableReferenceTypeTestCase.java +++ /dev/null @@ -1,50 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2005 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * Adrian Colyer Initial implementation - * ******************************************************************/ -package org.aspectj.weaver; - -import junit.framework.TestCase; - -import org.aspectj.weaver.bcel.BcelWorld; - -/** - * @author colyer - * - */ -public class TypeVariableReferenceTypeTestCase extends TestCase { - - ReferenceType javaLangClass; - ReferenceType javaLangObject; - BoundedReferenceType extendsClass; - BoundedReferenceType superClass; - BoundedReferenceType extendsWithExtras; - BcelWorld world; - - public void testConstructionByNameAndVariable() { - TypeVariable tv = new TypeVariable("T", javaLangClass); - TypeVariableReferenceType tvrt = new TypeVariableReferenceType(tv, world); - assertEquals("T", tvrt.getTypeVariable().getName()); - assertEquals(javaLangClass, tvrt.getTypeVariable().getUpperBound()); - } - - @Override - protected void setUp() throws Exception { - super.setUp(); - world = new BcelWorld(); - javaLangClass = (ReferenceType) world.resolve(UnresolvedType.forName("java/lang/Class")); - javaLangObject = (ReferenceType) world.resolve(UnresolvedType.OBJECT); - extendsClass = new BoundedReferenceType(javaLangClass, true, world); - superClass = new BoundedReferenceType(javaLangClass, false, world); - extendsWithExtras = new BoundedReferenceType(javaLangClass, true, world, new ReferenceType[] { (ReferenceType) world - .resolve(UnresolvedType.forName("java/util/List")) }); - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/TypeVariableTestCase.java b/weaver/testsrc/org/aspectj/weaver/TypeVariableTestCase.java deleted file mode 100644 index e213b6104..000000000 --- a/weaver/testsrc/org/aspectj/weaver/TypeVariableTestCase.java +++ /dev/null @@ -1,104 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2005 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * Adrian Colyer Initial implementation - * ******************************************************************/ -package org.aspectj.weaver; - -import junit.framework.TestCase; - -import org.aspectj.weaver.bcel.BcelWorld; - -public class TypeVariableTestCase extends TestCase { - - private UnresolvedType javaLangNumber; - private UnresolvedType javaLangDouble; - private UnresolvedType javaUtilList; - private UnresolvedType javaIoSerializable; - private World world; - - public void testDefaultBounds() { - TypeVariable typevariable = new TypeVariable("T"); - assertNull(typevariable.getUpperBound()); - assertEquals("Object", UnresolvedType.OBJECT, typevariable.getFirstBound()); - assertEquals("no additional bounds", 0, typevariable.getSuperInterfaces().length); - } - - public void testName() { - TypeVariable tv = new TypeVariable("T"); - assertEquals("T", tv.getName()); - } - - public void testUpperBound() { - TypeVariable tv = new TypeVariable("N", javaLangNumber); - assertEquals("java.lang.Number", javaLangNumber, tv.getUpperBound()); - } - - public void testAdditionalUpperBounds() { - TypeVariable tv = new TypeVariable("E", UnresolvedType.OBJECT, new UnresolvedType[] { javaUtilList }); - assertEquals("1 additional bound", 1, tv.getSuperInterfaces().length); - assertEquals("java.util.List", javaUtilList, tv.getSuperInterfaces()[0]); - } - - public void testResolution() { - TypeVariable tv = new TypeVariable("T", javaLangNumber, new UnresolvedType[] { javaUtilList }); - tv.resolve(world); - assertEquals("resolved number", javaLangNumber.resolve(world), tv.getUpperBound()); - assertEquals("resolved list", javaUtilList.resolve(world), tv.getSuperInterfaces()[0]); - } - - public void testBindWithoutResolve() { - TypeVariable tv = new TypeVariable("X"); - try { - tv.canBeBoundTo(null); - fail("Should throw illegal state exception"); - } catch (IllegalStateException ex) { - } - } - - public void testCanBindToUpperMatch() { - TypeVariable tv = new TypeVariable("X", javaLangNumber); - tv.resolve(world); - assertTrue(tv.canBeBoundTo(javaLangDouble.resolve(world))); - } - - public void testCanBindToUpperFail() { - TypeVariable tv = new TypeVariable("X", javaLangNumber); - tv.resolve(world); - assertFalse(tv.canBeBoundTo(UnresolvedType.OBJECT.resolve(world))); - } - - public void testCanBindToInterfaceMatch() { - TypeVariable tv = new TypeVariable("T", javaLangNumber, new UnresolvedType[] { javaIoSerializable }); - tv.resolve(world); - assertTrue(tv.canBeBoundTo(javaLangDouble.resolve(world))); - } - - public void testCanBindToInterfaceFail() { - TypeVariable tv = new TypeVariable("T", javaLangNumber, new UnresolvedType[] { javaUtilList }); - tv.resolve(world); - assertFalse(tv.canBeBoundTo(javaLangDouble.resolve(world))); - } - - @Override - protected void setUp() throws Exception { - super.setUp(); - javaLangNumber = UnresolvedType.forSignature("Ljava/lang/Number;"); - javaLangDouble = UnresolvedType.forSignature("Ljava/lang/Double;"); - javaIoSerializable = UnresolvedType.forSignature("Ljava/io/Serializable;"); - javaUtilList = UnresolvedType.forSignature("Ljava/util/List;"); - world = new BcelWorld(); - } - - @Override - protected void tearDown() throws Exception { - super.tearDown(); - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/TypeXTestCase.java b/weaver/testsrc/org/aspectj/weaver/TypeXTestCase.java deleted file mode 100644 index dc4c2e3d1..000000000 --- a/weaver/testsrc/org/aspectj/weaver/TypeXTestCase.java +++ /dev/null @@ -1,219 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - - -package org.aspectj.weaver; - -import junit.framework.TestCase; - -import org.aspectj.testing.util.TestUtil; -import org.aspectj.util.LangUtil; -import org.aspectj.weaver.bcel.BcelWorld; - -/** - * This is a test case for all the portions of UnresolvedType that don't require a world. - */ -public class TypeXTestCase extends TestCase { - - public TypeXTestCase(String name) { - super(name); - } - - public void testUnresolvedTypes() { - // basic equality - String[] testNames = - new String[] {"int", "long", "int[]", "boolean[][]", - "java.lang.String", "java.lang.String[]", "void" }; - String[] testSigs = - new String[] {"I", "J", "[I", "[[Z", - "Ljava/lang/String;", "[Ljava/lang/String;", "V" }; - - String[] componentNames = - new String[] {null, null, "int", "boolean[]", - null, "java.lang.String", null }; - - int[] sizes = new int[] {1, 2, 1, 1, 1, 1, 0}; - - boolean[] isPrimitive = - new boolean[] { true, true, false, false, false, false, true }; - - nameSignatureTest(testNames, testSigs); - arrayTest(UnresolvedType.forNames(testNames), componentNames); - arrayTest(UnresolvedType.forSignatures(testSigs), componentNames); - - sizeTest(UnresolvedType.forNames(testNames), sizes); - sizeTest(UnresolvedType.forSignatures(testSigs), sizes); - - isPrimitiveTest(UnresolvedType.forSignatures(testSigs), isPrimitive); - } - - public void testNameAndSigWithInners() { - UnresolvedType t = UnresolvedType.forName("java.util.Map$Entry"); - assertEquals(t.getName(), "java.util.Map$Entry"); - assertEquals(t.getSignature(), "Ljava/util/Map$Entry;"); - assertEquals(t.getOutermostType(), UnresolvedType.forName("java.util.Map")); - assertEquals(UnresolvedType.forName("java.util.Map").getOutermostType(), UnresolvedType.forName("java.util.Map")); - } - - public void testNameAndSigWithParameters() { - UnresolvedType t = UnresolvedType.forName("java.util.List"); - assertEquals(t.getName(),"java.util.List"); - assertEquals(t.getSignature(),"Pjava/util/List;"); - t = UnresolvedType.forSignature("Pjava/util/List;"); - assertEquals(t.getName(),"java.util.List"); - assertEquals(t.getSignature(),"Pjava/util/List;"); - t = UnresolvedType.forName("java.util.Map>"); - assertEquals(t.getName(),"java.util.Map>"); - assertEquals(t.getSignature(),"Pjava/util/Map;>;"); - t = UnresolvedType.forSignature("Pjava/util/Map;>;"); - assertEquals(t.getName(),"java.util.Map>"); - assertEquals(t.getSignature(),"Pjava/util/Map;>;"); - } - - /** - * Verify UnresolvedType signature processing creates the right kind of UnresolvedType's from a signature. - * - * For example, calling UnresolvedType.dump() for - * "Ljava/util/Map;Ljava/lang/String;>;" - * results in: - * UnresolvedType: signature=Ljava/util/Map;Ljava/lang/String;>; parameterized=true #params=2 - * UnresolvedType: signature=Ljava/util/List; parameterized=true #params=1 - * UnresolvedType: signature=Ljava/lang/String; parameterized=false #params=0 - * UnresolvedType: signature=Ljava/lang/String; parameterized=false #params=0 - */ - public void testTypexGenericSignatureProcessing() { - UnresolvedType tx = null; - - tx = UnresolvedType.forSignature("Pjava/util/Set;"); - checkTX(tx,true,1); - - tx = UnresolvedType.forSignature("Pjava/util/Set;>;"); - checkTX(tx,true,1); - - tx = UnresolvedType.forSignature("Pjava/util/Map;Ljava/lang/String;>;"); - checkTX(tx,true,2); - checkTX(tx.getTypeParameters()[0],true,1); - checkTX(tx.getTypeParameters()[1],false,0); -// System.err.println(tx.dump()); - } - - public void testTypeXForParameterizedTypes() { - if (LangUtil.is15VMOrGreater()) { // no funny types pre 1.5 - World world = new BcelWorld(); - UnresolvedType stringType = UnresolvedType.forName("java/lang/String"); - ResolvedType listOfStringType = - TypeFactory.createParameterizedType( - UnresolvedType.forName("java/util/List").resolve(world), - new UnresolvedType[] {stringType}, - world); - assertEquals("1 type param",1,listOfStringType.typeParameters.length); - assertEquals(stringType,listOfStringType.typeParameters[0]); - assertTrue(listOfStringType.isParameterizedType()); - assertFalse(listOfStringType.isGenericType()); - } - } - - public void testTypeFactoryForParameterizedTypes() { - if (LangUtil.is15VMOrGreater()) { // no funny types pre 1.5 - UnresolvedType enumOfSimpleType = - TypeFactory.createTypeFromSignature("Pjava/lang/Enum;"); - assertEquals(1, enumOfSimpleType.getTypeParameters().length); - - UnresolvedType enumOfNestedType = - TypeFactory.createTypeFromSignature("Pjava/lang/Enum;"); - assertEquals(1, enumOfNestedType.getTypeParameters().length); - - // is this signature right? - UnresolvedType nestedTypeOfParameterized = - TypeFactory.createTypeFromSignature("PMyInterface$MyOtherType;"); - assertEquals(0, nestedTypeOfParameterized.getTypeParameters().length); - - // how about this one? is this valid? - UnresolvedType doublyNestedTypeSignatures = - TypeFactory.createTypeFromSignature("PMyInterface$MyOtherType;"); - assertEquals(1, doublyNestedTypeSignatures.getTypeParameters().length); - - } - } - - private void checkTX(UnresolvedType tx,boolean shouldBeParameterized,int numberOfTypeParameters) { - assertTrue("Expected parameterization flag to be "+shouldBeParameterized,tx.isParameterizedType()==shouldBeParameterized); - if (numberOfTypeParameters==0) { - UnresolvedType[] params = tx.getTypeParameters(); - assertTrue("Expected 0 type parameters but found "+params.length, params.length==0); - } else { - assertTrue("Expected #type parameters to be "+numberOfTypeParameters,tx.getTypeParameters().length==numberOfTypeParameters); - } - } - - - private void isPrimitiveTest(UnresolvedType[] types, boolean[] isPrimitives) { - for (int i = 0, len = types.length; i < len; i++) { - UnresolvedType type = types[i]; - boolean b = isPrimitives[i]; - assertEquals(type + " is primitive: ", b, type.isPrimitiveType()); - } - } - - private void sizeTest(UnresolvedType[] types, int[] sizes) { - for (int i = 0, len = types.length; i < len; i++) { - UnresolvedType type = types[i]; - int size = sizes[i]; - assertEquals("size of " + type + ": ", size, type.getSize()); - } - } - - private void arrayTest(UnresolvedType[] types, String[] components) { - for (int i = 0, len = types.length; i < len; i++) { - UnresolvedType type = types[i]; - String component = components[i]; - assertEquals(type + " is array: ", component != null, type.isArray()); - if (component != null) - assertEquals(type + " componentType: ", component, - type.getComponentType().getName()); - } - } - - private void nameSignatureTest(String[] ns, String[] ss) { - for (int i = 0, len = ns.length; i < len; i++) { - String n = ns[i]; - String s = ss[i]; - UnresolvedType tn = UnresolvedType.forName(n); - UnresolvedType ts = UnresolvedType.forSignature(s); - - assertEquals("forName(n).getName()", n, - tn.getName()); - assertEquals("forSignature(s).getSignature()", s, - ts.getSignature()); - assertEquals("forName(n).getSignature()", s, - tn.getSignature()); - assertEquals("forSignature(n).getName()", n, - ts.getName()); - - TestUtil.assertCommutativeEquals(tn, tn, true); - TestUtil.assertCommutativeEquals(ts, ts, true); - TestUtil.assertCommutativeEquals(tn, ts, true); - - for (int j = 0; j < len; j++) { - if (i == j) continue; - UnresolvedType tn1 = UnresolvedType.forName(ns[j]); - UnresolvedType ts1 = UnresolvedType.forSignature(ss[j]); - TestUtil.assertCommutativeEquals(tn, tn1, false); - TestUtil.assertCommutativeEquals(ts, tn1, false); - TestUtil.assertCommutativeEquals(tn, ts1, false); - TestUtil.assertCommutativeEquals(ts, ts1, false); - } - } - } - - -} diff --git a/weaver/testsrc/org/aspectj/weaver/WeaverMessagesTestCase.java b/weaver/testsrc/org/aspectj/weaver/WeaverMessagesTestCase.java deleted file mode 100644 index 7326971d3..000000000 --- a/weaver/testsrc/org/aspectj/weaver/WeaverMessagesTestCase.java +++ /dev/null @@ -1,47 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * IBM Corporation - initial API and implementation - *******************************************************************************/ -package org.aspectj.weaver; - -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.List; -import java.util.MissingResourceException; - -import junit.framework.TestCase; - -/** - * @author Adrian Colyer - */ -public class WeaverMessagesTestCase extends TestCase { - - public void testAllMessagesDefined() { - - Class wmClass = WeaverMessages.class; - Field[] fields = wmClass.getDeclaredFields(); - List fieldList = new ArrayList(); - for (int i = 0; i < fields.length; i++) { - Field f = fields[i]; - if (f.getType() == String.class) { - try { - String key = (String) f.get(null); -// String value = WeaverMessages.format(key); - assertFalse("Each key should be unique",fieldList.contains(key)); - fieldList.add(key); -// System.out.println(key + "," + value); - } catch (IllegalAccessException ex) { - } catch(MissingResourceException mrEx) { - fail("Missing resource: " + mrEx); - } - } - } - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/bcel/AfterReturningWeaveTestCase.java b/weaver/testsrc/org/aspectj/weaver/bcel/AfterReturningWeaveTestCase.java deleted file mode 100644 index 8ccafe4de..000000000 --- a/weaver/testsrc/org/aspectj/weaver/bcel/AfterReturningWeaveTestCase.java +++ /dev/null @@ -1,58 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - - -package org.aspectj.weaver.bcel; - -import java.io.IOException; - -import org.aspectj.weaver.ShadowMunger; - -public class AfterReturningWeaveTestCase extends WeaveTestCase { - { - regenerate = false; - } - - public AfterReturningWeaveTestCase(String name) { - super(name); - } - - public void testAfterReturning() throws IOException { - weaveTest( - getStandardTargets(), - "AfterReturning", - makeAdviceAll("afterReturning")); - } - - public void testAfterReturningParam() throws IOException { - weaveTest( - getStandardTargets(), - "AfterReturningParam", - makeAdviceField("afterReturning", "java.lang.Object")); - } - public void testAfterReturningCheckcastParam() throws IOException { - weaveTest( - getStandardTargets(), - "AfterReturningCheckcastParam", - makeAdviceField("afterReturning", "java.rmi.server.LogStream")); - } - - public void testAfterReturningConversionParam() throws IOException { - String mungerString = - "afterReturning(): call(int *.*(..)) -> " - + "static void Aspect.ajc_afterReturning_field_get(java.lang.Object)"; - ShadowMunger cm = makeConcreteAdvice(mungerString, 1); - - weaveTest("FancyHelloWorld", "AfterReturningConversionParam", cm); - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/bcel/AfterThrowingWeaveTestCase.java b/weaver/testsrc/org/aspectj/weaver/bcel/AfterThrowingWeaveTestCase.java deleted file mode 100644 index b0c3f04ce..000000000 --- a/weaver/testsrc/org/aspectj/weaver/bcel/AfterThrowingWeaveTestCase.java +++ /dev/null @@ -1,45 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - -package org.aspectj.weaver.bcel; - -import java.io.IOException; - -import org.aspectj.weaver.Advice; -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.ShadowMunger; - -public class AfterThrowingWeaveTestCase extends WeaveTestCase { - { - regenerate = false; - } - - public AfterThrowingWeaveTestCase(String name) { - super(name); - } - - public void testAfterThrowing() throws IOException { - weaveTest(getStandardTargets(), "AfterThrowing", makeAdviceAll("afterThrowing")); - } - - public void testAfterThrowingParam() throws IOException { - BcelWorld world = new BcelWorld(); - - ShadowMunger myMunger = BcelTestUtils.shadowMunger(world, - "afterThrowing(): get(* *.out) -> static void Aspect.ajc_afterThrowing_field_get(java.lang.Throwable)", - Advice.ExtraArgument); - ShadowMunger cm = myMunger.concretize(ResolvedType.MISSING, world, null); - - weaveTest(getStandardTargets(), "AfterThrowingParam", cm); - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/bcel/AfterWeaveTestCase.java b/weaver/testsrc/org/aspectj/weaver/bcel/AfterWeaveTestCase.java deleted file mode 100644 index a51c69b9a..000000000 --- a/weaver/testsrc/org/aspectj/weaver/bcel/AfterWeaveTestCase.java +++ /dev/null @@ -1,31 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - - -package org.aspectj.weaver.bcel; - -import java.io.*; - -public class AfterWeaveTestCase extends WeaveTestCase { - { - regenerate = false; - } - - public AfterWeaveTestCase(String name) { - super(name); - } - - - public void testAfter() throws IOException { - weaveTest(getStandardTargets(), "After", makeAdviceAll("after")); - } -} diff --git a/weaver/testsrc/org/aspectj/weaver/bcel/ArgsWeaveTestCase.java b/weaver/testsrc/org/aspectj/weaver/bcel/ArgsWeaveTestCase.java deleted file mode 100644 index 1108c3574..000000000 --- a/weaver/testsrc/org/aspectj/weaver/bcel/ArgsWeaveTestCase.java +++ /dev/null @@ -1,119 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - - -package org.aspectj.weaver.bcel; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import org.aspectj.apache.bcel.Constants; -import org.aspectj.apache.bcel.generic.InstructionFactory; -import org.aspectj.apache.bcel.generic.InstructionHandle; -import org.aspectj.apache.bcel.generic.InstructionList; -import org.aspectj.apache.bcel.generic.Type; -import org.aspectj.weaver.AdviceKind; -import org.aspectj.weaver.MemberImpl; -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.Shadow; -import org.aspectj.weaver.ShadowMunger; -import org.aspectj.weaver.UnresolvedType; - -/**. - */ -public class ArgsWeaveTestCase extends WeaveTestCase { - { - regenerate = false; - } - - public ArgsWeaveTestCase(String name) { - super(name); - } - - - public void testAfterReturningArgs() throws IOException { - weaveTest("HelloWorld", "ArgsAfterReturningHelloWorld", makeArgsMunger("afterReturning")); - } - - - public void testFancyAfterReturningArgs() throws IOException { - weaveTest("FancyHelloWorld", "ArgsAfterReturningFancyHelloWorld", makeArgsMunger("afterReturning")); - } - - public void testThrowing() throws IOException { - weaveTest("HelloWorld", "ArgsAfterThrowingHelloWorld", makeArgsMunger("afterThrowing")); - } - - public void testLots() throws IOException { - List l = new ArrayList<>(); - - BcelAdvice p1 = - makeArgsMunger("before"); - - BcelAdvice p2 = - makeArgsMunger("afterThrowing"); - - BcelAdvice p3 = - makeArgsMunger("afterReturning"); - - l.add(p1); - l.add(p2); - l.add(p3); - - weaveTest("HelloWorld", "ArgsBeforeAfterHelloWorld", addLexicalOrder(l)); - } - - /* private */ InstructionList getArgsAdviceTag(BcelShadow shadow, String where) { - String methodName = - "ajc_" + where + "_" + shadow.getKind().toLegalJavaIdentifier(); - InstructionFactory fact = shadow.getFactory(); - InstructionList il = new InstructionList(); - - - il.append( - BcelRenderer.renderExpr( - fact, - new BcelWorld(), - shadow.getArgVar(0), - Type.OBJECT)); - - il.append( - fact.createInvoke( - "Aspect", - methodName, - Type.VOID, - new Type[] { Type.OBJECT }, - Constants.INVOKESTATIC)); - - return il; - } - - private BcelAdvice makeArgsMunger(final String kindx) { - ResolvedType rtx = world.resolve(UnresolvedType.forName("Aspect"),true); - assertTrue("Cant find required type Aspect",!rtx.isMissing()); - return new BcelAdvice(AdviceKind.stringToKind(kindx), makePointcutNoZeroArg(), - MemberImpl.method(UnresolvedType.forName("Aspect"), 0, "foo", "()V"), 0, -1, -1, null, - rtx) { - @Override - public void specializeOn(Shadow shadow) { - super.specializeOn(shadow); - shadow.getArgVar(0); - } - @Override - public InstructionList getAdviceInstructions(BcelShadow shadow, BcelVar extraVar, InstructionHandle fk) { - return getArgsAdviceTag(shadow, kindx); - } - }; - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/bcel/AroundArgsWeaveTestCase.java b/weaver/testsrc/org/aspectj/weaver/bcel/AroundArgsWeaveTestCase.java deleted file mode 100644 index 4088755b5..000000000 --- a/weaver/testsrc/org/aspectj/weaver/bcel/AroundArgsWeaveTestCase.java +++ /dev/null @@ -1,41 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - - -package org.aspectj.weaver.bcel; - -import java.io.IOException; - -import org.aspectj.weaver.ShadowMunger; - -public class AroundArgsWeaveTestCase extends WeaveTestCase { - { - regenerate = false; - } - - public AroundArgsWeaveTestCase(String name) { - super(name); - } - - public void testWeave() throws IOException - { - String label = "AroundArgs"; - ShadowMunger p = - makeConcreteAdvice( - "around(list) : " - + "(call(public * add(..)) && target(list)) -> " - + "static boolean Aspect.ajc_around0" - + "(java.util.ArrayList, org.aspectj.runtime.internal.AroundClosure)"); - weaveTest(new String[] {"DynamicHelloWorld"}, label, p); - - } -} diff --git a/weaver/testsrc/org/aspectj/weaver/bcel/AroundWeaveTestCase.java b/weaver/testsrc/org/aspectj/weaver/bcel/AroundWeaveTestCase.java deleted file mode 100644 index a9d1fe9b4..000000000 --- a/weaver/testsrc/org/aspectj/weaver/bcel/AroundWeaveTestCase.java +++ /dev/null @@ -1,100 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - - -package org.aspectj.weaver.bcel; - -import java.io.IOException; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.List; - -import org.aspectj.weaver.AdviceKind; -import org.aspectj.weaver.Member; -import org.aspectj.weaver.MemberImpl; -import org.aspectj.weaver.Shadow; -import org.aspectj.weaver.ShadowMunger; -import org.aspectj.weaver.UnresolvedType; - -public class AroundWeaveTestCase extends WeaveTestCase { - { - regenerate = false; - } - - public AroundWeaveTestCase(String name) { - super(name); - } - - public void testAround() throws IOException { - aroundTest("Around", true); - } - - public void testAroundAll() throws IOException { - aroundTest("AroundAll", false); - } - - public void testAroundAndOthers() throws IOException { - aroundTestAndOthers("AroundAndOthers", true); - } - - public void testAroundAllAndOthers() throws IOException { - aroundTestAndOthers("AroundAllAndOthers", false); - } - - - private BcelAdvice makeAroundMunger(final boolean matchOnlyPrintln) { - BcelWorld world = super.world; - final Member sig = - MemberImpl.method( - UnresolvedType.forName("Aspect"), - Modifier.STATIC, - "ajc_around", - "(Lorg/aspectj/runtime/internal/AroundClosure;)Ljava/lang/Object;"); - - return new BcelAdvice( - AdviceKind.stringToKind("around"), - matchOnlyPrintln ? makePointcutPrintln() : makePointcutAll(), - sig, 0, -1, -1, null, UnresolvedType.forName("Aspect").resolve(world)) - { - @Override - public void specializeOn(Shadow s) { - super.specializeOn(s); - ((BcelShadow) s).initializeForAroundClosure(); - } - }; - } - - private void aroundTest(String outName, final boolean matchOnlyPrintln) throws IOException { - weaveTest(getStandardTargets(), outName, makeAroundMunger(matchOnlyPrintln)); - } - - private void aroundTestAndOthers(String outName, final boolean matchOnlyPrintln) - throws IOException - { - - List l = new ArrayList<>(); - - // the afterReturning was taken out to avoid circular advice dependency - - l.addAll(makeAdviceAll("before", matchOnlyPrintln)); - //l.addAll(makeAdviceAll("afterReturning", matchOnlyPrintln)); - - l.add(makeAroundMunger(matchOnlyPrintln)); - - l.addAll(makeAdviceAll("before", matchOnlyPrintln)); - //l.addAll(makeAdviceAll("afterReturning", matchOnlyPrintln)); - - l.add(makeAroundMunger(matchOnlyPrintln)); - weaveTest(getStandardTargets(), outName, addLexicalOrder(l)); - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/bcel/BcelGenericSignatureToTypeXTestCase.java b/weaver/testsrc/org/aspectj/weaver/bcel/BcelGenericSignatureToTypeXTestCase.java deleted file mode 100644 index 9ba0b1943..000000000 --- a/weaver/testsrc/org/aspectj/weaver/bcel/BcelGenericSignatureToTypeXTestCase.java +++ /dev/null @@ -1,87 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2005 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * Adrian Colyer Initial implementation - * ******************************************************************/ -package org.aspectj.weaver.bcel; - -import junit.framework.TestCase; - -import org.aspectj.apache.bcel.Repository; -import org.aspectj.apache.bcel.classfile.JavaClass; -import org.aspectj.apache.bcel.classfile.Signature; -import org.aspectj.util.GenericSignature; -import org.aspectj.util.GenericSignatureParser; -import org.aspectj.util.GenericSignature.ClassSignature; -import org.aspectj.weaver.UnresolvedType; - -/** - * @author colyer - * - */ -public class BcelGenericSignatureToTypeXTestCase extends TestCase { - - public final GenericSignature.ClassSignature getGenericClassTypeSignature(JavaClass jClass) { - Signature sig = jClass.getSignatureAttribute(); - if (sig != null) { - GenericSignatureParser parser = new GenericSignatureParser(); - ClassSignature classSig = parser.parseAsClassSignature(sig.getSignature()); - return classSig; - } - return null; - } - - // public final GenericSignature.MethodTypeSignature getGenericMethodTypeSignature(JavaClass jClass) { - // Signature sig = jClass.getSignatureAttribute(); - // if (sig != null) { - // GenericSignatureParser parser = new GenericSignatureParser(); - // MethodTypeSignature mSig = parser.parseAsMethodSignature(sig); - // return mSig; - // } - // return null; - // } - - // public FieldTypeSignature asFieldTypeSignature() { - // if (fieldSig == null) { - // GenericSignatureParser parser = new GenericSignatureParser(); - // fieldSig = parser.parseAsFieldSignature(getSignature()); - // } - // return fieldSig; - // } - - public void testEnumFromHell() throws Exception { - BcelWorld world = new BcelWorld(); - JavaClass javaLangEnum = Repository.lookupClass("java/lang/Enum"); - GenericSignature.ClassSignature cSig = getGenericClassTypeSignature(javaLangEnum); - UnresolvedType superclass = BcelGenericSignatureToTypeXConverter.classTypeSignature2TypeX(cSig.superclassSignature, - cSig.formalTypeParameters, world); - assertEquals("Ljava/lang/Object;", superclass.getSignature()); - assertEquals("2 superinterfaces", 2, cSig.superInterfaceSignatures.length); - UnresolvedType comparable = BcelGenericSignatureToTypeXConverter.classTypeSignature2TypeX(cSig.superInterfaceSignatures[0], - cSig.formalTypeParameters, world); - assertEquals("Pjava/lang/Comparable;", comparable.getSignature()); - UnresolvedType serializable = BcelGenericSignatureToTypeXConverter.classTypeSignature2TypeX( - cSig.superInterfaceSignatures[1], cSig.formalTypeParameters, world); - assertEquals("Ljava/io/Serializable;", serializable.getSignature()); - } - - public void testColonColon() throws Exception { - BcelWorld world = new BcelWorld(); - GenericSignature.ClassSignature cSig = new GenericSignatureParser() - .parseAsClassSignature("Ljava/lang/Object;Ljava/lang/Comparable;"); - UnresolvedType resolved = BcelGenericSignatureToTypeXConverter.classTypeSignature2TypeX(cSig.superclassSignature, - cSig.formalTypeParameters, world); - assertEquals("Ljava/lang/Object;", resolved.getSignature()); - // UnresolvedType resolvedInt = - BcelGenericSignatureToTypeXConverter.classTypeSignature2TypeX(cSig.superInterfaceSignatures[0], cSig.formalTypeParameters, - world); - - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/bcel/BcelTestUtils.java b/weaver/testsrc/org/aspectj/weaver/bcel/BcelTestUtils.java deleted file mode 100644 index bc9f47a85..000000000 --- a/weaver/testsrc/org/aspectj/weaver/bcel/BcelTestUtils.java +++ /dev/null @@ -1,65 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2008 Contributors - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Andy Clement initial implementation - * ******************************************************************/ -package org.aspectj.weaver.bcel; - -import org.aspectj.weaver.Advice; -import org.aspectj.weaver.AdviceKind; -import org.aspectj.weaver.Member; -import org.aspectj.weaver.TestUtils; -import org.aspectj.weaver.UnresolvedType; -import org.aspectj.weaver.World; -import org.aspectj.weaver.patterns.FormalBinding; -import org.aspectj.weaver.patterns.Pointcut; -import org.aspectj.weaver.patterns.SimpleScope; - -public class BcelTestUtils { - /** - * Moved from BcelWorld to here - * - * Parse a string into advice. - * - *
- * - *
-	 * Kind ( Id , ... ) : Pointcut -> MethodSignature
-	 * 
- * - *
- */ - public static Advice shadowMunger(World w, String str, int extraFlag) { - str = str.trim(); - int start = 0; - int i = str.indexOf('('); - AdviceKind kind = AdviceKind.stringToKind(str.substring(start, i)); - start = ++i; - i = str.indexOf(')', i); - String[] ids = TestUtils.parseIds(str.substring(start, i).trim()); - // start = ++i; - - i = str.indexOf(':', i); - start = ++i; - i = str.indexOf("->", i); - Pointcut pointcut = Pointcut.fromString(str.substring(start, i).trim()); - Member m = TestUtils.methodFromString(str.substring(i + 2, str.length()).trim()); - - // now, we resolve - UnresolvedType[] types = m.getParameterTypes(); - FormalBinding[] bindings = new FormalBinding[ids.length]; - for (int j = 0, len = ids.length; j < len; j++) { - bindings[j] = new FormalBinding(types[j], ids[j], j, 0, 0); - } - - Pointcut p = pointcut.resolve(new SimpleScope(w, bindings)); - - return new BcelAdvice(kind, p, m, extraFlag, 0, 0, null, null); - } -} diff --git a/weaver/testsrc/org/aspectj/weaver/bcel/BcelTests.java b/weaver/testsrc/org/aspectj/weaver/bcel/BcelTests.java deleted file mode 100644 index 77f066083..000000000 --- a/weaver/testsrc/org/aspectj/weaver/bcel/BcelTests.java +++ /dev/null @@ -1,57 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - - -package org.aspectj.weaver.bcel; - -import org.aspectj.util.LangUtil; - -import junit.framework.*; - -public class BcelTests extends TestCase { - - public static Test suite() { - TestSuite suite = new TestSuite(BcelTests.class.getName()); - // abstract - //suite.addTestSuite(WeaveTestCase.class); - //$JUnit-BEGIN$ - suite.addTestSuite(AfterReturningWeaveTestCase.class); - suite.addTestSuite(AfterThrowingWeaveTestCase.class); - suite.addTestSuite(AfterWeaveTestCase.class); - suite.addTestSuite(ArgsWeaveTestCase.class); - suite.addTestSuite(AroundArgsWeaveTestCase.class); - suite.addTestSuite(AroundWeaveTestCase.class); - suite.addTestSuite(BeforeWeaveTestCase.class); - suite.addTestSuite(CheckerTestCase.class); - suite.addTestSuite(FieldSetTestCase.class); - suite.addTestSuite(HierarchyDependsTestCase.class); - suite.addTestSuite(IdWeaveTestCase.class); - suite.addTestSuite(MoveInstructionsWeaveTestCase.class); - suite.addTestSuite(NonstaticWeaveTestCase.class); - suite.addTestSuite(PatternWeaveTestCase.class); - suite.addTestSuite(PointcutResidueTestCase.class); - suite.addTestSuite(TraceJarWeaveTestCase.class); - suite.addTestSuite(TjpWeaveTestCase.class); - suite.addTestSuite(UtilityTestCase.class); - suite.addTestSuite(WeaveOrderTestCase.class); - suite.addTestSuite(WorldTestCase.class); - suite.addTestSuite(ZipTestCase.class); - if (LangUtil.is19VMOrGreater()) { - suite.addTestSuite(JImageTestCase.class); - } - //$JUnit-END$ - return suite; - } - - public BcelTests(String name) { super(name); } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/bcel/BeforeWeaveTestCase.java b/weaver/testsrc/org/aspectj/weaver/bcel/BeforeWeaveTestCase.java deleted file mode 100644 index a43689777..000000000 --- a/weaver/testsrc/org/aspectj/weaver/bcel/BeforeWeaveTestCase.java +++ /dev/null @@ -1,31 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - - -package org.aspectj.weaver.bcel; - -import java.io.IOException; - -public class BeforeWeaveTestCase extends WeaveTestCase { - { - regenerate = false; - } - - public BeforeWeaveTestCase(String name) { - super(name); - } - - - public void testBefore() throws IOException { - weaveTest(getStandardTargets(), "Before", makeAdviceAll("before")); - } -} diff --git a/weaver/testsrc/org/aspectj/weaver/bcel/CheckerTestCase.java b/weaver/testsrc/org/aspectj/weaver/bcel/CheckerTestCase.java deleted file mode 100644 index b503e9593..000000000 --- a/weaver/testsrc/org/aspectj/weaver/bcel/CheckerTestCase.java +++ /dev/null @@ -1,48 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - - -package org.aspectj.weaver.bcel; - -import java.io.IOException; - -import org.aspectj.weaver.Checker; -import org.aspectj.weaver.patterns.DeclareErrorOrWarning; -import org.aspectj.bridge.*; -import org.aspectj.bridge.MessageHandler; - -public class CheckerTestCase extends WeaveTestCase { - { - regenerate = false; - } - - public CheckerTestCase(String name) { - super(name); - } - - - public void testStaticTjp() throws IOException { - Checker checker = new Checker( - new DeclareErrorOrWarning(true, makePointcutPrintln(), "hey, we found a println")); - - MessageHandler handler = new MessageHandler(); - world.setMessageHandler(handler); - - weaveTest("HelloWorld", "IdHelloWorld", checker); - assertEquals(1, handler.numMessages(IMessage.ERROR, false)); - - handler = new MessageHandler(); - world.setMessageHandler(handler); - weaveTest("FancyHelloWorld", "IdFancyHelloWorld", checker); - assertEquals(3, handler.numMessages(IMessage.ERROR, false)); - } -} diff --git a/weaver/testsrc/org/aspectj/weaver/bcel/ClassLoaderRepositoryTests.java b/weaver/testsrc/org/aspectj/weaver/bcel/ClassLoaderRepositoryTests.java deleted file mode 100644 index 664b0c783..000000000 --- a/weaver/testsrc/org/aspectj/weaver/bcel/ClassLoaderRepositoryTests.java +++ /dev/null @@ -1,213 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2006 Contributors - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Andy Clement initial implementation - * ******************************************************************/ - - -package org.aspectj.weaver.bcel; - -import java.io.File; -import java.lang.ref.Reference; -import java.lang.reflect.Field; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.Enumeration; -import java.util.Map; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -import junit.framework.TestCase; - -import org.aspectj.apache.bcel.util.ClassLoaderRepository; - -/** NOT YET INCLUDED IN A FULL TEST RUN - WORK IN PROGRESS CHECKING CLASSLOADERREPOSITORY OPTIMIZATIONS */ -public class ClassLoaderRepositoryTests extends TestCase { - private File f; - private ZipFile zf; - private Enumeration entries; - private Map map; - - public void setUp() throws Exception { - f = new File("../lib/aspectj/lib/aspectjtools.jar"); - assertTrue("Couldn't find aspectjtools to test. Tried: "+f.getAbsolutePath(),f.exists()); - zf = new ZipFile(f); - entries = zf.entries(); -// ClassLoaderRepository.sharedCacheCompactFrequency = 16384; - map = getSharedMap(); - } - - public void tearDown() { - new ClassLoaderRepository((ClassLoader) null).reset(); - } - - private ClassLoaderRepository setupRepository() throws Exception { - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - ClassLoader res = new URLClassLoader(new URL[]{f.toURL()},cl); - ClassLoaderRepository rep = new ClassLoaderRepository(res); - return rep; - } - - private void compareTwoRepositories() throws Exception { - ClassLoaderRepository rep1 = setupRepository(); - ClassLoaderRepository rep2 = setupRepository(); - int i = 0; - while (entries.hasMoreElements()) { - ZipEntry zfe = (ZipEntry)entries.nextElement(); - String classfileName = zfe.getName(); - if (classfileName.endsWith(".class")) { - String clazzname = classfileName.substring(0,classfileName.length()-6).replace('/','.'); - - // twice by each - rep1.loadClass(clazzname); - rep1.loadClass(clazzname); - rep2.loadClass(clazzname); - rep2.loadClass(clazzname); - i++; - } - } - System.err.println("Successfully compared "+i+" entries!!"); - System.err.println(rep1.report()); - System.err.println(rep2.report()); - } - -// private void loadOnce() throws Exception { -// ClassLoaderRepository rep = setupRepository(); -// while (entries.hasMoreElements()) { -// ZipEntry zfe = (ZipEntry) entries.nextElement(); -// String classfileName = zfe.getName(); -// if (classfileName.endsWith(".class")) { -// String clazzname = classfileName.substring(0, -// classfileName.length() - 6).replace('/', '.'); -// -// rep.loadClass(clazzname); -// } -// } -// } - - public void testMultiThreaded() throws Throwable { - ClassLoaderRepository.useSharedCache=true; -// ClassLoaderRepository.sharedCacheCompactFrequency = 200; - //loadOnce(); - TestThread threads[] = new TestThread[6]; - for (int i=0; i())", - "constructor-call(void java.lang.StringBuffer.(java.lang.String))" }); - } - - public void testId() throws IOException { - final List l = new ArrayList(); - BcelAdvice p = new BcelAdvice(null, makePointcutAll(), null, 0, -1, -1, null, null) { - public boolean implementOn(Shadow shadow) { - l.add(shadow); - return true; - } - }; - weaveTest(new String[] { "HelloWorld" }, "Id2", p); - - checkShadowSet(l, new String[] { "method-execution(void HelloWorld.main(java.lang.String[]))", - "method-call(void java.io.PrintStream.println(java.lang.String))", - "field-get(java.io.PrintStream java.lang.System.out)", "constructor-execution(void HelloWorld.())", }); - } - - // this test requires that Trace has been unzipped and placed in the correct place - // public void testTraceId() throws IOException { - // String saveClassDir = classDir; - // try { - // classDir = "testdata/dummyAspect.jar"; - // - // - // - // final List l = new ArrayList(); - // BcelAdvice p = new BcelAdvice(null, makePointcutAll(), null, 0, -1, -1, null, null) { - // public void implementOn(Shadow shadow) { - // l.add(shadow); - // } - // }; - // boolean tempRunTests = runTests; - // runTests = false; - // weaveTest(new String[] {"DummyAspect"}, "Id", p); - // runTests = tempRunTests; - // - // checkShadowSet(l, new String[] { - // "constructor-execution(void DummyAspect.())", - // // XXX waiting on parser stuff - // //"advice-execution(void DummyAspect.ajc_before_1(java.lang.Object))", - // }); - // } finally { - // classDir = saveClassDir; - // } - // } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/bcel/JImageTestCase.java b/weaver/testsrc/org/aspectj/weaver/bcel/JImageTestCase.java deleted file mode 100644 index 9b465ebe8..000000000 --- a/weaver/testsrc/org/aspectj/weaver/bcel/JImageTestCase.java +++ /dev/null @@ -1,135 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2017 Contributors - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * ******************************************************************/ -package org.aspectj.weaver.bcel; - -import java.io.IOException; -import java.net.URI; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; -import java.nio.file.FileVisitResult; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.attribute.BasicFileAttributes; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import org.aspectj.bridge.AbortException; -import org.aspectj.bridge.IMessage; -import org.aspectj.bridge.IMessage.Kind; -import org.aspectj.bridge.IMessageHandler; -import org.aspectj.util.LangUtil; -import org.aspectj.weaver.UnresolvedType; -import org.aspectj.weaver.bcel.ClassPathManager.ClassFile; -import org.aspectj.weaver.bcel.ClassPathManager.Entry; -import org.aspectj.weaver.bcel.ClassPathManager.JImageEntry; - -import junit.framework.TestCase; - -/** - * Exercise the JImage handling in @link {@link org.aspectj.weaver.bcel.ClassPathManager}. - * - * @author Andy Clement - */ -public class JImageTestCase extends TestCase { - - ClassPathManager cpm; - - public void setUp() throws Exception { - List paths = new ArrayList<>(); - paths.add(LangUtil.getJrtFsFilePath()); - cpm = new ClassPathManager(paths,new TestMessageHandler()); - } - - public void testOnJava9() { - assertTrue(LangUtil.is19VMOrGreater()); - } - - public void testBasicStructureAndCapabilities() { - // Should be one entry for finding JRT contents - List entries = cpm.getEntries(); - assertEquals(1,entries.size()); - assertEquals(JImageEntry.class,entries.get(0).getClass()); - - ClassFile stringClassFile = cpm.find(UnresolvedType.JL_STRING); - assertNotNull(stringClassFile); - assertEquals("java/lang/String.class",stringClassFile.getPath()); - } - - public void testBehaviour() throws Exception { - JImageEntry jie = getJImageEntry(); - - Map packageCache = JImageEntry.getPackageCache(); - assertTrue(packageCache.size()>0); - // Note: seems to be about 1625 entries in it for Java9 - Path path = packageCache.get("java/lang"); - assertEquals("modules/java.base/java/lang", path.toString()); - path = packageCache.get("java/io"); - assertEquals("modules/java.base/java/io", path.toString()); - - assertNotNull(jie.find("java/lang/String")); - assertNotNull(jie.find("java/io/File")); - // TODO test the filecache, hard because difficult to simulate collection of SoftReferences - } - - - static class TestMessageHandler implements IMessageHandler { - - @Override - public boolean handleMessage(IMessage message) throws AbortException { - return false; - } - - @Override - public boolean isIgnoring(Kind kind) { - return false; - } - - @Override - public void dontIgnore(Kind kind) { - } - - @Override - public void ignore(Kind kind) { - } - - } - - // --- - - private JImageEntry getJImageEntry() { - return (JImageEntry) cpm.getEntries().get(0); - } - - public List getAllTheClasses() { - final List result = new ArrayList<>(); - URI JRT_URI = URI.create("jrt:/"); //$NON-NLS-1$ - FileSystem fs = FileSystems.getFileSystem(JRT_URI); - Iterable roots = fs.getRootDirectories(); - try { - for (java.nio.file.Path path : roots) { - Files.walkFileTree(path, new SimpleFileVisitor() { - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - if (file.getNameCount()>3 && file.toString().endsWith(".class")) { - String withClassSuffix = file.subpath(2, file.getNameCount()).toString(); - result.add(withClassSuffix.substring(0,withClassSuffix.length()-".class".length())); - } - return FileVisitResult.CONTINUE; - } - }); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - return result; - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/bcel/MegaZipTestCase.java b/weaver/testsrc/org/aspectj/weaver/bcel/MegaZipTestCase.java deleted file mode 100644 index 797c153d5..000000000 --- a/weaver/testsrc/org/aspectj/weaver/bcel/MegaZipTestCase.java +++ /dev/null @@ -1,107 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - -package org.aspectj.weaver.bcel; - -import java.io.File; -import java.io.IOException; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.List; - -import org.aspectj.weaver.AdviceKind; -import org.aspectj.weaver.BcweaverTests; -import org.aspectj.weaver.Member; -import org.aspectj.weaver.MemberImpl; -import org.aspectj.weaver.Shadow; -import org.aspectj.weaver.ShadowMunger; -import org.aspectj.weaver.UnresolvedType; - -public class MegaZipTestCase extends WeaveTestCase { - - private File outDir; - - public MegaZipTestCase(String arg0) { - super(arg0); - } - - public void setUp() throws Exception { - super.setUp(); - outDir = BcweaverTests.getOutdir(); - } - - public void tearDown() throws Exception { - super.tearDown(); - BcweaverTests.removeOutDir(); - outDir = null; - } - - private BcelAdvice makeAroundMunger(final boolean matchOnlyPrintln) { - // BcelWorld world = new BcelWorld(); - final Member sig = MemberImpl.method(UnresolvedType.forName("fluffy.Aspect"), Modifier.STATIC, "aroundFun", - "(Lorg/aspectj/runtime/internal/AroundClosure;)Ljava/lang/Object;"); - - return new BcelAdvice(AdviceKind.stringToKind("around"), matchOnlyPrintln ? makePointcutPrintln() : makePointcutAll(), sig, - 0, -1, -1, null, null) { - public void specializeOn(Shadow s) { - super.specializeOn(s); - ((BcelShadow) s).initializeForAroundClosure(); - } - }; - } - - public List getShadowMungers() { - List ret = new ArrayList(); - ret.add(makeConcreteAdvice("before" + "(): call(* *.println(..)) -> static void fluffy.Aspect.before_method_call()")); - ret.add(makeConcreteAdvice("afterReturning" - + "(): call(* *.println(..)) -> static void fluffy.Aspect.afterReturning_method_call()")); - - ret.add(makeConcreteAdvice("before" + "(): execution(* *.*(..)) -> static void fluffy.Aspect.ignoreMe()")); - - ret.add(makeConcreteAdvice("afterReturning" + "(): execution(* *.*(..)) -> static void fluffy.Aspect.ignoreMe()")); - - ret.add(makeConcreteAdvice("afterThrowing" - + "(): execution(* *.*(..)) -> static void fluffy.Aspect.afterThrowing_method_execution(java.lang.Throwable)", 1)); - ret.add(makeConcreteAdvice("after" + "(): execution(* *.*(..)) -> static void fluffy.Aspect.ignoreMe()")); - - ret.add(makeAroundMunger(true)); - return ret; - } - - public void zipTest(String fileName) throws IOException { - long startTime = System.currentTimeMillis(); - File inFile = new File(BcweaverTests.TESTDATA_PATH, fileName); - File outFile = new File(outDir, fileName); - outFile.delete(); - - world = new BcelWorld("c:/apps/java-1.3.1_04/lib/tools.jar"); - BcelWeaver weaver1 = new BcelWeaver(world); - - ZipFileWeaver weaver = new ZipFileWeaver(inFile); - - weaver1.setShadowMungers(getShadowMungers()); - - weaver.weave(weaver1, outFile); - assertTrue(outFile.lastModified() > startTime); - } - - public void testEmptyForAntJUnit() { - } - - // this is something we test every now and again. - // to try, rename as testBig and put aspectjtools.jar in testdata - public void trytestBig() throws IOException { - System.out.println("could take 80 seconds..."); - zipTest("aspectjtools.jar"); - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/bcel/MoveInstructionsWeaveTestCase.java b/weaver/testsrc/org/aspectj/weaver/bcel/MoveInstructionsWeaveTestCase.java deleted file mode 100644 index bd7c2ae65..000000000 --- a/weaver/testsrc/org/aspectj/weaver/bcel/MoveInstructionsWeaveTestCase.java +++ /dev/null @@ -1,81 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - -package org.aspectj.weaver.bcel; - -import java.io.IOException; -import java.util.ArrayList; - -import org.aspectj.apache.bcel.generic.InstructionFactory; -import org.aspectj.weaver.NameMangler; -import org.aspectj.weaver.Shadow; - -public class MoveInstructionsWeaveTestCase extends WeaveTestCase { - { - regenerate = false; - } - - public MoveInstructionsWeaveTestCase(String name) { - super(name); - } - - public void testHello() throws IOException { - BcelAdvice p = new BcelAdvice(null, makePointcutAll(), null, 0, -1, -1, null, null) { - public void specializeOn(Shadow s) { - super.specializeOn(s); - ((BcelShadow) s).initializeForAroundClosure(); - } - - public boolean implementOn(Shadow s) { - BcelShadow shadow = (BcelShadow) s; - LazyMethodGen newMethod = shadow.extractShadowInstructionsIntoNewMethod(NameMangler.getExtractableName(shadow - .getSignature()) - + "_extracted", 0, this.getSourceLocation(), new ArrayList(),shadow.getEnclosingClass().isInterface()); - shadow.getRange().append(shadow.makeCallToCallback(newMethod)); - - if (!shadow.isFallsThrough()) { - shadow.getRange().append(InstructionFactory.createReturn(newMethod.getReturnType())); - } - return true; - } - }; - - weaveTest("HelloWorld", "ExtractedHelloWorld", p); - } - - static int counter = 0; - - public void testFancyHello() throws IOException { - BcelAdvice p = new BcelAdvice(null, makePointcutAll(), null, 0, -1, -1, null, null) { - public void specializeOn(Shadow s) { - super.specializeOn(s); - ((BcelShadow) s).initializeForAroundClosure(); - } - - public boolean implementOn(Shadow s) { - BcelShadow shadow = (BcelShadow) s; - LazyMethodGen newMethod = - shadow.extractShadowInstructionsIntoNewMethod(NameMangler.getExtractableName(shadow - .getSignature()) - + "_extracted" + counter++, 0, this.getSourceLocation(), new ArrayList(),shadow.getEnclosingClass().isInterface()); - shadow.getRange().append(shadow.makeCallToCallback(newMethod)); - - if (!shadow.isFallsThrough()) { - shadow.getRange().append(InstructionFactory.createReturn(newMethod.getReturnType())); - } - return true; - } - }; - - weaveTest("FancyHelloWorld", "ExtractedFancyHelloWorld", p); - } -} diff --git a/weaver/testsrc/org/aspectj/weaver/bcel/NonstaticWeaveTestCase.java b/weaver/testsrc/org/aspectj/weaver/bcel/NonstaticWeaveTestCase.java deleted file mode 100644 index 1f67a6249..000000000 --- a/weaver/testsrc/org/aspectj/weaver/bcel/NonstaticWeaveTestCase.java +++ /dev/null @@ -1,83 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - - -package org.aspectj.weaver.bcel; - -import java.io.IOException; - -import org.aspectj.weaver.*; -import org.aspectj.weaver.patterns.*; - -public class NonstaticWeaveTestCase extends WeaveTestCase { - { - regenerate = false; - } - - public NonstaticWeaveTestCase(String name) { - super(name); - } - - - public void testBefore() throws IOException { - String s = "before(): get(* *.*) -> void Aspect.ajc_before()"; - PerClause per = new PerSingleton(); - per = per.concretize(world.resolve("Aspect")); - - ShadowMunger myMunger = this.makeConcreteAdvice(s, 0, per); - - weaveTest(getStandardTargets(), "NonStaticBefore", myMunger); - } - - public void testBeforeCflow() throws IOException { - String s = "before(): get(* *.*) -> void Aspect.ajc_before()"; - PerClause per = new PatternParser("percflow(execution(void main(..)))").maybeParsePerClause(); - per.resolve(new TestScope(new String[0], new String[0], world)); - - ResolvedType onAspect = world.resolve("Aspect"); - CrosscuttingMembers xcut = new CrosscuttingMembers(onAspect,true); - onAspect.crosscuttingMembers = xcut; - - per = per.concretize(onAspect); - - ShadowMunger myMunger = this.makeConcreteAdvice(s, 0, per); - - xcut.addConcreteShadowMunger(myMunger); - - - weaveTest(getStandardTargets(), "CflowNonStaticBefore", xcut.getShadowMungers()); - } - - public void testBeforePerThis() throws IOException { - String s = "before(): call(* println(..)) -> void Aspect.ajc_before()"; - PerClause per = new PatternParser("pertarget(call(* println(..)))").maybeParsePerClause(); - per.resolve(new TestScope(new String[0], new String[0], world)); - - ResolvedType onAspect = world.resolve("Aspect"); - CrosscuttingMembers xcut = new CrosscuttingMembers(onAspect,true); - onAspect.crosscuttingMembers = xcut; - per = per.concretize(onAspect); - - ShadowMunger myMunger = this.makeConcreteAdvice(s, 0, per); - xcut.addConcreteShadowMunger(myMunger); - -// List mungers = new ArrayList(); -// mungers.add(myMunger); -// mungers.addAll(onAspect.getExtraConcreteShadowMungers()); - - - weaveTest(getStandardTargets(), "PerThisNonStaticBefore", xcut.getShadowMungers()); - } - - - -} diff --git a/weaver/testsrc/org/aspectj/weaver/bcel/PatternWeaveTestCase.java b/weaver/testsrc/org/aspectj/weaver/bcel/PatternWeaveTestCase.java deleted file mode 100644 index 2d9af5395..000000000 --- a/weaver/testsrc/org/aspectj/weaver/bcel/PatternWeaveTestCase.java +++ /dev/null @@ -1,122 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - -package org.aspectj.weaver.bcel; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import org.aspectj.weaver.CompressingDataOutputStream; -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.Shadow; -import org.aspectj.weaver.VersionedDataInputStream; -import org.aspectj.weaver.patterns.ConstantPoolSimulator; -import org.aspectj.weaver.patterns.FormalBinding; -import org.aspectj.weaver.patterns.Pointcut; -import org.aspectj.weaver.patterns.SimpleScope; - -public class PatternWeaveTestCase extends WeaveTestCase { - { - regenerate = false; - } - - public PatternWeaveTestCase(String name) { - super(name); - } - - String[] none = new String[0]; - - // XXX this test is incompatible with optimizations made to weaver - - public void testPublic() throws IOException { - String[] publicHello = new String[] { "method-execution(void HelloWorld.main(java.lang.String[]))", }; - String[] publicFancyHello = new String[] { "method-execution(void FancyHelloWorld.main(java.lang.String[]))", - "method-execution(java.lang.String FancyHelloWorld.getName())", }; - checkPointcut("execution(public * *(..))", publicHello, publicFancyHello); - } - - // - // public void testPrintln() throws IOException { - // String[] callPrintlnHello = new String[] { - // "method-call(void java.io.PrintStream.println(java.lang.String))", - // }; - // String[] callPrintlnFancyHello = new String[] { - // "method-call(void java.io.PrintStream.println(java.lang.String))", - // "method-call(void java.io.PrintStream.println(java.lang.String))", - // "method-call(void java.io.PrintStream.println(java.lang.Object))", - // }; - // checkPointcut("call(* println(*))", callPrintlnHello, callPrintlnFancyHello); - // } - // - // public void testMumble() throws IOException { - // checkPointcut("call(* mumble(*))", none, none); - // } - // - // public void testFooBar() throws IOException { - // checkPointcut("call(FooBar *(..))", none, none); - // } - // - // public void testGetOut() throws IOException { - // String[] getOutHello = new String[] { - // "field-get(java.io.PrintStream java.lang.System.out)", - // }; - // - // checkPointcut("get(* java.lang.System.out)", getOutHello, getOutHello); - // } - // - // // private Pointcut makePointcut(String s) { - // // return new PatternParser(s).parsePointcut(); - // // } - // - private void checkPointcut(String pointcutSource, String[] expectedHelloShadows, String[] expectedFancyShadows) - throws IOException { - Pointcut sp = Pointcut.fromString(pointcutSource); - Pointcut rp = sp.resolve(new SimpleScope(world, FormalBinding.NONE)); - Pointcut cp = rp.concretize(ResolvedType.MISSING, ResolvedType.MISSING, 0); - - final List l = new ArrayList(); - BcelAdvice p = new BcelAdvice(null, cp, null, 0, -1, -1, null, null) { - public boolean implementOn(Shadow shadow) { - l.add(shadow); - return true; - } - }; - weaveTest(new String[] { "HelloWorld" }, "PatternWeave", p); - - checkShadowSet(l, expectedHelloShadows); - - l.clear(); - weaveTest(new String[] { "FancyHelloWorld" }, "PatternWeave", p); - - checkShadowSet(l, expectedFancyShadows); - - checkSerialize(rp); - } - - public void checkSerialize(Pointcut p) throws IOException { - ByteArrayOutputStream bo = new ByteArrayOutputStream(); - ConstantPoolSimulator cps = new ConstantPoolSimulator(); - CompressingDataOutputStream out = new CompressingDataOutputStream(bo, cps); - p.write(out); - out.close(); - - ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray()); - VersionedDataInputStream in = new VersionedDataInputStream(bi, cps); - Pointcut newP = Pointcut.read(in, null); - - assertEquals("write/read", p, newP); - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/bcel/PointcutResidueTestCase.java b/weaver/testsrc/org/aspectj/weaver/bcel/PointcutResidueTestCase.java deleted file mode 100644 index 7f6f5f163..000000000 --- a/weaver/testsrc/org/aspectj/weaver/bcel/PointcutResidueTestCase.java +++ /dev/null @@ -1,189 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - -package org.aspectj.weaver.bcel; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.List; - -import org.aspectj.weaver.AdviceKind; -import org.aspectj.weaver.CompressingDataOutputStream; -import org.aspectj.weaver.CrosscuttingMembers; -import org.aspectj.weaver.MemberImpl; -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.ShadowMunger; -import org.aspectj.weaver.UnresolvedType; -import org.aspectj.weaver.VersionedDataInputStream; -import org.aspectj.weaver.patterns.ConstantPoolSimulator; -import org.aspectj.weaver.patterns.Pointcut; -import org.aspectj.weaver.patterns.SimpleScope; - -public class PointcutResidueTestCase extends WeaveTestCase { - { - regenerate = false; - } - - public PointcutResidueTestCase(String name) { - super(name); - } - - String[] none = new String[0]; - - // ----- - - // ---- - - public void testArgResidue1() throws IOException { - checkMultiArgWeave("StringResidue1", - "call(* *(java.lang.Object, java.lang.Object)) && args(java.lang.String, java.lang.String)"); - } - - public void testArgResidue2() throws IOException { - checkMultiArgWeave("StringResidue2", "call(* *(java.lang.Object, java.lang.Object)) && args(.., java.lang.String)"); - } - - public void testArgResidue3() throws IOException { - checkMultiArgWeave("StringResidue3", "call(* *(java.lang.Object, java.lang.Object)) && args(java.lang.String, ..)"); - } - - // BETAX this is a beta feature. - // public void testArgResidue4() throws IOException { - // checkMultiArgWeave( - // "StringResidue4", - // "call(* *(java.lang.Object, java.lang.Object)) && args(.., java.lang.String, ..)"); - // } - - public void testMultiArgState() throws IOException { - checkWeave("StateResidue", "MultiArgHelloWorld", "call(* *(java.lang.Object, java.lang.Object)) && args(s, ..)", - new String[] { "java.lang.String" }, new String[] { "s" }); - checkWeave("StateResidue", "MultiArgHelloWorld", "call(* *(java.lang.Object, java.lang.Object)) && args(s, *)", - new String[] { "java.lang.String" }, new String[] { "s" }); - } - - public void testAdd() throws IOException { - checkDynamicWeave("AddResidue", "call(public * add(..)) && target(java.util.ArrayList)"); - checkDynamicWeave("AddResidue", "call(public * add(..)) && (target(java.util.ArrayList) || target(java.lang.String))"); - checkDynamicWeave("AddResidue", - "call(public * add(..)) && this(java.io.Serializable) && target(java.util.ArrayList) && !this(java.lang.Integer)"); - } - - public void testNot() throws IOException { - checkDynamicWeave("AddNotResidue", "call(public * add(..)) && !target(java.util.ArrayList)"); - checkDynamicWeave("AddNotResidue", "call(public * add(..)) && !(target(java.util.ArrayList) || target(java.lang.String)) "); - checkDynamicWeave("AddNotResidue", "call(public * add(..)) && target(java.lang.Object) && !target(java.util.ArrayList)"); - } - - public void testState() throws IOException { - checkWeave("AddStateResidue", "DynamicHelloWorld", "call(public * add(..)) && target(list)", - new String[] { "java.util.ArrayList" }, new String[] { "list" }); - checkWeave("AddStateResidue", "DynamicHelloWorld", "target(foo) && !target(java.lang.Integer) && call(public * add(..))", - new String[] { "java.util.ArrayList" }, new String[] { "foo" }); - checkDynamicWeave("AddResidue", "call(public * add(..)) && (target(java.util.ArrayList) || target(java.lang.String))"); - checkDynamicWeave("AddResidue", - "call(public * add(..)) && this(java.io.Serializable) && target(java.util.ArrayList) && !this(java.lang.Integer)"); - } - - public void testNoResidueArgs() throws IOException { - checkDynamicWeave("NoResidue", "call(public * add(..)) && args(java.lang.Object)"); - checkDynamicWeave("NoResidue", "call(public * add(..)) && args(*)"); - checkDynamicWeave("NoResidue", "call(public * add(..))"); - } - - // ---- cflow tests - - public void testCflowState() throws IOException { - checkWeave("CflowStateResidue", "DynamicHelloWorld", - "cflow(call(public * add(..)) && target(list)) && execution(public void main(..))", - new String[] { "java.util.ArrayList" }, new String[] { "list" }); - // checkWeave( - // "CflowStateResidue", - // "DynamicHelloWorld", - // "cflow(call(public * add(..)) && target(list)) && this(obj) && execution(public void doit(..))", - // new String[] { "java.lang.Object", "java.util.ArrayList" }, - // new String[] { "obj", "list" }); - // checkWeave( - // "AddStateResidue", - // "DynamicHelloWorld", - // "target(foo) && !target(java.lang.Integer) && call(public * add(..))", - // new String[] { "java.util.ArrayList" }, - // new String[] { "foo" }); - // checkDynamicWeave( - // "AddResidue", - // "call(public * add(..)) && (target(java.util.ArrayList) || target(java.lang.String))"); - // checkDynamicWeave( - // "AddResidue", - // "call(public * add(..)) && this(java.io.Serializable) && target(java.util.ArrayList) && !this(java.lang.Integer)"); - } - - // ---- - - private void checkDynamicWeave(String label, String pointcutSource) throws IOException { - checkWeave(label, "DynamicHelloWorld", pointcutSource, new String[0], new String[0]); - } - - private void checkMultiArgWeave(String label, String pointcutSource) throws IOException { - checkWeave(label, "MultiArgHelloWorld", pointcutSource, new String[0], new String[0]); - } - - private void checkWeave(String label, String filename, String pointcutSource, String[] formalTypes, String[] formalNames) - throws IOException { - final Pointcut sp = Pointcut.fromString(pointcutSource); - final Pointcut rp = sp.resolve(new SimpleScope(world, SimpleScope.makeFormalBindings(UnresolvedType.forNames(formalTypes), - formalNames))); - - ShadowMunger pp = new BcelAdvice(AdviceKind.Before, rp, MemberImpl.method(UnresolvedType.forName("Aspect"), - Modifier.STATIC, "ajc_before_0", - MemberImpl.typesToSignature(UnresolvedType.VOID, UnresolvedType.forNames(formalTypes), false)), 0, -1, -1, null, - null); - - ResolvedType inAspect = world.resolve("Aspect"); - CrosscuttingMembers xcut = new CrosscuttingMembers(inAspect, true); - inAspect.crosscuttingMembers = xcut; - - ShadowMunger cp = pp.concretize(inAspect, world, null); - - xcut.addConcreteShadowMunger(cp); - - // System.out.println("extras: " + inAspect.getExtraConcreteShadowMungers()); - // List advice = new ArrayList(); - // advice.add(cp); - // advice.addAll(inAspect.getExtraConcreteShadowMungers()); - weaveTest(new String[] { filename }, label, xcut.getShadowMungers()); - - checkSerialize(rp); - } - - public void weaveTest(String name, String outName, ShadowMunger planner) throws IOException { - List l = new ArrayList(1); - l.add(planner); - weaveTest(name, outName, l); - } - - public void checkSerialize(Pointcut p) throws IOException { - ByteArrayOutputStream bo = new ByteArrayOutputStream(); - ConstantPoolSimulator cps = new ConstantPoolSimulator(); - CompressingDataOutputStream out = new CompressingDataOutputStream(bo, cps); - p.write(out); - out.close(); - - ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray()); - VersionedDataInputStream in = new VersionedDataInputStream(bi, cps); - Pointcut newP = Pointcut.read(in, null); - - assertEquals("write/read", p, newP); - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/bcel/TjpWeaveTestCase.java b/weaver/testsrc/org/aspectj/weaver/bcel/TjpWeaveTestCase.java deleted file mode 100644 index b24b3f6d0..000000000 --- a/weaver/testsrc/org/aspectj/weaver/bcel/TjpWeaveTestCase.java +++ /dev/null @@ -1,99 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - -package org.aspectj.weaver.bcel; - -import java.io.IOException; -import java.util.Arrays; - -import org.aspectj.weaver.Advice; -import org.aspectj.weaver.AdviceKind; -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.ShadowMunger; -import org.aspectj.weaver.TestUtils; -import org.aspectj.weaver.UnresolvedType; - -public class TjpWeaveTestCase extends WeaveTestCase { - { - regenerate = false; - } - - public TjpWeaveTestCase(String name) { - super(name); - } - - public void setUp() throws Exception { - super.setUp(); - behave15 = true; - } - - public void tearDown() throws Exception { - super.tearDown(); - behave15 = false; - } - - public void testStaticTjp() throws IOException { - BcelAdvice munger = new BcelAdvice(AdviceKind.stringToKind("before"), makePointcutAll(), - TestUtils.methodFromString("static void Aspect.ajc_before(org.aspectj.lang.JoinPoint$StaticPart)"), - Advice.ThisJoinPointStaticPart, -1, -1, null, null); - - weaveTest("HelloWorld", "StaticTjpBeforeHelloWorld", munger); - } - - public void testEnclosingStaticTjp() throws IOException { - BcelAdvice munger = new BcelAdvice(AdviceKind.stringToKind("before"), makePointcutAll(), - TestUtils.methodFromString("static void Aspect.ajc_before(org.aspectj.lang.JoinPoint$StaticPart)"), - Advice.ThisEnclosingJoinPointStaticPart, -1, -1, null, null); - - weaveTest("HelloWorld", "StaticEnclosingTjpBeforeHelloWorld", munger); - } - - public void testTjp() throws IOException { - BcelAdvice munger = new BcelAdvice(AdviceKind.stringToKind("before"), makePointcutAll(), - TestUtils.methodFromString("static void Aspect.ajc_before(org.aspectj.lang.JoinPoint)"), Advice.ThisJoinPoint, -1, - -1, null, null); - - weaveTest("HelloWorld", "TjpBeforeHelloWorld", munger); - } - - public void testAroundTjp() throws IOException { - BcelAdvice munger = new BcelAdvice( - AdviceKind.stringToKind("around"), - makePointcutAll(), - TestUtils - .methodFromString("static java.lang.Object Aspect.ajc_around(org.aspectj.runtime.internal.AroundClosure, org.aspectj.lang.JoinPoint)"), - Advice.ThisJoinPoint | Advice.ExtraArgument, -1, -1, null, null); - - weaveTest("HelloWorld", "TjpAroundHelloWorld", munger); - } - - public void testAround2Tjp() throws IOException { - ResolvedType rtx = world.resolve(UnresolvedType.forName("Aspect"), true); - assertTrue("Couldnt find type Aspect", !rtx.isMissing()); - BcelAdvice munger1 = new BcelAdvice( - AdviceKind.stringToKind("around"), - makePointcutAll(), - TestUtils - .methodFromString("static java.lang.Object Aspect.ajc_around(org.aspectj.runtime.internal.AroundClosure, org.aspectj.lang.JoinPoint)"), - Advice.ThisJoinPoint | Advice.ExtraArgument, -1, -1, null, rtx); - - BcelAdvice munger2 = new BcelAdvice( - AdviceKind.stringToKind("around"), - makePointcutAll(), - TestUtils - .methodFromString("static java.lang.Object Aspect.ajc_around(org.aspectj.runtime.internal.AroundClosure, org.aspectj.lang.JoinPoint)"), - Advice.ThisJoinPoint | Advice.ExtraArgument, -1, -1, null, rtx); - - weaveTest("HelloWorld", "TjpAround2HelloWorld", Arrays.asList(new ShadowMunger[] { munger1, munger2 })); - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/bcel/TraceJarWeaveTestCase.java b/weaver/testsrc/org/aspectj/weaver/bcel/TraceJarWeaveTestCase.java deleted file mode 100644 index 45535407b..000000000 --- a/weaver/testsrc/org/aspectj/weaver/bcel/TraceJarWeaveTestCase.java +++ /dev/null @@ -1,40 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - - -package org.aspectj.weaver.bcel; - -import java.io.IOException; - -public class TraceJarWeaveTestCase extends WeaveTestCase { - { - regenerate = false; - } - - public TraceJarWeaveTestCase(String name) { - super(name); - } - - - public void testTraceJar() throws IOException { - world = new BcelWorld(getTraceJar()); - BcelWeaver weaver = new BcelWeaver(world); - weaver.addLibraryAspect("MyTrace"); - UnwovenClassFile classFile - = makeUnwovenClassFile(classDir, "DynamicHelloWorld", outDirPath); - - weaver.addClassFile(classFile,false); - weaver.prepareForWeave(); - - weaveTestInner(weaver, classFile, "DynamicHelloWorld", "TraceJarHello"); - } -} diff --git a/weaver/testsrc/org/aspectj/weaver/bcel/UtilityTestCase.java b/weaver/testsrc/org/aspectj/weaver/bcel/UtilityTestCase.java deleted file mode 100644 index 8083c1532..000000000 --- a/weaver/testsrc/org/aspectj/weaver/bcel/UtilityTestCase.java +++ /dev/null @@ -1,52 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - - -package org.aspectj.weaver.bcel; -import java.io.*; - -import junit.framework.TestCase; - -public class UtilityTestCase extends TestCase { - - public UtilityTestCase(String name) { - super(name); - } - - public void disassembleTest(String name) throws IOException { - BcelWorld world = new BcelWorld("../weaver/bin"); -// world.setFastDelegateSupport(false); - world.addPath(WeaveTestCase.classDir); - - LazyClassGen clazz = new LazyClassGen(BcelWorld.getBcelObjectType(world.resolve(name))); - clazz.print(); - System.out.println(); - } - - - public void testHelloWorld() throws IOException { - disassembleTest("Test"); - } - public void testFancyHelloWorld() throws IOException { - disassembleTest("FancyHelloWorld"); - } -// public void testSwitchy() throws IOException { -// disassembleTest("TestSwitchy"); -// } - - public static void main(String[] args) throws IOException { - BcelWorld world = new BcelWorld(); - LazyClassGen clazz = new LazyClassGen(BcelWorld.getBcelObjectType(world.resolve(args[0]))); - clazz.print(); - } -} - diff --git a/weaver/testsrc/org/aspectj/weaver/bcel/WeaveOrderTestCase.java b/weaver/testsrc/org/aspectj/weaver/bcel/WeaveOrderTestCase.java deleted file mode 100644 index f22805f11..000000000 --- a/weaver/testsrc/org/aspectj/weaver/bcel/WeaveOrderTestCase.java +++ /dev/null @@ -1,149 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - - -package org.aspectj.weaver.bcel; - -import org.aspectj.weaver.patterns.*; -import org.aspectj.weaver.*; - -/**. - */ -public class WeaveOrderTestCase extends WeaveTestCase { - { - regenerate = false; - } - - public WeaveOrderTestCase(String name) { - super(name); - } - - - public void testLexicalOrder() { - Advice a1 = - makeConcreteAdvice(AdviceKind.Before, UnresolvedType.OBJECT, UnresolvedType.OBJECT, 1); - Advice a2 = - makeConcreteAdvice(AdviceKind.Before, UnresolvedType.OBJECT, UnresolvedType.THROWABLE, 2); - - assertEquals(-1, a2.compareTo(a1)); - assertEquals(+1, a1.compareTo(a2)); - } - - public void testLexicalOrderWithAfter() { - Advice a1 = - makeConcreteAdvice(AdviceKind.Before, UnresolvedType.OBJECT, UnresolvedType.OBJECT, 1); - Advice a2 = - makeConcreteAdvice(AdviceKind.After, UnresolvedType.OBJECT, UnresolvedType.THROWABLE, 2); - - assertEquals(+1, a2.compareTo(a1)); - assertEquals(-1, a1.compareTo(a2)); - - a1 = - makeConcreteAdvice(AdviceKind.After, UnresolvedType.OBJECT, UnresolvedType.OBJECT, 1); - a2 = - makeConcreteAdvice(AdviceKind.After, UnresolvedType.OBJECT, UnresolvedType.THROWABLE, 2); - - assertEquals(+1, a2.compareTo(a1)); - assertEquals(-1, a1.compareTo(a2)); - } - - public void testSubtypes() { - Advice a1 = - makeConcreteAdvice(AdviceKind.Before, UnresolvedType.OBJECT, UnresolvedType.OBJECT, 1); - Advice a2 = - makeConcreteAdvice(AdviceKind.Before, UnresolvedType.THROWABLE, UnresolvedType.OBJECT, 1); - Advice a3 = - makeConcreteAdvice(AdviceKind.Before, UnresolvedType.forName("java.lang.String"), UnresolvedType.OBJECT, 1); - - assertEquals(+1, a2.compareTo(a1)); - assertEquals(-1, a1.compareTo(a2)); - - assertEquals(+1, a3.compareTo(a1)); - assertEquals(-1, a1.compareTo(a3)); - - assertEquals(0, a3.compareTo(a2)); - assertEquals(0, a2.compareTo(a3)); - } - - - public void testDominates() { - Declare dom = - new PatternParser("declare precedence: java.lang.String, java.lang.Throwable").parseDeclare(); - //??? concretize dom - ResolvedType aType = world.resolve("Aspect"); - CrosscuttingMembers xcut = new CrosscuttingMembers(aType,true); - aType.crosscuttingMembers = xcut; - xcut.addDeclare(dom); - world.getCrosscuttingMembersSet().addFixedCrosscuttingMembers(aType); - - Advice a1 = - makeConcreteAdvice(AdviceKind.Before, UnresolvedType.OBJECT, UnresolvedType.OBJECT, 1); - Advice a2 = - makeConcreteAdvice(AdviceKind.Before, UnresolvedType.OBJECT, UnresolvedType.THROWABLE, 2); - Advice a3 = - makeConcreteAdvice(AdviceKind.Before, UnresolvedType.OBJECT, UnresolvedType.forName("java.lang.String"), 2); - - assertEquals(-1, a2.compareTo(a1)); - assertEquals(+1, a1.compareTo(a2)); - - assertEquals(-1, a3.compareTo(a1)); - assertEquals(+1, a1.compareTo(a3)); - - - assertEquals(+1, a3.compareTo(a2)); - assertEquals(-1, a2.compareTo(a3)); - } - - public void testDominatesHarder() { - Declare dom = - new PatternParser("declare precedence: *, java.lang.String, java.lang.Throwable").parseDeclare(); - //??? concretize dom - ResolvedType aType = world.resolve("Aspect"); - CrosscuttingMembers xcut = new CrosscuttingMembers(aType,true); - aType.crosscuttingMembers = xcut; - xcut.addDeclare(dom); - world.getCrosscuttingMembersSet().addFixedCrosscuttingMembers(aType); - - Advice a1 = - makeConcreteAdvice(AdviceKind.Before, UnresolvedType.OBJECT, UnresolvedType.OBJECT, 2); - Advice a2 = - makeConcreteAdvice(AdviceKind.Before, UnresolvedType.OBJECT, UnresolvedType.THROWABLE, 1); - Advice a3 = - makeConcreteAdvice(AdviceKind.Before, UnresolvedType.OBJECT, UnresolvedType.forName("java.lang.String"), 1); - - assertEquals(-1, a2.compareTo(a1)); - assertEquals(+1, a1.compareTo(a2)); - - assertEquals(-1, a3.compareTo(a1)); - assertEquals(+1, a1.compareTo(a3)); - - - assertEquals(+1, a3.compareTo(a2)); - assertEquals(-1, a2.compareTo(a3)); - } - - - - - private Advice makeConcreteAdvice(AdviceKind kind, UnresolvedType declaringAspect, - UnresolvedType concreteAspect, int lexicalPosition) - { - Advice a1 = new BcelAdvice(kind, makeResolvedPointcut("this(*)"), - MemberImpl.method(declaringAspect, 0, "foo", "()V"), - 0, lexicalPosition, lexicalPosition, null, null); - a1 = (Advice)a1.concretize(concreteAspect.resolve(world), world, null); - return a1; - } - - - -} diff --git a/weaver/testsrc/org/aspectj/weaver/bcel/WeaveTestCase.java b/weaver/testsrc/org/aspectj/weaver/bcel/WeaveTestCase.java deleted file mode 100644 index d39299ace..000000000 --- a/weaver/testsrc/org/aspectj/weaver/bcel/WeaveTestCase.java +++ /dev/null @@ -1,318 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - -package org.aspectj.weaver.bcel; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintStream; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import org.aspectj.apache.bcel.Constants; -import org.aspectj.apache.bcel.generic.InstructionFactory; -import org.aspectj.apache.bcel.generic.InstructionList; -import org.aspectj.apache.bcel.generic.InvokeInstruction; -import org.aspectj.apache.bcel.generic.Type; -import org.aspectj.testing.util.TestUtil; -import org.aspectj.util.FileUtil; -import org.aspectj.util.LangUtil; -import org.aspectj.weaver.Advice; -import org.aspectj.weaver.BcweaverTests; -import org.aspectj.weaver.ShadowMunger; -import org.aspectj.weaver.patterns.FormalBinding; -import org.aspectj.weaver.patterns.PerClause; -import org.aspectj.weaver.patterns.Pointcut; -import org.aspectj.weaver.patterns.SimpleScope; - -import junit.framework.TestCase; - -public abstract class WeaveTestCase extends TestCase { - - public boolean regenerate = false; - public boolean runTests = true; - public boolean behave15 = false; - - File outDir; - String outDirPath; - - public BcelWorld world = new BcelWorld(); - { - world.addPath(classDir); - // Some of the tests in here rely on comparing output from dumping the delegates - if - // we are using ASM delegates we don't know the names of parameters (they are irrelevant...) - // and are missing from the dumping of asm delegates. This switch ensures we - // continue to use BCEL for these tests. - // world.setFastDelegateSupport(false); - } - - public WeaveTestCase(String name) { - super(name); - } - - @Override - public void setUp() throws Exception { - outDir = BcweaverTests.getOutdir(); - outDirPath = outDir.getAbsolutePath(); - } - - @Override - public void tearDown() throws Exception { - super.tearDown(); - BcweaverTests.removeOutDir(); - outDir = null; - outDirPath = null; - } - - public static InstructionList getAdviceTag(BcelShadow shadow, String where) { - String methodName = "ajc_" + where + "_" + shadow.getKind().toLegalJavaIdentifier(); - - InstructionFactory fact = shadow.getFactory(); - InvokeInstruction il = fact.createInvoke("Aspect", methodName, Type.VOID, new Type[] {}, Constants.INVOKESTATIC); - return new InstructionList(il); - } - - public void weaveTest(String name, String outName, ShadowMunger planner) throws IOException { - List l = new ArrayList(1); - l.add(planner); - weaveTest(name, outName, l); - } - - // static String classDir = "../weaver/bin"; - static String classDir = BcweaverTests.TESTDATA_PATH + File.separator + "bin"; - - public void weaveTest(String name, String outName, List planners) throws IOException { - BcelWeaver weaver = new BcelWeaver(world); - try { - if (behave15) - world.setBehaveInJava5Way(true); - - UnwovenClassFile classFile = makeUnwovenClassFile(classDir, name, outDirPath); - - weaver.addClassFile(classFile, false); - weaver.setShadowMungers(planners); - weaveTestInner(weaver, classFile, name, outName); - } finally { - if (behave15) - world.setBehaveInJava5Way(false); - } - } - - protected void weaveTestInner(BcelWeaver weaver, UnwovenClassFile classFile, String name, String outName) throws IOException { - // int preErrors = currentResult.errorCount(); - BcelObjectType classType = BcelWorld.getBcelObjectType(world.resolve(classFile.getClassName())); - LazyClassGen gen = weaver.weave(classFile, classType); - if (gen == null) { - // we didn't do any weaving, but let's make a gen anyway - gen = classType.getLazyClassGen(); // new LazyClassGen(classType); - } - try { - String filenameToUse = findMostRelevantFile(outName); - checkClass(gen, outDirPath, filenameToUse); - if (runTests) { - System.out.println("*******RUNNING: " + outName + " " + name + " *******"); - TestUtil.runMain(makeClassPath(outDirPath), name); - } - } catch (Error e) { - System.err.println("Comparing to " + outName + ".txt"); - gen.print(System.err); - throw e; - } catch (RuntimeException e) { - gen.print(System.err); - throw e; - } - } - - public String findMostRelevantFile(String name) { - double version = LangUtil.getVmVersion(); - while (version > 0) { - String possibleFileName = name+"."+Double.toString(version)+".txt"; - if (new File(TESTDATA_DIR, possibleFileName).exists()) { - return possibleFileName; - } - version--; - } - // Use the standard file - return name+".txt"; - } - - public String makeClassPath(String outDir) { - return outDir + File.pathSeparator + getTraceJar() + File.pathSeparator + classDir + File.pathSeparator - + System.getProperty("java.class.path"); - } - - /** - * '/' in the name indicates the location of the class - */ - public static UnwovenClassFile makeUnwovenClassFile(String classDir, String name, String outDir) throws IOException { - File outFile = new File(outDir, name + ".class"); - if (classDir.endsWith(".jar")) { - String fname = name + ".class"; - UnwovenClassFile ret = new UnwovenClassFile(outFile.getAbsolutePath(), FileUtil.readAsByteArray(FileUtil - .getStreamFromZip(classDir, fname))); - return ret; - } else { - File inFile = new File(classDir, name + ".class"); - return new UnwovenClassFile(outFile.getAbsolutePath(), FileUtil.readAsByteArray(inFile)); - } - } - - public void checkClass(LazyClassGen gen, String outDir, String expectedFile) throws IOException { - if (regenerate) - genClass(gen, outDir, expectedFile); - else - realCheckClass(gen, outDir, expectedFile); - } - - static final File TESTDATA_DIR = new File(BcweaverTests.TESTDATA_PATH); - - void genClass(LazyClassGen gen, String outDir, String expectedFile) throws IOException { - // ClassGen b = getJavaClass(outDir, className); - FileOutputStream out = new FileOutputStream(new File(TESTDATA_DIR, expectedFile)); - PrintStream ps = new PrintStream(out); - gen.print(ps); - ps.flush(); - - } - - void realCheckClass(LazyClassGen gen, String outDir, String expectedFile) throws IOException { - TestUtil.assertMultiLineStringEquals(expectedFile/* "classes" */, - FileUtil.readAsString(new File(TESTDATA_DIR, expectedFile)), gen.toLongString()); - } - - // ---- - public ShadowMunger makeConcreteAdvice(String mungerString) { - return makeConcreteAdvice(mungerString, 0, null); - } - - public ShadowMunger makeConcreteAdvice(String mungerString, int extraArgFlag) { - return makeConcreteAdvice(mungerString, extraArgFlag, null); - } - - protected ShadowMunger makeConcreteAdvice(String mungerString, int extraArgFlag, PerClause perClause) { - Advice myMunger = BcelTestUtils.shadowMunger(world, mungerString, extraArgFlag); - - // PerSingleton s = new PerSingleton(); - // s.concretize(world.resolve("Aspect")); - // System.err.println(((KindedPointcut)myMunger.getPointcut().getPointcut()).getKind()); - Advice cm = (Advice) myMunger.concretize(myMunger.getDeclaringAspect().resolve(world), world, perClause); - return cm; - } - - public ShadowMunger makeAdviceField(String kind, String extraArgType) { - return makeConcreteAdvice(kind + "(): get(* *.*) -> static void Aspect.ajc_" + kind + "_field_get(" + extraArgType + ")", 1); - } - - public List makeAdviceAll(String kind, boolean matchOnlyPrintln) { - List ret = new ArrayList(); - if (matchOnlyPrintln) { - ret.add(makeConcreteAdvice(kind + "(): call(* *.println(..)) -> static void Aspect.ajc_" + kind + "_method_execution()")); - } else { - ret.add(makeConcreteAdvice(kind + "(): call(* *.*(..)) -> static void Aspect.ajc_" + kind + "_method_call()")); - ret.add(makeConcreteAdvice(kind + "(): call(*.new(..)) -> static void Aspect.ajc_" + kind + "_constructor_call()")); - ret.add(makeConcreteAdvice(kind + "(): execution(* *.*(..)) -> static void Aspect.ajc_" + kind + "_method_execution()")); - ret.add(makeConcreteAdvice(kind + "(): execution(*.new(..)) -> static void Aspect.ajc_" + kind - + "_constructor_execution()")); - // ret.add( - // makeConcreteMunger( - // kind - // + "(): staticinitialization(*) -> static void Aspect.ajc_" - // + kind - // + "_staticinitialization()")); - ret.add(makeConcreteAdvice(kind + "(): get(* *.*) -> static void Aspect.ajc_" + kind + "_field_get()")); - // ret.add( - // makeConcreteMunger( - // kind + "(): set(* *.*) -> static void Aspect.ajc_" + kind + "_field_set()")); - // XXX no test for advice execution, staticInitialization or (god help us) preInitialization - } - return ret; - } - - public List makeAdviceAll(final String kind) { - return makeAdviceAll(kind, false); - } - - public Pointcut makePointcutAll() { - return makeConcretePointcut("get(* *.*) || call(* *.*(..)) || execution(* *.*(..)) || call(*.new(..)) || execution(*.new(..))"); - } - - public Pointcut makePointcutNoZeroArg() { - return makeConcretePointcut("call(* *.*(*, ..)) || execution(* *.*(*, ..)) || call(*.new(*, ..)) || execution(*.new(*, ..))"); - } - - public Pointcut makePointcutPrintln() { - return makeConcretePointcut("call(* *.println(..))"); - } - - public Pointcut makeConcretePointcut(String s) { - return makeResolvedPointcut(s).concretize(null, null, 0); - } - - public Pointcut makeResolvedPointcut(String s) { - Pointcut pointcut0 = Pointcut.fromString(s); - return pointcut0.resolve(new SimpleScope(world, FormalBinding.NONE)); - } - - // ---- - - public String[] getStandardTargets() { - return new String[] { "HelloWorld", "FancyHelloWorld" }; - } - - public String getTraceJar() { - return BcweaverTests.TESTDATA_PATH + "/tracing.jar"; - } - - // ---- - - protected void weaveTest(String[] inClassNames, String outKind, ShadowMunger patternMunger) throws IOException { - for (int i = 0; i < inClassNames.length; i++) { - String inFileName = inClassNames[i]; - weaveTest(inFileName, outKind + inFileName, patternMunger); - } - } - - protected void weaveTest(String[] inClassNames, String outKind, List patternMungers) throws IOException { - for (int i = 0; i < inClassNames.length; i++) { - String inFileName = inClassNames[i]; - weaveTest(inFileName, outKind + inFileName, patternMungers); - } - } - - protected List addLexicalOrder(List l) { - int i = 10; - for (ShadowMunger element: l) { - ((Advice)element).setLexicalPosition(i += 10); - } - return l; - } - - // XXX cut-and-paster from IdWeaveTestCase - public void checkShadowSet(List l, String[] ss) { - outer: for (int i = 0, len = ss.length; i < len; i++) { - // inner: - for (Iterator j = l.iterator(); j.hasNext();) { - BcelShadow shadow = (BcelShadow) j.next(); - String shadowString = shadow.toString(); - if (shadowString.equals(ss[i])) { - j.remove(); - continue outer; - } - } - assertTrue("didn't find " + ss[i] + " in " + l, false); - } - assertTrue("too many things in " + l, l.size() == 0); - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/bcel/WorldTestCase.java b/weaver/testsrc/org/aspectj/weaver/bcel/WorldTestCase.java deleted file mode 100644 index 3d21b57ad..000000000 --- a/weaver/testsrc/org/aspectj/weaver/bcel/WorldTestCase.java +++ /dev/null @@ -1,163 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - -package org.aspectj.weaver.bcel; - -import java.lang.reflect.Modifier; - -import org.aspectj.weaver.Advice; -import org.aspectj.weaver.BcweaverTests; -import org.aspectj.weaver.CommonWorldTests; -import org.aspectj.weaver.Member; -import org.aspectj.weaver.MemberImpl; -import org.aspectj.weaver.ResolvedMember; -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.ShadowMunger; -import org.aspectj.weaver.TestUtils; -import org.aspectj.weaver.UnresolvedType; -import org.aspectj.weaver.World; - -/** - * This is a test case for the nameType parts of worlds. - */ -public class WorldTestCase extends CommonWorldTests { - - private final BcelWorld world = new BcelWorld(BcweaverTests.TESTDATA_PATH + "/tracing.jar"); - - protected World getWorld() { - return world; - } - - protected boolean getSupportsAutoboxing() { - return true; - } - - // XXX fix the various XXXs before expecting this test to work - public void xtestTraceJar() { - ResolvedType trace = world.resolve(UnresolvedType.forName("Trace"), true); - assertTrue("Couldnt find type Trace", !trace.isMissing()); - fieldsTest(trace, Member.NONE); - /* Member constr = */TestUtils.methodFromString("void Trace.()"); - // XXX need attribute fix - - // methodsTest(trace, new Member[] { constr }); - - interfacesTest(trace, ResolvedType.NONE); - superclassTest(trace, UnresolvedType.OBJECT); - isInterfaceTest(trace, false); - isClassTest(trace, false); - isAspectTest(trace, true); - - pointcutsTest(trace, new Member[] { MemberImpl.pointcut(trace, "traced", "(Ljava/lang/Object;)V"), }); - - modifiersTest(trace.findPointcut("traced"), Modifier.PUBLIC | Modifier.ABSTRACT); - - mungersTest( - trace, - new ShadowMunger[] { - BcelTestUtils.shadowMunger(world, "before(foo): traced(foo) -> void Trace.ajc_before_4(java.lang.Object))", - 0), - BcelTestUtils - .shadowMunger( - world, - "afterReturning(foo): traced(foo) -> void Trace.ajc_afterreturning_3(java.lang.Object, java.lang.Object))", - Advice.ExtraArgument), - BcelTestUtils - .shadowMunger( - world, - "around(): execution(* doit(..)) -> java.lang.Object Trace.ajc_around_2(org.aspectj.runtime.internal.AroundClosure))", - Advice.ExtraArgument), - BcelTestUtils - .shadowMunger( - world, - "around(foo): traced(foo) -> java.lang.Object Trace.ajc_around_1(java.lang.Object, org.aspectj.runtime.internal.AroundClosure))", - Advice.ExtraArgument), }); - - ResolvedType myTrace = world.resolve(UnresolvedType.forName("MyTrace"), true); - assertTrue("Couldnt find type MyTrace", !myTrace.isMissing()); - - interfacesTest(myTrace, ResolvedType.NONE); - superclassTest(myTrace, trace); - isInterfaceTest(myTrace, false); - isClassTest(myTrace, false); - isAspectTest(myTrace, true); - - // XXX need attribute fix - - // fieldsTest(myTrace, Member.NONE); - - pointcutsTest(trace, new Member[] { MemberImpl.pointcut(trace, "traced", "(Ljava/lang/Object;)V"), }); - - modifiersTest(myTrace.findPointcut("traced"), Modifier.PUBLIC); - - // this tests for declared mungers - mungersTest(myTrace, ShadowMunger.NONE); - - } - - public void testIterator() { - int abstractPublic = Modifier.ABSTRACT | Modifier.PUBLIC; - ResolvedType iter = world.getCoreType(UnresolvedType.forRawTypeName("java.util.Iterator")); - - modifiersTest(iter, abstractPublic | Modifier.INTERFACE); - fieldsTest(iter, ResolvedMember.NONE); - methodsTest(iter, new Member[] { - MemberImpl.method(iter, 0, "hasNext", "()Z"), - MemberImpl.method(iter, 0, "remove", "()V"), - MemberImpl.method(iter, 0, "next", "()Ljava/lang/Object;"), - MemberImpl.method(iter, 0, "forEachRemaining", "(Ljava/util/function/Consumer;)V") -// default void forEachRemaining(Consumer action) { -// Objects.requireNonNull(action); -// while (hasNext()) -// action.accept(next()); -// } - }); - ResolvedMember remove = iter.lookupMethod(MemberImpl.method(iter, 0, "remove", "()V")); - assertNotNull("iterator doesn't have remove", remove); - modifiersTest(remove, Modifier.PUBLIC); // no longer abstract in Java8 (default instead) - exceptionsTest(remove, UnresolvedType.NONE); - - ResolvedMember clone = iter.lookupMethod(MemberImpl.method(UnresolvedType.OBJECT, 0, "clone", "()Ljava/lang/Object;")); - assertNotNull("iterator doesn't have clone", clone); - // AV: JRockit Object.clone() is not native.. corrupted test here: - // modifiersTest(clone, Modifier.PROTECTED | Modifier.NATIVE); - assertTrue("should be protected" + clone.toString(), Modifier.isProtected(clone.getModifiers())); - exceptionsTest(clone, UnresolvedType.forNames(new String[] { "java.lang.CloneNotSupportedException" })); - - interfacesTest(iter, ResolvedType.NONE); - superclassTest(iter, UnresolvedType.OBJECT); - pointcutsTest(iter, ResolvedMember.NONE); - mungersTest(iter, ShadowMunger.NONE); - isInterfaceTest(iter, true); - isClassTest(iter, false); - isAspectTest(iter, false); - } - - public void testObjectCoersion() { - assertCouldBeCoercibleFrom("java.lang.Object", "java.lang.String"); - assertCouldBeCoercibleFrom("java.lang.Integer", "java.lang.Object"); - assertCouldBeCoercibleFrom("java.io.Serializable", "java.lang.Runnable"); - assertCouldBeCoercibleFrom("java.util.Stack", "java.lang.Runnable"); - assertCouldNotBeCoercibleFrom("java.lang.Runnable", "java.lang.Integer"); - assertCouldNotBeCoercibleFrom("java.lang.Integer", "java.lang.String"); - assertCouldNotBeCoercibleFrom("java.lang.Integer", "java.lang.Runnable"); - } - - // ---- - - private void assertCouldBeCoercibleFrom(String a, String b) { - isCoerceableFromTest(world.resolve(a), world.resolve(b), true); - } - - private void assertCouldNotBeCoercibleFrom(String a, String b) { - isCoerceableFromTest(world.resolve(a), world.resolve(b), false); - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/bcel/ZipFileWeaver.java b/weaver/testsrc/org/aspectj/weaver/bcel/ZipFileWeaver.java deleted file mode 100644 index 3be5ae332..000000000 --- a/weaver/testsrc/org/aspectj/weaver/bcel/ZipFileWeaver.java +++ /dev/null @@ -1,39 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - - -package org.aspectj.weaver.bcel; - -import java.io.File; -import java.io.IOException; - - -//XXX delete very soon -public class ZipFileWeaver { - File inFile; - public ZipFileWeaver(File inFile) { - super(); - this.inFile = inFile; - } - - public void weave(BcelWeaver weaver, File outFile) throws IOException { - int count = 0; - long startTime = System.currentTimeMillis(); - weaver.addJarFile(inFile, new File("."),false); - weaver.weave(outFile); - long stopTime = System.currentTimeMillis(); - - - System.out.println("handled " + count + " entries, in " + - (stopTime-startTime)/1000. + " seconds"); - } -} diff --git a/weaver/testsrc/org/aspectj/weaver/bcel/ZipTestCase.java b/weaver/testsrc/org/aspectj/weaver/bcel/ZipTestCase.java deleted file mode 100644 index e9ef568b1..000000000 --- a/weaver/testsrc/org/aspectj/weaver/bcel/ZipTestCase.java +++ /dev/null @@ -1,121 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - -package org.aspectj.weaver.bcel; - -import java.io.File; -import java.io.IOException; -import java.util.Collection; - -import junit.framework.TestCase; - -import org.aspectj.weaver.BcweaverTests; - -public class ZipTestCase extends TestCase { - - File outDir; - - /** - * Constructor for ZipTestCase. - * - * @param arg0 - */ - public ZipTestCase(String arg0) { - super(arg0); - } - - public void setUp() { - outDir = BcweaverTests.getOutdir(); - } - - public void tearDown() { - BcweaverTests.removeOutDir(); - outDir = null; - } - - public void zipTest(String fileName, String aspectjar) throws IOException { - zipTest(fileName, aspectjar, false); - } - - public void zipTest(String fileName, String aspectjar, boolean isInJar) throws IOException { - File inFile = new File(fileName); - File outFile = new File(outDir, inFile.getName()); - BcelWorld world = new BcelWorld(); - BcelWeaver weaver = new BcelWeaver(world); - - long startTime = System.currentTimeMillis(); - // ensure that a fast cpu doesn't complete file write within 1000ms of start - try { - Thread.sleep(2000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - weaver.addJarFile(inFile, new File("."), false); - - if (aspectjar != null) { - if (isInJar) { - weaver.addJarFile(new File(aspectjar), new File("."), false); - } else { - weaver.addLibraryJarFile(new File(aspectjar)); - world.addPath(new File(aspectjar).toString()); - } - } - weaver.addLibraryJarFile(new File(BcweaverTests.TESTDATA_PATH + "/Regex.jar")); // ??? - world.addPath(new File(BcweaverTests.TESTDATA_PATH + "/Regex.jar").getPath()); - - Collection woven = weaver.weave(outFile); - long stopTime = System.currentTimeMillis(); - - System.out.println("handled " + woven.size() + " entries, in " + (stopTime - startTime) / 1000. + " seconds"); - // last mod times on linux (at least) are only accurate to the second. - // with fast disks and a fast cpu the following test can fail if the write completes less than - // 1000 milliseconds after the start of the test, hence the 1000ms delay added above. - assertTrue(outFile.lastModified() > startTime); - } - - public void testSmall() throws IOException { - zipTest(BcweaverTests.TESTDATA_PATH + "/Regex.jar", null); - } - - public void testSmallWithAspects() throws IOException { - System.out.println("could take 4 seconds..."); - zipTest(BcweaverTests.TESTDATA_PATH + "/Regex.jar", BcweaverTests.TESTDATA_PATH + "/megatrace.jar"); - } - - public void testSmallWithAspectsNoWeave() throws IOException { - System.out.println("could take 4 seconds..."); - zipTest(BcweaverTests.TESTDATA_PATH + "/Regex.jar", BcweaverTests.TESTDATA_PATH + "/megatraceNoweave.jar", true); - } - - public void testBig() throws IOException { - System.out.println("could take 4 seconds..."); - zipTest("../lib/bcel/bcel.jar", null); - } - - public void testBigWithEasyNoTrace() throws IOException { - System.out.println("could take 4 seconds..."); - zipTest("../lib/bcel/bcel.jar", BcweaverTests.TESTDATA_PATH + "/megatrace0easy.jar"); - } - - // this is something we test every now and again. - public void xtestBigWithHardNoTrace() throws IOException { - System.out.println("could take 24 seconds..."); - zipTest("../lib/bcel/bcel.jar", BcweaverTests.TESTDATA_PATH + "/megatrace0hard.jar"); - } - - public void xtestBigWithAspects() throws IOException { - System.out.println("could take 40 seconds..."); - zipTest("../lib/bcel/bcel.jar", BcweaverTests.TESTDATA_PATH + "/megatrace.jar"); - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/patterns/AnnotationPatternMatchingTestCase.java b/weaver/testsrc/org/aspectj/weaver/patterns/AnnotationPatternMatchingTestCase.java deleted file mode 100644 index e8999bafd..000000000 --- a/weaver/testsrc/org/aspectj/weaver/patterns/AnnotationPatternMatchingTestCase.java +++ /dev/null @@ -1,252 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2004 IBM Corporation. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Andy Clement initial implementation - * ******************************************************************/ -package org.aspectj.weaver.patterns; - -import java.util.ArrayList; -import java.util.List; - -import junit.framework.TestCase; - -import org.aspectj.bridge.AbortException; -import org.aspectj.bridge.IMessage; -import org.aspectj.bridge.IMessage.Kind; -import org.aspectj.bridge.IMessageHandler; -import org.aspectj.util.LangUtil; -import org.aspectj.weaver.BcweaverTests; -import org.aspectj.weaver.ResolvedMember; -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.bcel.BcelWorld; - -/* - * Sample types that this program uses are: - - import p.SimpleAnnotation; - - @SimpleAnnotation(id=2) - public class AnnotatedClass { - - @SimpleAnnotation(id=3) - public void m1() { } - - @SimpleAnnotation(id=4) - int i; - } - - * with SimpleAnnotation defined as: - - package p; - import java.lang.annotation.*; - - @Retention(RetentionPolicy.RUNTIME) - public @interface SimpleAnnotation { - int id(); - String fruit() default "bananas"; - } - - *NOTE NOTE NOTE NOTE NOTE NOTE NOTE* - If you need to rebuild the test data code, run 'ant -f build-15.xml' in the - testdata directory. - - */ -public class AnnotationPatternMatchingTestCase extends TestCase { - - private BcelWorld world; - private AnnotationTypePattern fooTP, simpleAnnotationTP; - - private ResolvedType loadType(String name) { - if (world == null) { - world = new BcelWorld(BcweaverTests.TESTDATA_PATH + "/testcode.jar"); - world.setBehaveInJava5Way(true); - } - return world.resolve(name); - } - - private void initAnnotationTypePatterns() { - PatternParser p = new PatternParser("@Foo"); - fooTP = p.maybeParseAnnotationPattern(); - fooTP = fooTP.resolveBindings(makeSimpleScope(), new Bindings(3), true); - - p = new PatternParser("@p.SimpleAnnotation"); - simpleAnnotationTP = p.maybeParseAnnotationPattern(); - simpleAnnotationTP = simpleAnnotationTP.resolveBindings(makeSimpleScope(), new Bindings(3), true); - } - - public void testAnnotationPatternMatchingOnTypes() { - if (LangUtil.is15VMOrGreater()) { - ResolvedType rtx = loadType("AnnotatedClass"); - initAnnotationTypePatterns(); - - // One should match - assertTrue("@Foo should not match on the AnnotatedClass", fooTP.matches(rtx).alwaysFalse()); - assertTrue("@SimpleAnnotation should match on the AnnotatedClass", simpleAnnotationTP.matches(rtx).alwaysTrue()); - } - - } - - static class MyMessageHandler implements IMessageHandler { - public List messages = new ArrayList(); - - public boolean handleMessage(IMessage message) throws AbortException { - messages.add(message); - return false; - } - - public boolean isIgnoring(Kind kind) { - return false; - } - - public void dontIgnore(IMessage.Kind kind) { - } - - public void ignore(Kind kind) { - } - } - - public void testReferenceToNonAnnotationType() { - // ResolvedType rtx = - loadType("AnnotatedClass"); // inits the world - PatternParser p = new PatternParser("@java.lang.String"); - - MyMessageHandler mh = new MyMessageHandler(); - world.setMessageHandler(mh); - AnnotationTypePattern atp = p.maybeParseAnnotationPattern(); - atp = atp.resolveBindings(makeSimpleScope(), new Bindings(3), true); - - assertTrue("Expected 1 error message but got " + mh.messages.size(), mh.messages.size() == 1); - - String expected = "Type referred to is not an annotation type"; - String msg = ((IMessage) mh.messages.get(0)).toString(); - assertTrue("Expected: " + expected + " but got " + msg, msg.indexOf(expected) != -1); - } - - public void testReferenceViaFormalToNonAnnotationType() { - // ResolvedType rtx = - loadType("AnnotatedClass"); // inits the world - PatternParser p = new PatternParser("a"); - - MyMessageHandler mh = new MyMessageHandler(); - world.setMessageHandler(mh); - AnnotationTypePattern atp = p.parseAnnotationNameOrVarTypePattern(); - atp = atp.resolveBindings(makeSimpleScope(), new Bindings(3), true); - - assertTrue("Expected 3 error messages but got " + mh.messages.size(), mh.messages.size() == 3); - - String expected = "Type referred to is not an annotation type"; - String msg = ((IMessage) mh.messages.get(0)).toString(); - assertTrue("Expected: " + expected + " but got " + msg, msg.indexOf(expected) != -1); - - // expected = "Binding not supported in @pcds (1.5.0 M1 limitation): null"; - // msg = ((IMessage)mh.messages.get(1)).toString(); - // assertTrue("Expected: "+expected+" but got "+msg,msg.indexOf(expected)!=-1); - } - - public TestScope makeSimpleScope() { - return new TestScope(new String[] { "int", "java.lang.String" }, new String[] { "a", "b" }, world); - } - - public void testUnresolvedAnnotationTypes() { - ResolvedType rtx = loadType("AnnotatedClass"); - - PatternParser p = new PatternParser("@Foo"); - AnnotationTypePattern fooTP = p.maybeParseAnnotationPattern(); - try { - fooTP.matches(rtx); - fail("Should have failed with illegal state exception, fooTP is not resolved"); - } catch (IllegalStateException ise) { - // Correct! - } - } - - public void testAnnotationPatternMatchingOnMethods() { - if (LangUtil.is15VMOrGreater()) { - ResolvedType rtx = loadType("AnnotatedClass"); - ResolvedMember aMethod = rtx.getDeclaredMethods()[1]; - - assertTrue("Haven't got the right method, I'm looking for 'm1()': " + aMethod.getName(), aMethod.getName().equals("m1")); - - initAnnotationTypePatterns(); - - // One should match - assertTrue("@Foo should not match on the AnnotatedClass.m1() method", fooTP.matches(aMethod).alwaysFalse()); - assertTrue("@SimpleAnnotation should match on the AnnotatedClass.m1() method", simpleAnnotationTP.matches(aMethod) - .alwaysTrue()); - } - } - - public void testAnnotationPatternMatchingOnFields() { - if (LangUtil.is15VMOrGreater()) { - ResolvedType rtx = loadType("AnnotatedClass"); - ResolvedMember aField = rtx.getDeclaredFields()[0]; - - assertTrue("Haven't got the right field, I'm looking for 'i'" + aField.getName(), aField.getName().equals("i")); - - initAnnotationTypePatterns(); - - // One should match - assertTrue("@Foo should not match on the AnnotatedClass.i field", fooTP.matches(aField).alwaysFalse()); - assertTrue("@SimpleAnnotation should match on the AnnotatedClass.i field", simpleAnnotationTP.matches(aField) - .alwaysTrue()); - } - - } - - public void testAnnotationTypeResolutionOnTypes() { - ResolvedType rtx = loadType("AnnotatedClass"); - ResolvedType[] types = rtx.getAnnotationTypes(); - assertTrue("Did not expect null", types != null); - assertTrue("Expected 1 entry but got " + types.length, types.length == 1); - assertTrue("Should be 'p.SimpleAnnotation' but is " + types[0], types[0].equals(world.resolve("p.SimpleAnnotation"))); - } - - public void testAnnotationTypeResolutionOnMethods() { - ResolvedType rtx = loadType("AnnotatedClass"); - - ResolvedMember aMethod = rtx.getDeclaredMethods()[1]; - assertTrue("Haven't got the right method, I'm looking for 'm1()': " + aMethod.getName(), aMethod.getName().equals("m1")); - - ResolvedType[] types = aMethod.getAnnotationTypes(); - assertTrue("Did not expect null", types != null); - assertTrue("Expected 1 entry but got " + types.length, types.length == 1); - assertTrue("Should be 'p.SimpleAnnotation' but is " + types[0], types[0].equals(world.resolve("p.SimpleAnnotation"))); - } - - public void testAnnotationTypeResolutionOnFields() { - ResolvedType rtx = loadType("AnnotatedClass"); - - ResolvedMember aField = rtx.getDeclaredFields()[0]; - - assertTrue("Haven't got the right field, I'm looking for 'i'" + aField.getName(), aField.getName().equals("i")); - - ResolvedType[] types = aField.getAnnotationTypes(); - assertTrue("Did not expect null", types != null); - assertTrue("Expected 1 entry but got " + types.length, types.length == 1); - assertTrue("Should be 'p.SimpleAnnotation' but is " + types[0], types[0].equals(world.resolve("p.SimpleAnnotation"))); - } - - public void testWildPatternMatchingOnTypes() { - - ResolvedType rtx = loadType("AnnotatedClass"); - initAnnotationTypePatterns(); - - // Let's create something wild - PatternParser p = new PatternParser("@(Foo || Boo)"); - AnnotationTypePattern ap = p.maybeParseAnnotationPattern(); - ap = ap.resolveBindings(makeSimpleScope(), new Bindings(3), true); - assertTrue("shouldnt match the type AnnotatedClass", ap.matches(rtx).alwaysFalse()); - - p = new PatternParser("@(p.SimpleAnnotation || Boo)"); - ap = p.maybeParseAnnotationPattern(); - ap = ap.resolveBindings(makeSimpleScope(), new Bindings(3), true); - assertTrue("should match the type AnnotatedClass", ap.matches(rtx).alwaysTrue()); - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/patterns/AnnotationPatternTestCase.java b/weaver/testsrc/org/aspectj/weaver/patterns/AnnotationPatternTestCase.java deleted file mode 100644 index 2d0820999..000000000 --- a/weaver/testsrc/org/aspectj/weaver/patterns/AnnotationPatternTestCase.java +++ /dev/null @@ -1,382 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2004 IBM Corporation. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * ******************************************************************/ -package org.aspectj.weaver.patterns; - -import junit.framework.TestCase; - -import org.aspectj.bridge.AbortException; -import org.aspectj.util.LangUtil; -import org.aspectj.weaver.AnnotatedElement; -import org.aspectj.weaver.AnnotationAJ; -import org.aspectj.weaver.BcweaverTests; -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.UnresolvedType; -import org.aspectj.weaver.bcel.BcelWorld; - -public class AnnotationPatternTestCase extends TestCase { - - public void testParseSimpleAnnotationPattern() { - PatternParser p = new PatternParser("@Foo"); - AnnotationTypePattern foo = p.maybeParseAnnotationPattern(); - foo = foo.resolveBindings(makeSimpleScope(), new Bindings(3), true); - assertTrue("ExactAnnotationTypePattern", foo instanceof ExactAnnotationTypePattern); - assertEquals("Foo", UnresolvedType.forSignature("LFoo;"), ((ExactAnnotationTypePattern) foo).annotationType); - } - - public void testParseAndAnnotationPattern() { - PatternParser p = new PatternParser("@Foo @Goo"); - AnnotationTypePattern fooAndGoo = p.maybeParseAnnotationPattern(); - assertTrue("AndAnnotationTypePattern", fooAndGoo instanceof AndAnnotationTypePattern); - assertEquals("@(Foo) @(Goo)", fooAndGoo.toString()); - fooAndGoo = fooAndGoo.resolveBindings(makeSimpleScope(), new Bindings(3), true); - assertEquals("@Foo @Goo", fooAndGoo.toString()); - AnnotationTypePattern left = ((AndAnnotationTypePattern) fooAndGoo).getLeft(); - AnnotationTypePattern right = ((AndAnnotationTypePattern) fooAndGoo).getRight(); - assertEquals("Foo", UnresolvedType.forSignature("LFoo;"), ((ExactAnnotationTypePattern) left).annotationType); - assertEquals("Goo", UnresolvedType.forSignature("LGoo;"), ((ExactAnnotationTypePattern) right).annotationType); - } - - // - // public void testParseOrAnnotationPattern() { - // PatternParser p = new PatternParser("@Foo || @Goo"); - // AnnotationTypePattern fooOrGoo = p.parseAnnotationTypePattern(); - // assertTrue("OrAnnotationTypePattern",fooOrGoo instanceof - // OrAnnotationTypePattern); - // assertEquals("(@Foo || @Goo)",fooOrGoo.toString()); - // AnnotationTypePattern left = - // ((OrAnnotationTypePattern)fooOrGoo).getLeft(); - // AnnotationTypePattern right = - // ((OrAnnotationTypePattern)fooOrGoo).getRight(); - // assertEquals("Foo",UnresolvedType.forName("Foo"),(( - // ExactAnnotationTypePattern)left).annotationType); - // assertEquals("Goo",UnresolvedType.forName("Goo"),(( - // ExactAnnotationTypePattern)right).annotationType); - // } - // - public void testParseNotAnnotationPattern() { - PatternParser p = new PatternParser("!@Foo"); - AnnotationTypePattern notFoo = p.maybeParseAnnotationPattern(); - assertTrue("NotAnnotationTypePattern", notFoo instanceof NotAnnotationTypePattern); - notFoo = notFoo.resolveBindings(makeSimpleScope(), new Bindings(3), true); - assertEquals("!@Foo", notFoo.toString()); - AnnotationTypePattern body = ((NotAnnotationTypePattern) notFoo).getNegatedPattern(); - assertEquals("Foo", UnresolvedType.forName("Foo"), ((ExactAnnotationTypePattern) body).annotationType); - } - - public void testParseBracketedAnnotationPattern() { - PatternParser p = new PatternParser("(@Foo)"); - AnnotationTypePattern foo = p.maybeParseAnnotationPattern(); - // cannot start with ( so, we get ANY - assertEquals("ANY", AnnotationTypePattern.ANY, foo); - } - - public void testParseFQAnnPattern() { - PatternParser p = new PatternParser("@org.aspectj.Foo"); - AnnotationTypePattern foo = p.maybeParseAnnotationPattern(); - assertEquals("@(org.aspectj.Foo)", foo.toString()); - } - - public void testParseComboPattern() { - // PatternParser p = new PatternParser("!((@Foo || @Goo) && !@Boo)"); - PatternParser p = new PatternParser("@(Foo || Goo)!@Boo"); - AnnotationTypePattern ap = p.maybeParseAnnotationPattern(); - ap = ap.resolveBindings(makeSimpleScope(), new Bindings(3), true); - AndAnnotationTypePattern atp = (AndAnnotationTypePattern) ap; - NotAnnotationTypePattern notBoo = (NotAnnotationTypePattern) atp.getRight(); - // ExactAnnotationTypePattern boo = (ExactAnnotationTypePattern) - notBoo.getNegatedPattern(); - // AnnotationTypePattern fooOrGoo = (AnnotationTypePattern) - atp.getLeft(); - assertEquals("@((Foo || Goo)) !@Boo", ap.toString()); - } - - // public void testParseAndOrPattern() { - // PatternParser p = new PatternParser("@Foo && @Boo || @Goo"); - // AnnotationTypePattern andOr = p.parseAnnotationTypePattern(); - // assertTrue("Should be or pattern",andOr instanceof - // OrAnnotationTypePattern); - // } - // - public void testParseBadPattern() { - PatternParser p = new PatternParser("@@Foo"); - try { - p.maybeParseAnnotationPattern(); - fail("ParserException expected"); - } catch (ParserException pEx) { - assertEquals("name pattern", pEx.getMessage()); - } - } - - public void testParseBadPattern2() { - PatternParser p = new PatternParser("Foo"); - AnnotationTypePattern bad = p.maybeParseAnnotationPattern(); - assertEquals("ANY", AnnotationTypePattern.ANY, bad); - } - - public void testParseNameOrVarAnnotationPattern() { - PatternParser p = new PatternParser("Foo"); - AnnotationTypePattern foo = p.parseAnnotationNameOrVarTypePattern(); - assertTrue("ExactAnnotationTypePattern expected", foo != null); - assertEquals("Foo", UnresolvedType.forName("Foo"), ((ExactAnnotationTypePattern) foo).annotationType); - } - - public void testParseNameOrVarAnnotationPatternWithNot() { - PatternParser p = new PatternParser("!@Foo"); - try { - // AnnotationTypePattern bad = - p.parseAnnotationNameOrVarTypePattern(); - fail("ParserException expected"); - } catch (ParserException pEx) { - assertEquals("identifier", pEx.getMessage()); - } - } - - public void testParseNameOrVarAnnotationPatternWithOr() { - PatternParser p = new PatternParser("Foo || Boo"); - AnnotationTypePattern foo = p.parseAnnotationNameOrVarTypePattern(); - // rest of pattern not consumed... - assertTrue("ExactAnnotationTypePattern", foo instanceof ExactAnnotationTypePattern); - assertEquals("Foo", UnresolvedType.forName("Foo"), ((ExactAnnotationTypePattern) foo).annotationType); - } - - public void testParseNameOrVarAnnotationWithBinding() { - PatternParser p = new PatternParser("foo"); - AnnotationTypePattern foo = p.parseAnnotationNameOrVarTypePattern(); - assertTrue("ExactAnnotationTypePattern", foo instanceof ExactAnnotationTypePattern); - assertEquals("@foo", ((ExactAnnotationTypePattern) foo).toString()); - } - - public void testParseNameOrVarAnnotationPatternWithAnd() { - PatternParser p = new PatternParser("Foo Boo"); - AnnotationTypePattern foo = p.parseAnnotationNameOrVarTypePattern(); - // rest of pattern not consumed... - assertEquals("@Foo", foo.toString()); - } - - public void testMaybeParseAnnotationPattern() { - PatternParser p = new PatternParser("@Foo"); - AnnotationTypePattern a = p.maybeParseAnnotationPattern(); - assertNotNull("Should find annotation pattern", a); - p = new PatternParser("Foo && Boo"); - a = p.maybeParseAnnotationPattern(); - assertEquals("Should be ANY pattern for a non-match", AnnotationTypePattern.ANY, a); - } - - public void testParseTypePatternsWithAnnotations() { - PatternParser p = new PatternParser("@Foo *"); - TypePattern t = p.parseTypePattern(); - assertTrue("AnyWithAnnotationTypePattern", t instanceof AnyWithAnnotationTypePattern); - AnnotationTypePattern atp = t.annotationPattern; - assertEquals("@(Foo)", atp.toString()); - assertEquals("(@(Foo) *)", t.toString()); - } - - public void testParseTypePatternsWithAnnotationsComplex() { - PatternParser p = new PatternParser("(@(Foo || Boo) (Foo || Boo))"); - TypePattern t = p.parseTypePattern(); - assertTrue("OrTypePattern", t instanceof OrTypePattern); - assertEquals("((@((Foo || Boo)) Foo) || (@((Foo || Boo)) Boo))", t.toString()); - } - - public void testNotSyntax() { - PatternParser p = new PatternParser("!@Foo (Foo || Boo))"); - TypePattern t = p.parseTypePattern(); - assertTrue("OrTypePattern", t instanceof OrTypePattern); - assertEquals("((!@(Foo) Foo) || (!@(Foo) Boo))", t.toString()); - } - - public void testParseMethodOrConstructorSigNoAP() { - PatternParser p = new PatternParser("* *.*(..)"); - SignaturePattern s = p.parseMethodOrConstructorSignaturePattern(); - assertEquals("Any annotation", AnnotationTypePattern.ANY, s.getAnnotationPattern()); - assertEquals("Any return", "*", s.getReturnType().toString()); - assertEquals("Any dec type", "*", s.getDeclaringType().toString()); - assertEquals("Any name", "*", s.getName().toString()); - assertEquals("* *.*(..)", s.toString()); - } - - public void testParseMethodOrConstructorSigSimpleAP() { - PatternParser p = new PatternParser("@Foo * *.*(..)"); - SignaturePattern s = p.parseMethodOrConstructorSignaturePattern(); - assertEquals("@(Foo) annotation", "@(Foo)", s.getAnnotationPattern().toString()); - assertEquals("Any return", "*", s.getReturnType().toString()); - assertEquals("Any dec type", "*", s.getDeclaringType().toString()); - assertEquals("Any name", "*", s.getName().toString()); - assertEquals("@(Foo) * *.*(..)", s.toString()); - } - - public void testParseMethodOrConstructorSigComplexAP() { - PatternParser p = new PatternParser("!@(Foo || Goo) * *.*(..)"); - SignaturePattern s = p.parseMethodOrConstructorSignaturePattern(); - assertEquals("complex annotation", "!@((Foo || Goo))", s.getAnnotationPattern().toString()); - assertEquals("Any return", "*", s.getReturnType().toString()); - assertEquals("Any dec type", "*", s.getDeclaringType().toString()); - assertEquals("Any name", "*", s.getName().toString()); - assertEquals("!@((Foo || Goo)) * *.*(..)", s.toString()); - } - - public void testParseMethodFieldSigNoAP() { - PatternParser p = new PatternParser("* *.*"); - SignaturePattern s = p.parseFieldSignaturePattern(); - assertEquals("Any annotation", AnnotationTypePattern.ANY, s.getAnnotationPattern()); - assertEquals("Any field type", "*", s.getReturnType().toString()); - assertEquals("Any dec type", "*", s.getDeclaringType().toString()); - assertEquals("Any name", "*", s.getName().toString()); - assertEquals("* *.*", s.toString()); - } - - public void testParseFieldSigSimpleAP() { - PatternParser p = new PatternParser("@Foo * *.*"); - SignaturePattern s = p.parseFieldSignaturePattern(); - assertEquals("@Foo annotation", "@(Foo)", s.getAnnotationPattern().toString()); - assertEquals("Any field type", "*", s.getReturnType().toString()); - assertEquals("Any dec type", "*", s.getDeclaringType().toString()); - assertEquals("Any name", "*", s.getName().toString()); - assertEquals("@(Foo) * *.*", s.toString()); - } - - public void testParseFieldSigComplexAP() { - PatternParser p = new PatternParser("!@(Foo || Goo) * *.*"); - SignaturePattern s = p.parseFieldSignaturePattern(); - assertEquals("complex annotation", "!@((Foo || Goo))", s.getAnnotationPattern().toString()); - assertEquals("Any field type", "*", s.getReturnType().toString()); - assertEquals("Any dec type", "*", s.getDeclaringType().toString()); - assertEquals("Any name", "*", s.getName().toString()); - assertEquals("!@((Foo || Goo)) * *.*", s.toString()); - } - - public void testExactAnnotationPatternMatching() { - if (LangUtil.is15VMOrGreater()) { - PatternParser p = new PatternParser("@Foo"); - AnnotationTypePattern ap = p.maybeParseAnnotationPattern(); - ap = ap.resolveBindings(makeSimpleScope(), new Bindings(3), true); - AnnotatedElementImpl ae = new AnnotatedElementImpl(new String[] { "Foo" }); - assertTrue("matches element with Foo", ap.matches(ae).alwaysTrue()); - AnnotatedElementImpl ae2 = new AnnotatedElementImpl(new String[] { "Boo" }); - assertTrue("does not match element with Boo", ap.matches(ae2).alwaysFalse()); - } - } - - public void testBindingAnnotationPatternMatching() { - if (LangUtil.is15VMOrGreater()) { - PatternParser p = new PatternParser("foo"); - AnnotationTypePattern ap = p.parseAnnotationNameOrVarTypePattern(); - try { - ap = ap.resolveBindings(makeSimpleScope(), new Bindings(3), true); - } catch (AbortException abEx) { - assertEquals("Binding not supported in @pcds (1.5.0 M1 limitation): null", abEx.getMessage()); - } - // uncomment these next lines once binding is supported - // AnnotatedElementImpl ae = new AnnotatedElementImpl(new - // String[]{"Foo"}); - // assertTrue("matches element with Foo",ap.matches(ae).alwaysTrue()) - // ; - // AnnotatedElementImpl ae2 = new AnnotatedElementImpl(new - // String[]{"Boo"}); - // assertTrue("does not match element with Boo",ap.matches(ae2). - // alwaysFalse()); - } - } - - public void testAndAnnotationPatternMatching() { - if (LangUtil.is15VMOrGreater()) { - PatternParser p = new PatternParser("@Foo @Boo"); - AnnotationTypePattern ap = p.maybeParseAnnotationPattern(); - ap = ap.resolveBindings(makeSimpleScope(), new Bindings(3), true); - AnnotatedElementImpl ae = new AnnotatedElementImpl(new String[] { "Foo", "Boo" }); - assertTrue("matches foo and boo", ap.matches(ae).alwaysTrue()); - ae = new AnnotatedElementImpl(new String[] { "Foo" }); - assertTrue("does not match foo", ap.matches(ae).alwaysFalse()); - ae = new AnnotatedElementImpl(new String[] { "Boo" }); - assertTrue("does not match boo", ap.matches(ae).alwaysFalse()); - ae = new AnnotatedElementImpl(new String[] { "Goo" }); - assertTrue("does not match goo", ap.matches(ae).alwaysFalse()); - } - } - - // - // public void testOrAnnotationPatternMatching() { - // PatternParser p = new PatternParser("@Foo || @Boo"); - // AnnotationTypePattern ap = p.parseAnnotationTypePattern(); - // ap = ap.resolveBindings(makeSimpleScope(),new Bindings(3),true); - // AnnotatedElementImpl ae = new AnnotatedElementImpl(new String[] - // {"Foo","Boo"}); - // assertTrue("matches foo and boo",ap.matches(ae).alwaysTrue()); - // ae = new AnnotatedElementImpl(new String[] {"Foo"}); - // assertTrue("matches foo",ap.matches(ae).alwaysTrue()); - // ae = new AnnotatedElementImpl(new String[] {"Boo"}); - // assertTrue("matches boo",ap.matches(ae).alwaysTrue()); - // ae = new AnnotatedElementImpl(new String[] {"Goo"}); - // assertTrue("does not match goo",ap.matches(ae).alwaysFalse()); - // } - // - public void testNotAnnotationPatternMatching() { - if (LangUtil.is15VMOrGreater()) { - PatternParser p = new PatternParser("!@Foo"); - AnnotationTypePattern ap = p.maybeParseAnnotationPattern(); - ap = ap.resolveBindings(makeSimpleScope(), new Bindings(3), true); - AnnotatedElementImpl ae = new AnnotatedElementImpl(new String[] { "Foo", "Boo" }); - assertTrue("does not match foo and boo", ap.matches(ae).alwaysFalse()); - ae = new AnnotatedElementImpl(new String[] { "Boo" }); - assertTrue("matches boo", ap.matches(ae).alwaysTrue()); - } - } - - public void testAnyAnnotationPatternMatching() { - AnnotatedElementImpl ae = new AnnotatedElementImpl(new String[] { "Foo", "Boo" }); - assertTrue("always matches", AnnotationTypePattern.ANY.matches(ae).alwaysTrue()); - ae = new AnnotatedElementImpl(new String[] {}); - assertTrue("always matches", AnnotationTypePattern.ANY.matches(ae).alwaysTrue()); - } - - public TestScope makeSimpleScope() { - BcelWorld bWorld = new BcelWorld(BcweaverTests.TESTDATA_PATH + "/testcode.jar"); // testcode contains Foo/Boo/Goo/etc - bWorld.setBehaveInJava5Way(true); - return new TestScope(new String[] { "int", "java.lang.String", "Foo", "Boo", "Goo" }, new String[] { "a", "b", "foo", - "boo", "goo" }, bWorld); - } - - // put test cases for AnnotationPatternList matching in separate test - // class... - - static class AnnotatedElementImpl implements AnnotatedElement { - - private String[] annotationTypes; - - public AnnotatedElementImpl(String[] annotationTypes) { - this.annotationTypes = annotationTypes; - } - - public boolean hasAnnotation(UnresolvedType ofType) { - for (int i = 0; i < annotationTypes.length; i++) { - if (annotationTypes[i].equals(ofType.getName())) { - return true; - } - } - return false; - } - - /* - * (non-Javadoc) - * - * @see org.aspectj.weaver.AnnotatedElement#getAnnotationTypes() - */ - public ResolvedType[] getAnnotationTypes() { - // TODO Auto-generated method stub - return null; - } - - public AnnotationAJ getAnnotationOfType(UnresolvedType ofType) { - // TODO Auto-generated method stub - return null; - } - - } -} diff --git a/weaver/testsrc/org/aspectj/weaver/patterns/ConcretizationTestCase.java b/weaver/testsrc/org/aspectj/weaver/patterns/ConcretizationTestCase.java deleted file mode 100644 index 8c3fd0d9c..000000000 --- a/weaver/testsrc/org/aspectj/weaver/patterns/ConcretizationTestCase.java +++ /dev/null @@ -1,116 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - -package org.aspectj.weaver.patterns; - -import org.aspectj.weaver.UnresolvedType; -import org.aspectj.weaver.bcel.WeaveTestCase; - -public class ConcretizationTestCase extends WeaveTestCase { - { - regenerate = false; - } - - public ConcretizationTestCase(String name) { - super(name); - } - - public void testNothingForAntJUnit() { - } - - // String[] none = new String[0]; - - /* - * XXX temporarily skipping public void testCflowResidual() throws IOException { - * - * BcelAdvice a = (BcelAdvice) makeConcreteTestAdviceEntryPart(); - * - * TestShadow shadow = new TestShadow(Shadow.MethodCall, Member.methodFromString("int Aspect.i(int x)"), UnresolvedType.OBJECT, - * world); - * - * ExposedState state = new ExposedState(1); - * - * a.specializeOn(shadow); - * - * //System.err.println(shadow); //System.err.println(a); - * - * //System.err.println(a.exposedState); - * - * - * } - * - * - * - * public Advice makeConcreteTestAdviceEntryPart() throws IOException { // XXX copied from below, refactor later - * - * - * // returns the advice for the entry part of cflow(foo(a)) Pointcut in = createResolvedPointcut( - * "cflow(foo(a)) && (args(b) && !cflow(foo(int)))", new String[] { "b", "a" }, new String[] { "float", "int" }); - * - * ResolvedPointcutDefinition ref = new ResolvedPointcutDefinition( UnresolvedType.forName("Aspect"), 0, "foo", new - * UnresolvedType[] { UnresolvedType.INT }, createResolvedPointcut( "args(refA)", new String[] { "refA" }, new String[] { "int" - * })); BcelObjectType target = (BcelObjectType) world.resolve("Aspect"); - * - * // now munge this to get the pointcut in it - * - * target.addPointcutDefinition(ref); CrosscuttingMembers xcut = new CrosscuttingMembers(target); target.crosscuttingMembers = - * xcut; - * - * Advice adviceMember = new BcelAdvice( AdviceKind.Before, in, Member.method(UnresolvedType.forName("FOO"), 0, "garadf", - * "(FI)V"), 0, 0, 0, null, null); // The pointcut to concretize - * - * // this returns the actual advice, but we don't care about it now. in.concretize(target, 2, adviceMember); - * - * List c = (List)xcut.getCflowEntries(); //target.getExtraConcreteShadowMungers(); - * - * return (Advice) c.get(0); } - * - * public void XtestCflow() throws IOException { Pointcut in = - * createResolvedPointcut("cflow(foo(a)) && (args(b) && !cflow(foo(int)))", new String[] {"b", "a"}, new String[] {"float", - * "int"} ); - * - * ResolvedPointcutDefinition ref = new ResolvedPointcutDefinition(UnresolvedType.forName("Aspect"), 0, "foo", new - * UnresolvedType[] { UnresolvedType.INT }, createResolvedPointcut("args(refA)", new String[] {"refA"}, new String[] {"int"})); - * - * List expectedSlots = new ArrayList(); expectedSlots.add(new ConcreteCflowPointcut.Slot(1, UnresolvedType.INT, 0)); - * - * checkConcr(in, ref, expectedSlots); } - * - * public void checkConcr( Pointcut in, ResolvedPointcutDefinition referredTo, List expectedSlots) throws IOException { - * - * BcelObjectType target = (BcelObjectType)world.resolve("Aspect"); - * - * // now munge this to get the pointcut in it - * - * target.addPointcutDefinition(referredTo); - * - * - * Advice adviceMember = new BcelAdvice(AdviceKind.Before, in, Member.method(UnresolvedType.forName("FOO"), 0, "garadf", - * "(FI)V"), 0, 0, 0, null, null); - * - * // The pointcut to concretize AndPointcut ap = (AndPointcut)in.concretize(target, 2, adviceMember); - * - * - * ConcreteCflowPointcut conc = (ConcreteCflowPointcut)ap.left; - * - * List slots = conc.slots; TestUtil.assertSetEquals(expectedSlots, slots); - * - * } - */ - - public Pointcut createResolvedPointcut(String pointcutSource, String[] formalNames, String[] formalTypes) { - final Pointcut sp = Pointcut.fromString(pointcutSource); - final Pointcut rp = sp.resolve(new SimpleScope(world, SimpleScope.makeFormalBindings(UnresolvedType.forNames(formalTypes), - formalNames))); - return rp; - } -} diff --git a/weaver/testsrc/org/aspectj/weaver/patterns/WildTypePatternResolutionTestCase.java b/weaver/testsrc/org/aspectj/weaver/patterns/WildTypePatternResolutionTestCase.java deleted file mode 100644 index e4039d063..000000000 --- a/weaver/testsrc/org/aspectj/weaver/patterns/WildTypePatternResolutionTestCase.java +++ /dev/null @@ -1,423 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2005 Contributors - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Adrian Colyer initial implementation - * ******************************************************************/ -package org.aspectj.weaver.patterns; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import junit.framework.TestCase; - -import org.aspectj.weaver.BoundedReferenceType; -import org.aspectj.weaver.CompressingDataOutputStream; -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.TypeFactory; -import org.aspectj.weaver.UnresolvedType; -import org.aspectj.weaver.VersionedDataInputStream; -import org.aspectj.weaver.World; -import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; -import org.aspectj.weaver.bcel.BcelWorld; - -// TODO write test cases for instanceof matching - -public class WildTypePatternResolutionTestCase extends TestCase { - - private World world; - private Bindings bindings; - private SimpleScope scope; - private ResolvedType javaUtilList; - private ResolvedType javaLangString; - private ResolvedType javaUtilListOfString; - private ResolvedType javaUtilListOfDouble; - private ResolvedType javaUtilListOfSomething; - - /** - * Foo where Foo exists and is generic Parser creates WildTypePattern namePatterns={Foo} resolveBindings resolves Foo to RT(Foo - * - raw) return ExactTypePattern(LFoo;) - */ - public void testSimpleFoo() { - TypePattern rtp = resolveWildTypePattern("List", false); - - assertTrue("resolves to exact type", rtp instanceof ExactTypePattern); - UnresolvedType exactType = rtp.getExactType(); - assertTrue(exactType.isRawType()); - assertEquals("Ljava/util/List;", exactType.getSignature()); - - ResolvedType rt = exactType.resolve(world); - assertEquals("Ljava/util/List;", rt.getSignature()); - assertTrue(rt.isRawType()); - - ExactTypePattern etp = (ExactTypePattern) writeAndRead(rtp); - exactType = etp.getExactType(); - - assertEquals("Ljava/util/List;", exactType.getSignature()); - - rt = exactType.resolve(world); - assertEquals("Ljava/util/List;", rt.getSignature()); - assertTrue(rt.isRawType()); - - assertTrue("matches List", etp.matches(javaUtilList, TypePattern.STATIC).alwaysTrue()); - assertTrue("matches generic List", etp.matches(javaUtilList.getGenericType(), TypePattern.STATIC).alwaysTrue()); - assertTrue("matches parameterized list", etp.matches(javaUtilListOfString, TypePattern.STATIC).alwaysTrue()); - assertTrue("does not match String", etp.matches(javaLangString, TypePattern.STATIC).alwaysFalse()); - } - - /** - * Foo where Foo exists and String meets the bounds Parser creates WildTypePattern namePatterns = {Foo}, - * typeParameters=WTP{String} resolveBindings resolves typeParameters to ExactTypePattern(String) resolves Foo to RT(Foo) - * returns ExactTypePattern(PFoo; - parameterized) - */ - public void testParameterized() { - TypePattern rtp = resolveWildTypePattern("List", false); - - assertTrue("resolves to exact type", rtp instanceof ExactTypePattern); - UnresolvedType exactType = rtp.getExactType(); - assertTrue(exactType.isParameterizedType()); - assertEquals("Pjava/util/List;", exactType.getSignature()); - - ResolvedType rt = exactType.resolve(world); - assertEquals("Pjava/util/List;", rt.getSignature()); - assertTrue(rt.isParameterizedType()); - - ExactTypePattern etp = (ExactTypePattern) writeAndRead(rtp); - exactType = etp.getExactType(); - - assertEquals("Pjava/util/List;", rt.getSignature()); - assertTrue(rt.isParameterizedType()); - - rt = exactType.resolve(world); - assertEquals("Pjava/util/List;", rt.getSignature()); - assertTrue(rt.isParameterizedType()); - - assertFalse("does not match List", etp.matches(javaUtilList, TypePattern.STATIC).alwaysTrue()); - assertFalse("does not match generic List", etp.matches(javaUtilList.getGenericType(), TypePattern.STATIC).alwaysTrue()); - assertTrue("matches parameterized list", etp.matches(javaUtilListOfString, TypePattern.STATIC).alwaysTrue()); - assertFalse("does not match parameterized list of double", etp.matches(javaUtilListOfDouble, TypePattern.STATIC) - .alwaysTrue()); - assertTrue("does not match String", etp.matches(javaLangString, TypePattern.STATIC).alwaysFalse()); - - } - - /** - * Foo where Foo exists and takes one bound Parser creates WildTypePattern namePatterns = {Foo}, typeParameters=WTP{Str*} - * resolveBindings resolves typeParameters to WTP{Str*} resolves Foo to RT(Foo) returns WildTypePattern(name = Foo, - * typeParameters = WTP{Str*} isGeneric=false) - */ - public void testParameterizedWildCard() { - TypePattern rtp = resolveWildTypePattern("List", false); - - assertTrue("resolves to WildTypePattern", rtp instanceof WildTypePattern); - assertTrue("one type parameter", rtp.typeParameters.size() == 1); - assertTrue("missing", ResolvedType.isMissing(rtp.getExactType())); - - WildTypePattern wtp = (WildTypePattern) writeAndRead(rtp); - assertTrue("one type parameter", wtp.typeParameters.size() == 1); - assertTrue("missing", ResolvedType.isMissing(wtp.getExactType())); - assertEquals("Str*", wtp.getTypeParameters().getTypePatterns()[0].toString()); - - assertFalse("does not match List", wtp.matches(javaUtilList, TypePattern.STATIC).alwaysTrue()); - assertFalse("does not match generic List", wtp.matches(javaUtilList.getGenericType(), TypePattern.STATIC).alwaysTrue()); - assertTrue("matches parameterized list", wtp.matches(javaUtilListOfString, TypePattern.STATIC).alwaysTrue()); - assertFalse("does not match parameterized list of double", wtp.matches(javaUtilListOfDouble, TypePattern.STATIC) - .alwaysTrue()); - assertTrue("does not match String", wtp.matches(javaLangString, TypePattern.STATIC).alwaysFalse()); - } - - /** - * Fo* Parser creates WildTypePattern namePatterns = {Fo*}, typeParameters=WTP{String} resolveBindings resolves - * typeParameters to ETP{String} returns WildTypePattern(name = Fo*, typeParameters = ETP{String} isGeneric=false) - */ - public void testWildcardParameterized() { - TypePattern rtp = resolveWildTypePattern("Li*", false); - - assertTrue("resolves to WildTypePattern", rtp instanceof WildTypePattern); - assertTrue("one type parameter", rtp.typeParameters.size() == 1); - assertEquals("Ljava/lang/String;", rtp.typeParameters.getTypePatterns()[0].getExactType().getSignature()); - - WildTypePattern wtp = (WildTypePattern) writeAndRead(rtp); - assertTrue("one type parameter", wtp.typeParameters.size() == 1); - assertEquals("Ljava/lang/String;", wtp.typeParameters.getTypePatterns()[0].getExactType().getSignature()); - - assertFalse("does not match List", wtp.matches(javaUtilList, TypePattern.STATIC).alwaysTrue()); - assertFalse("does not match generic List", wtp.matches(javaUtilList.getGenericType(), TypePattern.STATIC).alwaysTrue()); - assertTrue("matches parameterized list", wtp.matches(javaUtilListOfString, TypePattern.STATIC).alwaysTrue()); - assertFalse("does not match parameterized list of double", wtp.matches(javaUtilListOfDouble, TypePattern.STATIC) - .alwaysTrue()); - assertTrue("does not match String", wtp.matches(javaLangString, TypePattern.STATIC).alwaysFalse()); - } - - /** - * Foo - */ - public void testSomething() { - TypePattern rtp = resolveWildTypePattern("List", false); - - assertTrue("resolves to exact type", rtp instanceof ExactTypePattern); - UnresolvedType exactType = rtp.getExactType(); - assertTrue(exactType.isParameterizedType()); - assertEquals("Pjava/util/List<*>;", exactType.getSignature()); - - ExactTypePattern etp = (ExactTypePattern) writeAndRead(rtp); - exactType = etp.getExactType(); - assertTrue(exactType.isParameterizedType()); - assertEquals("Pjava/util/List<*>;", exactType.getSignature()); - - assertFalse("does not match List", etp.matches(javaUtilList, TypePattern.STATIC).alwaysTrue()); - assertFalse("does not match generic List", etp.matches(javaUtilList.getGenericType(), TypePattern.STATIC).alwaysTrue()); - assertFalse("does not match parameterized list", etp.matches(javaUtilListOfString, TypePattern.STATIC).alwaysTrue()); - assertFalse("does not match parameterized list of double", etp.matches(javaUtilListOfDouble, TypePattern.STATIC) - .alwaysTrue()); - assertTrue("does not match String", etp.matches(javaLangString, TypePattern.STATIC).alwaysFalse()); - - assertTrue("matches list of something", etp.matches(javaUtilListOfSomething, TypePattern.STATIC).alwaysTrue()); - } - - /** - * Foo - */ - public void testSomethingExtends() { - TypePattern rtp = resolveWildTypePattern("List", false); - - assertTrue("resolves to exact type", rtp instanceof ExactTypePattern); - UnresolvedType exactType = rtp.getExactType(); - assertTrue(exactType.isParameterizedType()); - assertEquals("Pjava/util/List<+Ljava/lang/Number;>;", exactType.getSignature()); - assertTrue("got a bounded reference type", exactType.getTypeParameters()[0] instanceof BoundedReferenceType); - - ExactTypePattern etp = (ExactTypePattern) writeAndRead(rtp); - exactType = etp.getExactType(); - exactType = exactType.resolve(world); - assertTrue(exactType.isParameterizedType()); - assertEquals("Pjava/util/List<+Ljava/lang/Number;>;", exactType.getSignature()); - assertTrue("got a bounded reference type", exactType.getTypeParameters()[0] instanceof BoundedReferenceType); - - assertFalse("does not match List", etp.matches(javaUtilList, TypePattern.STATIC).alwaysTrue()); - assertFalse("does not match generic List", etp.matches(javaUtilList.getGenericType(), TypePattern.STATIC).alwaysTrue()); - assertFalse("does not match parameterized list", etp.matches(javaUtilListOfString, TypePattern.STATIC).alwaysTrue()); - assertFalse("does not match parameterized list of double", etp.matches(javaUtilListOfDouble, TypePattern.STATIC) - .alwaysTrue()); - assertTrue("does not match String", etp.matches(javaLangString, TypePattern.STATIC).alwaysFalse()); - assertFalse("does not match list of something", etp.matches(javaUtilListOfSomething, TypePattern.STATIC).alwaysTrue()); - - ResolvedType listOfNumber = TypeFactory.createParameterizedType(javaUtilList, new UnresolvedType[] { UnresolvedType - .forName("java.lang.Number").resolve(world) }, world); - - ResolvedType listOfDouble = TypeFactory.createParameterizedType(javaUtilList, new UnresolvedType[] { UnresolvedType - .forName("java.lang.Double").resolve(world) }, world); - - assertFalse("does not match list of number", etp.matches(listOfNumber, TypePattern.STATIC).alwaysTrue()); - assertFalse("does not match list of double", etp.matches(listOfDouble, TypePattern.STATIC).alwaysTrue()); - - ResolvedType extendsNumber = TypeFactory.createTypeFromSignature("+Ljava/lang/Number;").resolve(world); - ResolvedType listOfExtendsNumber = TypeFactory.createParameterizedType(javaUtilList, - new UnresolvedType[] { extendsNumber }, world); - - assertTrue("matches list of ? extends number", etp.matches(listOfExtendsNumber, TypePattern.STATIC).alwaysTrue()); - - } - - /** - * Foo - */ - public void testSomethingExtendsPattern() { - TypePattern rtp = resolveWildTypePattern("List", false); - - assertTrue("resolves to wild type pattern", rtp instanceof WildTypePattern); - assertEquals("one type parameter", 1, rtp.getTypeParameters().size()); - TypePattern tp = rtp.getTypeParameters().getTypePatterns()[0]; - assertTrue("parameter is wild", tp instanceof WildTypePattern); - WildTypePattern tpwtp = (WildTypePattern) tp; - assertEquals("?", tpwtp.getNamePatterns()[0].maybeGetSimpleName()); - assertEquals("java.lang.Number+", tpwtp.upperBound.toString()); - - WildTypePattern wtp = (WildTypePattern) writeAndRead(rtp); - assertEquals("one type parameter", 1, wtp.getTypeParameters().size()); - tp = rtp.getTypeParameters().getTypePatterns()[0]; - assertTrue("parameter is wild", tp instanceof WildTypePattern); - tpwtp = (WildTypePattern) tp; - assertEquals("?", tpwtp.getNamePatterns()[0].maybeGetSimpleName()); - assertEquals("java.lang.Number+", tpwtp.upperBound.toString()); - - assertFalse("does not match List", wtp.matches(javaUtilList, TypePattern.STATIC).alwaysTrue()); - assertFalse("does not match generic List", wtp.matches(javaUtilList.getGenericType(), TypePattern.STATIC).alwaysTrue()); - assertFalse("does not match parameterized list", wtp.matches(javaUtilListOfString, TypePattern.STATIC).alwaysTrue()); - assertFalse("does not match parameterized list of double", wtp.matches(javaUtilListOfDouble, TypePattern.STATIC) - .alwaysTrue()); - assertTrue("does not match String", wtp.matches(javaLangString, TypePattern.STATIC).alwaysFalse()); - assertFalse("does not match list of something", wtp.matches(javaUtilListOfSomething, TypePattern.STATIC).alwaysTrue()); - - ResolvedType listOfNumber = TypeFactory.createParameterizedType(javaUtilList, new UnresolvedType[] { UnresolvedType - .forName("java.lang.Number").resolve(world) }, world); - - ResolvedType listOfDouble = TypeFactory.createParameterizedType(javaUtilList, new UnresolvedType[] { UnresolvedType - .forName("java.lang.Double").resolve(world) }, world); - - assertFalse("does not match list of number", wtp.matches(listOfNumber, TypePattern.STATIC).alwaysTrue()); - assertFalse("does not match list of double", wtp.matches(listOfDouble, TypePattern.STATIC).alwaysTrue()); - - ResolvedType extendsNumber = TypeFactory.createTypeFromSignature("+Ljava/lang/Number;").resolve(world); - ResolvedType listOfExtendsNumber = TypeFactory.createParameterizedType(javaUtilList, - new UnresolvedType[] { extendsNumber }, world); - - assertTrue("matches list of ? extends number", wtp.matches(listOfExtendsNumber, TypePattern.STATIC).alwaysTrue()); - - ResolvedType extendsDouble = TypeFactory.createTypeFromSignature("+Ljava/lang/Double;").resolve(world); - ResolvedType listOfExtendsDouble = TypeFactory.createParameterizedType(javaUtilList, - new UnresolvedType[] { extendsDouble }, world); - - assertTrue("matches list of ? extends double", wtp.matches(listOfExtendsDouble, TypePattern.STATIC).alwaysTrue()); - - } - - /** - * Foo - */ - public void testSomethingExtendsPatternv2() { - TypePattern rtp = resolveWildTypePattern("List", false); - - assertTrue("resolves to wild type pattern", rtp instanceof WildTypePattern); - assertEquals("one type parameter", 1, rtp.getTypeParameters().size()); - TypePattern tp = rtp.getTypeParameters().getTypePatterns()[0]; - assertTrue("parameter is wild", tp instanceof WildTypePattern); - WildTypePattern tpwtp = (WildTypePattern) tp; - assertEquals("?", tpwtp.getNamePatterns()[0].maybeGetSimpleName()); - assertEquals("Num*", tpwtp.upperBound.toString()); - - WildTypePattern wtp = (WildTypePattern) writeAndRead(rtp); - assertEquals("one type parameter", 1, wtp.getTypeParameters().size()); - tp = rtp.getTypeParameters().getTypePatterns()[0]; - assertTrue("parameter is wild", tp instanceof WildTypePattern); - tpwtp = (WildTypePattern) tp; - assertEquals("?", tpwtp.getNamePatterns()[0].maybeGetSimpleName()); - assertEquals("Num*", tpwtp.upperBound.toString()); - - assertFalse("does not match List", wtp.matches(javaUtilList, TypePattern.STATIC).alwaysTrue()); - assertFalse("does not match generic List", wtp.matches(javaUtilList.getGenericType(), TypePattern.STATIC).alwaysTrue()); - assertFalse("does not match parameterized list", wtp.matches(javaUtilListOfString, TypePattern.STATIC).alwaysTrue()); - assertFalse("does not match parameterized list of double", wtp.matches(javaUtilListOfDouble, TypePattern.STATIC) - .alwaysTrue()); - assertTrue("does not match String", wtp.matches(javaLangString, TypePattern.STATIC).alwaysFalse()); - assertFalse("does not match list of something", wtp.matches(javaUtilListOfSomething, TypePattern.STATIC).alwaysTrue()); - - ResolvedType listOfNumber = TypeFactory.createParameterizedType(javaUtilList, new UnresolvedType[] { UnresolvedType - .forName("java.lang.Number").resolve(world) }, world); - - ResolvedType listOfDouble = TypeFactory.createParameterizedType(javaUtilList, new UnresolvedType[] { UnresolvedType - .forName("java.lang.Double").resolve(world) }, world); - - boolean matchesListOfNumber = wtp.matches(listOfNumber, TypePattern.STATIC).alwaysTrue(); - assertFalse("does not match list of number", matchesListOfNumber); - assertFalse("does not match list of double", wtp.matches(listOfDouble, TypePattern.STATIC).alwaysTrue()); - - ResolvedType extendsNumber = TypeFactory.createTypeFromSignature("+Ljava/lang/Number;").resolve(world); - ResolvedType listOfExtendsNumber = TypeFactory.createParameterizedType(javaUtilList, - new UnresolvedType[] { extendsNumber }, world); - - boolean matchesListOfQmarkExtendsNumber = wtp.matches(listOfExtendsNumber, TypePattern.STATIC).alwaysTrue(); - assertTrue("failed to correctly match list of ? extends number", matchesListOfQmarkExtendsNumber); - - ResolvedType extendsDouble = TypeFactory.createTypeFromSignature("+Ljava/lang/Double;").resolve(world); - ResolvedType listOfExtendsDouble = TypeFactory.createParameterizedType(javaUtilList, - new UnresolvedType[] { extendsDouble }, world); - - assertFalse("does not match list of ? extends double", wtp.matches(listOfExtendsDouble, TypePattern.STATIC).alwaysTrue()); - } - - /** - * Foo - * - */ - public void testSomethingSuper() { - TypePattern rtp = resolveWildTypePattern("List", false); - - assertTrue("resolves to exact type", rtp instanceof ExactTypePattern); - UnresolvedType exactType = rtp.getExactType(); - assertTrue(exactType.isParameterizedType()); - assertEquals("Pjava/util/List<-Ljava/lang/Double;>;", exactType.getSignature()); - assertTrue("got a bounded reference type", exactType.getTypeParameters()[0] instanceof BoundedReferenceType); - - ExactTypePattern etp = (ExactTypePattern) writeAndRead(rtp); - exactType = etp.getExactType(); - exactType = exactType.resolve(world); - assertTrue(exactType.isParameterizedType()); - assertEquals("Pjava/util/List<-Ljava/lang/Double;>;", exactType.getSignature()); - assertTrue("got a bounded reference type", exactType.getTypeParameters()[0] instanceof BoundedReferenceType); - - assertFalse("does not match List", etp.matches(javaUtilList, TypePattern.STATIC).alwaysTrue()); - assertFalse("does not match generic List", etp.matches(javaUtilList.getGenericType(), TypePattern.STATIC).alwaysTrue()); - assertFalse("does not match parameterized list", etp.matches(javaUtilListOfString, TypePattern.STATIC).alwaysTrue()); - assertFalse("does not match parameterized list of double", etp.matches(javaUtilListOfDouble, TypePattern.STATIC) - .alwaysTrue()); - assertTrue("does not match String", etp.matches(javaLangString, TypePattern.STATIC).alwaysFalse()); - assertFalse("does not match list of something", etp.matches(javaUtilListOfSomething, TypePattern.STATIC).alwaysTrue()); - - ResolvedType listOfNumber = TypeFactory.createParameterizedType(javaUtilList, new UnresolvedType[] { UnresolvedType - .forName("java.lang.Number").resolve(world) }, world); - - ResolvedType listOfDouble = TypeFactory.createParameterizedType(javaUtilList, new UnresolvedType[] { UnresolvedType - .forName("java.lang.Double").resolve(world) }, world); - - assertFalse("does not match list of number", etp.matches(listOfNumber, TypePattern.STATIC).alwaysTrue()); - assertFalse("does not match list of double", etp.matches(listOfDouble, TypePattern.STATIC).alwaysTrue()); - - ResolvedType superDouble = TypeFactory.createTypeFromSignature("-Ljava/lang/Double;").resolve(world); - ResolvedType listOfSuperDouble = TypeFactory.createParameterizedType(javaUtilList, new UnresolvedType[] { superDouble }, - world); - - assertTrue("matches list of ? super double", etp.matches(listOfSuperDouble, TypePattern.STATIC).alwaysTrue()); - } - - private TypePattern resolveWildTypePattern(String source, boolean requireExact) { - WildTypePattern wtp = makeWildTypePattern(source); - return wtp.resolveBindings(scope, bindings, false, requireExact); - } - - private WildTypePattern makeWildTypePattern(String source) { - PatternParser parser = new PatternParser(source); - return (WildTypePattern) parser.parseTypePattern(); - } - - private TypePattern writeAndRead(TypePattern etp) { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ConstantPoolSimulator cps = new ConstantPoolSimulator(); - CompressingDataOutputStream dos = new CompressingDataOutputStream(baos, cps); - etp.write(dos); - dos.flush(); - dos.close(); - ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); - VersionedDataInputStream in = new VersionedDataInputStream(bais, cps); - in.setVersion(new WeaverVersionInfo()); - TypePattern ret = TypePattern.read(in, null); - return ret; - } catch (IOException ioEx) { - fail(ioEx + " thrown during serialization"); - } - return null; - } - - protected void setUp() throws Exception { - super.setUp(); - this.world = new BcelWorld(); - this.world.setBehaveInJava5Way(true); - this.bindings = new Bindings(0); - this.scope = new SimpleScope(world, new FormalBinding[] {}); - this.scope.setImportedPrefixes(new String[] { "java.io.", "java.util.", "java.lang." }); - this.javaLangString = UnresolvedType.forName("java.lang.String").resolve(world); - this.javaUtilList = UnresolvedType.forName("java.util.List").resolve(world); - this.javaUtilListOfString = TypeFactory.createParameterizedType(javaUtilList, new UnresolvedType[] { javaLangString }, - world); - this.javaUtilListOfDouble = TypeFactory.createParameterizedType(javaUtilList, new UnresolvedType[] { UnresolvedType - .forName("java.lang.Double").resolve(world) }, world); - this.javaUtilListOfSomething = TypeFactory.createParameterizedType(javaUtilList, - new UnresolvedType[] { UnresolvedType.SOMETHING.resolve(world) }, world); - } -} diff --git a/weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelAndOrNotTestCase.java b/weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelAndOrNotTestCase.java deleted file mode 100644 index d72960fea..000000000 --- a/weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelAndOrNotTestCase.java +++ /dev/null @@ -1,24 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2008 Contributors - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Andy Clement - * ******************************************************************/ -package org.aspectj.weaver.patterns.bcel; - -import org.aspectj.weaver.World; -import org.aspectj.weaver.bcel.BcelWorld; -import org.aspectj.weaver.patterns.AndOrNotTestCase; - -public class BcelAndOrNotTestCase extends AndOrNotTestCase { - - public World getWorld() { - return new BcelWorld(); - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelBindingTestCase.java b/weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelBindingTestCase.java deleted file mode 100644 index fac53dee1..000000000 --- a/weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelBindingTestCase.java +++ /dev/null @@ -1,24 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2008 Contributors - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Andy Clement - * ******************************************************************/ -package org.aspectj.weaver.patterns.bcel; - -import org.aspectj.weaver.World; -import org.aspectj.weaver.bcel.BcelWorld; -import org.aspectj.weaver.patterns.BindingTestCase; - -public class BcelBindingTestCase extends BindingTestCase { - - public World getWorld() { - return new BcelWorld(); - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelModifiersPatternTestCase.java b/weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelModifiersPatternTestCase.java deleted file mode 100644 index 06b89dc15..000000000 --- a/weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelModifiersPatternTestCase.java +++ /dev/null @@ -1,24 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2008 Contributors - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Andy Clement - * ******************************************************************/ -package org.aspectj.weaver.patterns.bcel; - -import org.aspectj.weaver.World; -import org.aspectj.weaver.bcel.BcelWorld; -import org.aspectj.weaver.patterns.ModifiersPatternTestCase; - -public class BcelModifiersPatternTestCase extends ModifiersPatternTestCase { - - public World getWorld() { - return new BcelWorld(); - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelParserTestCase.java b/weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelParserTestCase.java deleted file mode 100644 index 8d8f14d28..000000000 --- a/weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelParserTestCase.java +++ /dev/null @@ -1,25 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2008 Contributors - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Andy Clement - * ******************************************************************/ -package org.aspectj.weaver.patterns.bcel; - -import org.aspectj.weaver.BcweaverTests; -import org.aspectj.weaver.World; -import org.aspectj.weaver.bcel.BcelWorld; -import org.aspectj.weaver.patterns.ParserTestCase; - -public class BcelParserTestCase extends ParserTestCase { - - public World getWorld() { - return new BcelWorld(BcweaverTests.TESTDATA_PATH + "/testcode.jar"); - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelPatternsTests.java b/weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelPatternsTests.java deleted file mode 100644 index 877810a1a..000000000 --- a/weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelPatternsTests.java +++ /dev/null @@ -1,46 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * PARC initial implementation - * ******************************************************************/ - -package org.aspectj.weaver.patterns.bcel; - -import junit.framework.Test; -import junit.framework.TestCase; -import junit.framework.TestSuite; - -import org.aspectj.weaver.patterns.AnnotationPatternMatchingTestCase; -import org.aspectj.weaver.patterns.AnnotationPatternTestCase; - -public class BcelPatternsTests extends TestCase { - - public static Test suite() { - TestSuite suite = new TestSuite(BcelPatternsTests.class.getName()); - // $JUnit-BEGIN$ - suite.addTestSuite(BcelAndOrNotTestCase.class); - suite.addTestSuite(BcelBindingTestCase.class); - suite.addTestSuite(BcelWithinTestCase.class); - suite.addTestSuite(BcelModifiersPatternTestCase.class); - suite.addTestSuite(BcelTypePatternListTestCase.class); - suite.addTestSuite(BcelParserTestCase.class); - suite.addTestSuite(BcelSignaturePatternTestCase.class); - suite.addTestSuite(BcelTypePatternTestCase.class); - - suite.addTestSuite(AnnotationPatternTestCase.class); - suite.addTestSuite(AnnotationPatternMatchingTestCase.class); - // $JUnit-END$ - return suite; - } - - public BcelPatternsTests(String name) { - super(name); - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelSignaturePatternTestCase.java b/weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelSignaturePatternTestCase.java deleted file mode 100644 index e7f0f9890..000000000 --- a/weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelSignaturePatternTestCase.java +++ /dev/null @@ -1,24 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2008 Contributors - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Andy Clement - * ******************************************************************/ -package org.aspectj.weaver.patterns.bcel; - -import org.aspectj.weaver.World; -import org.aspectj.weaver.bcel.BcelWorld; -import org.aspectj.weaver.patterns.SignaturePatternTestCase; - -public class BcelSignaturePatternTestCase extends SignaturePatternTestCase { - - public World getWorld() { - return new BcelWorld(); - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelTypePatternListTestCase.java b/weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelTypePatternListTestCase.java deleted file mode 100644 index 935533d3c..000000000 --- a/weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelTypePatternListTestCase.java +++ /dev/null @@ -1,23 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2008 Contributors - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Andy Clement - * ******************************************************************/ -package org.aspectj.weaver.patterns.bcel; - -import org.aspectj.weaver.World; -import org.aspectj.weaver.bcel.BcelWorld; -import org.aspectj.weaver.patterns.TypePatternListTestCase; - -public class BcelTypePatternListTestCase extends TypePatternListTestCase { - - public World getWorld() { - return new BcelWorld(); - } -} diff --git a/weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelTypePatternTestCase.java b/weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelTypePatternTestCase.java deleted file mode 100644 index 47b2ce3af..000000000 --- a/weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelTypePatternTestCase.java +++ /dev/null @@ -1,24 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2008 Contributors - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Andy Clement - * ******************************************************************/ -package org.aspectj.weaver.patterns.bcel; - -import org.aspectj.weaver.World; -import org.aspectj.weaver.bcel.BcelWorld; -import org.aspectj.weaver.patterns.TypePatternTestCase; - -public class BcelTypePatternTestCase extends TypePatternTestCase { - - public World getWorld() { - return new BcelWorld(); - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelWithinTestCase.java b/weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelWithinTestCase.java deleted file mode 100644 index 8da1c172f..000000000 --- a/weaver/testsrc/org/aspectj/weaver/patterns/bcel/BcelWithinTestCase.java +++ /dev/null @@ -1,23 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2008 Contributors - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Andy Clement - * ******************************************************************/ -package org.aspectj.weaver.patterns.bcel; - -import org.aspectj.weaver.World; -import org.aspectj.weaver.bcel.BcelWorld; -import org.aspectj.weaver.patterns.WithinTestCase; - -public class BcelWithinTestCase extends WithinTestCase { - - public World getWorld() { - return new BcelWorld(); - } -} diff --git a/weaver/testsrc/org/aspectj/weaver/reflect/ReflectionBasedReferenceTypeDelegateTest.java b/weaver/testsrc/org/aspectj/weaver/reflect/ReflectionBasedReferenceTypeDelegateTest.java deleted file mode 100644 index 8047bbd4c..000000000 --- a/weaver/testsrc/org/aspectj/weaver/reflect/ReflectionBasedReferenceTypeDelegateTest.java +++ /dev/null @@ -1,316 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2005 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * Adrian Colyer Initial implementation - * ******************************************************************/ -package org.aspectj.weaver.reflect; - -import java.lang.reflect.Method; -import java.util.HashSet; -import java.util.Set; - -import junit.framework.TestCase; - -import org.aspectj.bridge.IMessageHandler; -import org.aspectj.weaver.ReferenceType; -import org.aspectj.weaver.ResolvedMember; -import org.aspectj.weaver.ResolvedType; -import org.aspectj.weaver.UnresolvedType; -import org.aspectj.weaver.bcel.BcelWorld; - -public class ReflectionBasedReferenceTypeDelegateTest extends TestCase { - - protected ReflectionWorld world; - private ResolvedType objectType; - private ResolvedType classType; - - public void testIsAspect() { - assertFalse(objectType.isAspect()); - } - - public void testIsAnnotationStyleAspect() { - assertFalse(objectType.isAnnotationStyleAspect()); - } - - public void testIsInterface() { - assertFalse(objectType.isInterface()); - assertTrue(world.resolve("java.io.Serializable").isInterface()); - } - - public void testIsEnum() { - assertFalse(objectType.isEnum()); - } - - public void testIsAnnotation() { - assertFalse(objectType.isAnnotation()); - } - - public void testIsAnnotationWithRuntimeRetention() { - assertFalse(objectType.isAnnotationWithRuntimeRetention()); - } - - public void testIsClass() { - assertTrue(objectType.isClass()); - assertFalse(world.resolve("java.io.Serializable").isClass()); - } - - public void testIsGeneric() { - assertFalse(objectType.isGenericType()); - } - - public void testIsExposedToWeaver() { - assertFalse(objectType.isExposedToWeaver()); - } - - public void testHasAnnotation() { - assertFalse(objectType.hasAnnotation(UnresolvedType.forName("Foo"))); - } - - public void testGetAnnotations() { - assertEquals("no entries", 0, objectType.getAnnotations().length); - } - - public void testGetAnnotationTypes() { - assertEquals("no entries", 0, objectType.getAnnotationTypes().length); - } - - public void testGetTypeVariables() { - assertEquals("no entries", 0, objectType.getTypeVariables().length); - } - - public void testGetPerClause() { - assertNull(objectType.getPerClause()); - } - - public void testGetModifiers() { - assertEquals(Object.class.getModifiers(), objectType.getModifiers()); - } - - public void testGetSuperclass() { - assertTrue("Superclass of object should be null, but it is: " + objectType.getSuperclass(), - objectType.getSuperclass() == null); - assertEquals(objectType, world.resolve("java.lang.Class").getSuperclass()); - ResolvedType d = world.resolve("reflect.tests.D"); - assertEquals(world.resolve("reflect.tests.C"), d.getSuperclass()); - } - - public void testArrayArgsSig() throws Exception { - Method invokeMethod = Method.class.getMethod("invoke", new Class[] { Object.class, Object[].class }); - ResolvedMember reflectionMethod = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMethod(invokeMethod, world); - String exp = "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"; - assertTrue("Expected: \n" + exp + "\n but got:\n" + reflectionMethod.getSignature(), reflectionMethod.getSignature() - .equals(exp)); - } - - protected int findMethod(String name, ResolvedMember[] methods) { - for (int i = 0; i < methods.length; i++) { - if (name.equals(methods[i].getName())) { - return i; - } - } - return -1; - } - - protected int findMethod(String name, int numArgs, ResolvedMember[] methods) { - for (int i = 0; i < methods.length; i++) { - if (name.equals(methods[i].getName()) && (methods[i].getParameterTypes().length == numArgs)) { - return i; - } - } - return -1; - } - - public void testGetDeclaredMethods() { - ResolvedMember[] methods = objectType.getDeclaredMethods(); - assertEquals(Object.class.getDeclaredMethods().length + Object.class.getDeclaredConstructors().length, methods.length); - - ResolvedType c = world.resolve("reflect.tests.C"); - methods = c.getDeclaredMethods(); - assertEquals(3, methods.length); - int idx = findMethod("foo", methods); - assertTrue(idx > -1); - - assertEquals(world.resolve("java.lang.String"), methods[idx].getReturnType()); - assertEquals(1, methods[idx].getParameterTypes().length); - assertEquals(objectType, methods[idx].getParameterTypes()[0]); - assertEquals(1, methods[idx].getExceptions().length); - assertEquals(world.resolve("java.lang.Exception"), methods[idx].getExceptions()[0]); - int baridx = findMethod("bar", methods); - int initidx = findMethod("", methods); - assertTrue(baridx > -1); - assertTrue(initidx > -1); - assertTrue(baridx != initidx && baridx != idx && idx <= 2 && initidx <= 2 && baridx <= 2); - - ResolvedType d = world.resolve("reflect.tests.D"); - methods = d.getDeclaredMethods(); - assertEquals(2, methods.length); - - classType = world.resolve("java.lang.Class"); - methods = classType.getDeclaredMethods(); - assertEquals(Class.class.getDeclaredMethods().length + Class.class.getDeclaredConstructors().length, methods.length); - } - - public void testGetDeclaredFields() { - ResolvedMember[] fields = objectType.getDeclaredFields(); - assertEquals(0, fields.length); - - ResolvedType c = world.resolve("reflect.tests.C"); - fields = c.getDeclaredFields(); - - assertEquals(2, fields.length); - assertEquals("f", fields[0].getName()); - assertEquals("s", fields[1].getName()); - assertEquals(UnresolvedType.INT, fields[0].getReturnType()); - assertEquals(world.resolve("java.lang.String"), fields[1].getReturnType()); - } - - public void testGetDeclaredInterfaces() { - ResolvedType[] interfaces = objectType.getDeclaredInterfaces(); - assertEquals(0, interfaces.length); - - ResolvedType d = world.resolve("reflect.tests.D"); - interfaces = d.getDeclaredInterfaces(); - assertEquals(1, interfaces.length); - assertEquals(world.resolve("java.io.Serializable"), interfaces[0]); - } - - public void testGetDeclaredPointcuts() { - ResolvedMember[] pointcuts = objectType.getDeclaredPointcuts(); - assertEquals(0, pointcuts.length); - } - - public void testSerializableSuperclass() { - ResolvedType serializableType = world.resolve("java.io.Serializable"); - ResolvedType superType = serializableType.getSuperclass(); - assertTrue("Superclass of serializable should be Object but was " + superType, superType.equals(UnresolvedType.OBJECT)); - - BcelWorld bcelworld = new BcelWorld(); - bcelworld.setBehaveInJava5Way(true); - ResolvedType bcelSupertype = bcelworld.resolve(UnresolvedType.SERIALIZABLE).getSuperclass(); - assertTrue("Should be null but is " + bcelSupertype, bcelSupertype.equals(UnresolvedType.OBJECT)); - } - - public void testSubinterfaceSuperclass() { - ResolvedType ifaceType = world.resolve("java.security.Key"); - ResolvedType superType = ifaceType.getSuperclass(); - assertTrue("Superclass should be Object but was " + superType, superType.equals(UnresolvedType.OBJECT)); - - BcelWorld bcelworld = new BcelWorld(); - bcelworld.setBehaveInJava5Way(true); - ResolvedType bcelSupertype = bcelworld.resolve("java.security.Key").getSuperclass(); - assertTrue("Should be null but is " + bcelSupertype, bcelSupertype.equals(UnresolvedType.OBJECT)); - } - - public void testVoidSuperclass() { - ResolvedType voidType = world.resolve(Void.TYPE); - ResolvedType superType = voidType.getSuperclass(); - assertNull(superType); - - BcelWorld bcelworld = new BcelWorld(); - bcelworld.setBehaveInJava5Way(true); - ResolvedType bcelSupertype = bcelworld.resolve("void").getSuperclass(); - assertTrue("Should be null but is " + bcelSupertype, bcelSupertype == null); - } - - public void testIntSuperclass() { - ResolvedType voidType = world.resolve(Integer.TYPE); - ResolvedType superType = voidType.getSuperclass(); - assertNull(superType); - - BcelWorld bcelworld = new BcelWorld(); - bcelworld.setBehaveInJava5Way(true); - ResolvedType bcelSupertype = bcelworld.resolve("int").getSuperclass(); - assertTrue("Should be null but is " + bcelSupertype, bcelSupertype == null); - } - - public void testGenericInterfaceSuperclass_BcelWorldResolution() { - BcelWorld bcelworld = new BcelWorld(); - bcelworld.setBehaveInJava5Way(true); - - UnresolvedType javaUtilMap = UnresolvedType.forName("java.util.Map"); - - ReferenceType rawType = (ReferenceType) bcelworld.resolve(javaUtilMap); - assertTrue("Should be the raw type ?!? " + rawType.getTypekind(), rawType.isRawType()); - - ReferenceType genericType = (ReferenceType) rawType.getGenericType(); - assertTrue("Should be the generic type ?!? " + genericType.getTypekind(), genericType.isGenericType()); - - ResolvedType rt = rawType.getSuperclass(); - assertTrue("Superclass for Map raw type should be Object but was " + rt, rt.equals(UnresolvedType.OBJECT)); - - ResolvedType rt2 = genericType.getSuperclass(); - assertTrue("Superclass for Map generic type should be Object but was " + rt2, rt2.equals(UnresolvedType.OBJECT)); - } - - // FIXME asc maybe. The reflection list of methods returned doesn't include (the static initializer) ... is that really - // a problem. - public void testCompareSubclassDelegates() { - - boolean barfIfClinitMissing = false; - world.setBehaveInJava5Way(true); - - BcelWorld bcelWorld = new BcelWorld(getClass().getClassLoader(), IMessageHandler.THROW, null); - bcelWorld.setBehaveInJava5Way(true); - UnresolvedType javaUtilHashMap = UnresolvedType.forName("java.util.HashMap"); - ReferenceType rawType = (ReferenceType) bcelWorld.resolve(javaUtilHashMap); - - ReferenceType rawReflectType = (ReferenceType) world.resolve(javaUtilHashMap); - ResolvedMember[] rms1 = rawType.getDelegate().getDeclaredMethods(); - ResolvedMember[] rms2 = rawReflectType.getDelegate().getDeclaredMethods(); - StringBuffer errors = new StringBuffer(); - Set one = new HashSet(); - for (int i = 0; i < rms1.length; i++) { - one.add(rms1[i].toString()); - } - Set two = new HashSet(); - for (int i = 0; i < rms2.length; i++) { - two.add(rms2[i].toString()); - } - for (int i = 0; i < rms2.length; i++) { - if (!one.contains(rms2[i].toString())) { - errors.append("Couldn't find " + rms2[i].toString() + " in the bcel set\n"); - } - } - for (int i = 0; i < rms1.length; i++) { - if (!two.contains(rms1[i].toString())) { - if (!barfIfClinitMissing && rms1[i].getName().equals("")) - continue; - errors.append("Couldn't find " + rms1[i].toString() + " in the reflection set\n"); - } - } - assertTrue("Errors:" + errors.toString(), errors.length() == 0); - - // the good old ibm vm seems to offer clinit through its reflection support (see pr145322) - if (rms1.length == rms2.length) - return; - if (barfIfClinitMissing) { - // the numbers must be exact - assertEquals(rms1.length, rms2.length); - } else { - // the numbers can be out by one in favour of bcel - if (rms1.length != (rms2.length + 1)) { - for (int i = 0; i < rms1.length; i++) { - System.err.println("bcel" + i + " is " + rms1[i]); - } - for (int i = 0; i < rms2.length; i++) { - System.err.println("refl" + i + " is " + rms2[i]); - } - } - assertTrue("Should be one extra (clinit) in BCEL case, but bcel=" + rms1.length + " reflect=" + rms2.length, - rms1.length == rms2.length + 1); - } - } - - // todo: array of int - - protected void setUp() throws Exception { - world = new ReflectionWorld(getClass().getClassLoader()); - objectType = world.resolve("java.lang.Object"); - } -} diff --git a/weaver/testsrc/org/aspectj/weaver/tools/PointcutDesignatorHandlerTests.java b/weaver/testsrc/org/aspectj/weaver/tools/PointcutDesignatorHandlerTests.java deleted file mode 100644 index 5e7d40d0b..000000000 --- a/weaver/testsrc/org/aspectj/weaver/tools/PointcutDesignatorHandlerTests.java +++ /dev/null @@ -1,251 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2005 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * Adrian Colyer Initial implementation - * ******************************************************************/ -package org.aspectj.weaver.tools; - -import junit.framework.TestCase; - -import org.aspectj.util.LangUtil; - -/** - * @author Adrian Colyer - * - */ -public class PointcutDesignatorHandlerTests extends TestCase { - - boolean needToSkip = false; - - protected void setUp() throws Exception { - super.setUp(); - needToSkip = needToSkipPointcutParserTests(); - } - - /** this condition can occur on the build machine only, and is way too complex to fix right now... */ - private boolean needToSkipPointcutParserTests() { - if (!LangUtil.is15VMOrGreater()) return false; - try { - Class.forName("org.aspectj.weaver.reflect.Java15ReflectionBasedReferenceTypeDelegate",false,this.getClass().getClassLoader());//ReflectionBasedReferenceTypeDelegate.class.getClassLoader()); - } catch (ClassNotFoundException cnfEx) { - return true; - } - return false; - } - - public void testParseWithoutHandler() { - if (needToSkip) return; - try { - PointcutParser - .getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution() - .parsePointcutExpression("bean(service.*"); - fail("should not be able to parse bean(service.*)"); - } catch(IllegalArgumentException ex) { - assertTrue("contains bean",ex.getMessage().indexOf("bean") != -1); - } - } - - public void testParseWithHandler() { - if (needToSkip) return; - PointcutParser parser = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution(); - BeanDesignatorHandler beanHandler = new BeanDesignatorHandler(); - parser.registerPointcutDesignatorHandler(beanHandler); - parser.parsePointcutExpression("bean(service.*)"); - assertEquals("service.*",beanHandler.getExpressionLastAskedToParse()); - } - - - /* - * Bug 205907 - the registered pointcut designator does not also get registered with the - * InternalUseOnlyPointcutParser inside the Java15ReflectionBasedReferenceTypeDelegate code. First test checks - * parsing is OK - */ - public void testParsingBeanInReferencePointcut01() throws Exception { - if (needToSkip) return; - PointcutParser parser = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution(); - BeanDesignatorHandler beanHandler = new BeanDesignatorHandler(); - parser.registerPointcutDesignatorHandler(beanHandler); - // The pointcut in CounterAspect look as follows: - // - // @Pointcut("execution(* setAge(..)) && bean(testBean1)") - // public void testBean1SetAge() { } - - // This should be found and resolved -// PointcutExpression pc = - parser.parsePointcutExpression("CounterAspect.testBean1SetAge()"); - - } - - /* - * Bug 205907 - the registered pointcut designator does not also get registered with the - * InternalUseOnlyPointcutParser inside the Java15ReflectionBasedReferenceTypeDelegate code. This test checks the - * actual matching. - */ - public void testParsingBeanInReferencePointcut02() throws Exception { - if (needToSkip) return; - PointcutParser parser = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution(); - BeanDesignatorHandler beanHandler = new BeanDesignatorHandler(); - parser.registerPointcutDesignatorHandler(beanHandler); - // The pointcut in CounterAspect look as follows: - // - // @Pointcut("execution(* toString(..)) && bean(testBean1)") - // public void testBean1toString() { } - - // This should be found and resolved - PointcutExpression pc = parser.parsePointcutExpression("CounterAspect.testBean1toString()"); - - DefaultMatchingContext context = new DefaultMatchingContext(); - context.addContextBinding("beanName", "testBean1"); - pc.setMatchingContext(context); - ShadowMatch sm = pc.matchesMethodExecution(Object.class.getMethod("toString", new Class[0])); - assertTrue(sm.alwaysMatches()); - - sm = pc.matchesMethodExecution(Object.class.getMethod("hashCode", new Class[0])); - assertTrue(sm.neverMatches()); - - context = new DefaultMatchingContext(); - context.addContextBinding("beanName", "testBean2"); - pc.setMatchingContext(context); - sm = pc.matchesMethodExecution(Object.class.getMethod("toString", new Class[0])); - assertTrue(sm.neverMatches()); - } - - public void testParseWithHandlerAndMultipleSegments() { - if (needToSkip) return; - PointcutParser parser = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution(); - BeanDesignatorHandler beanHandler = new BeanDesignatorHandler(); - parser.registerPointcutDesignatorHandler(beanHandler); - parser.parsePointcutExpression("bean(org.xyz.someapp..*)"); - assertEquals("org.xyz.someapp..*",beanHandler.getExpressionLastAskedToParse()); - } - - public void testStaticMatch() throws Exception { - if (needToSkip) return; - PointcutParser parser = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution(); - BeanDesignatorHandler beanHandler = new BeanDesignatorHandler(); - parser.registerPointcutDesignatorHandler(beanHandler); - PointcutExpression pc = parser.parsePointcutExpression("bean(myBean)"); - DefaultMatchingContext context = new DefaultMatchingContext(); - context.addContextBinding("beanName","myBean"); - pc.setMatchingContext(context); - ShadowMatch sm = pc.matchesMethodExecution(Object.class.getMethod("toString",new Class[0])); - assertTrue(sm.alwaysMatches()); - context.addContextBinding("beanName", "notMyBean"); - sm = pc.matchesMethodExecution(Object.class.getMethod("toString",new Class[0])); - assertTrue(sm.neverMatches()); - } - - public void testDynamicMatch() throws Exception { - if (needToSkip) return; - PointcutParser parser = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution(); - BeanDesignatorHandler beanHandler = new BeanDesignatorHandler(); - beanHandler.simulateDynamicTest = true; - parser.registerPointcutDesignatorHandler(beanHandler); - PointcutExpression pc = parser.parsePointcutExpression("bean(myBean)"); - ShadowMatch sm = pc.matchesMethodExecution(Object.class.getMethod("toString",new Class[0])); - DefaultMatchingContext context = new DefaultMatchingContext(); - assertTrue(sm.maybeMatches()); - assertFalse(sm.alwaysMatches()); - assertFalse(sm.neverMatches()); - context.addContextBinding("beanName","myBean"); - sm.setMatchingContext(context); - assertTrue(sm.matchesJoinPoint(null, null, null).matches()); - context.addContextBinding("beanName", "notMyBean"); - assertFalse(sm.matchesJoinPoint(null, null, null).matches()); - } - - public void testFastMatch() { - if (needToSkip) return; - PointcutParser parser = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution(); - BeanDesignatorHandler beanHandler = new BeanDesignatorHandler(); - parser.registerPointcutDesignatorHandler(beanHandler); - PointcutExpression pc = parser.parsePointcutExpression("bean(myBean)"); - DefaultMatchingContext context = new DefaultMatchingContext(); - context.addContextBinding("beanName","myBean"); - pc.setMatchingContext(context); - assertTrue(pc.couldMatchJoinPointsInType(String.class)); - context.addContextBinding("beanName","yourBean"); - assertFalse(pc.couldMatchJoinPointsInType(String.class)); - } - - private class BeanDesignatorHandler implements PointcutDesignatorHandler { - - private String askedToParse; - public boolean simulateDynamicTest = false; - - public String getDesignatorName() { - return "bean"; - } - - /* (non-Javadoc) - * @see org.aspectj.weaver.tools.PointcutDesignatorHandler#parse(java.lang.String) - */ - public ContextBasedMatcher parse(String expression) { - this.askedToParse = expression; - return new BeanPointcutExpression(expression,this.simulateDynamicTest); - } - - public String getExpressionLastAskedToParse() { - return this.askedToParse; - } - } - - private class BeanPointcutExpression implements ContextBasedMatcher { - - private final String beanNamePattern; - private final boolean simulateDynamicTest; - - public BeanPointcutExpression(String beanNamePattern, boolean simulateDynamicTest) { - this.beanNamePattern = beanNamePattern; - this.simulateDynamicTest = simulateDynamicTest; - } - - - public boolean couldMatchJoinPointsInType(Class aClass) { - return true; - } - - /* (non-Javadoc) - * @see org.aspectj.weaver.tools.ContextBasedMatcher#couldMatchJoinPointsInType(java.lang.Class) - */ - public boolean couldMatchJoinPointsInType(Class aClass, MatchingContext context) { - if (this.beanNamePattern.equals(context.getBinding("beanName"))) { - return true; - } else { - return false; - } - } - - - /* (non-Javadoc) - * @see org.aspectj.weaver.tools.ContextBasedMatcher#mayNeedDynamicTest() - */ - public boolean mayNeedDynamicTest() { - return this.simulateDynamicTest; - } - - - public FuzzyBoolean matchesStatically(MatchingContext matchContext) { - if (this.simulateDynamicTest) return FuzzyBoolean.MAYBE; - if (this.beanNamePattern.equals(matchContext.getBinding("beanName"))) { - return FuzzyBoolean.YES; - } else { - return FuzzyBoolean.NO; - } - } - - - /* (non-Javadoc) - * @see org.aspectj.weaver.tools.ContextBasedMatcher#matchesDynamically(org.aspectj.weaver.tools.MatchingContext) - */ - public boolean matchesDynamically(MatchingContext matchContext) { - return this.beanNamePattern.equals(matchContext.getBinding("beanName")); - } - } -} diff --git a/weaver/testsrc/org/aspectj/weaver/tools/PointcutExpressionTest.java b/weaver/testsrc/org/aspectj/weaver/tools/PointcutExpressionTest.java deleted file mode 100644 index 46189fd32..000000000 --- a/weaver/testsrc/org/aspectj/weaver/tools/PointcutExpressionTest.java +++ /dev/null @@ -1,596 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * IBM Corporation - initial API and implementation - *******************************************************************************/ -package org.aspectj.weaver.tools; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Method; - -import junit.framework.TestCase; - -import org.aspectj.util.LangUtil; - -public class PointcutExpressionTest extends TestCase { - - PointcutParser p; - Constructor asCons; - Constructor bsCons; - Constructor bsStringCons; - Constructor clientCons; - Method a; - Method aa; - Method aaa; - Field x; - Field y; - Method b; - Method bsaa; - Field n; - Method foo; - Method bar; - - public void testMatchesMethodCall() { - PointcutExpression ex = p.parsePointcutExpression("call(* *..A.a*(..))"); - assertTrue("Should match call to A.a()", ex.matchesMethodCall(a, a).alwaysMatches()); - assertTrue("Should match call to A.aaa()", ex.matchesMethodCall(aaa, a).alwaysMatches()); - assertTrue("Should match call to B.aa()", ex.matchesMethodCall(bsaa, a).alwaysMatches()); - assertTrue("Should not match call to B.b()", ex.matchesMethodCall(b, a).neverMatches()); - ex = p.parsePointcutExpression("call(* *..A.a*(int))"); - assertTrue("Should match call to A.aa()", ex.matchesMethodCall(aa, a).alwaysMatches()); - assertTrue("Should not match call to A.a()", ex.matchesMethodCall(a, a).neverMatches()); - ex = p.parsePointcutExpression("call(void aaa(..)) && this(org.aspectj.weaver.tools.PointcutExpressionTest.Client)"); - assertTrue("Should match call to A.aaa() from Client", ex.matchesMethodCall(aaa, foo).alwaysMatches()); - ex = p.parsePointcutExpression("call(void aaa(..)) && this(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); - assertTrue("Should match call to A.aaa() from B", ex.matchesMethodCall(aaa, b).alwaysMatches()); - assertTrue("May match call to A.aaa() from A", ex.matchesMethodCall(aaa, a).maybeMatches()); - assertFalse("May match call to A.aaa() from A", ex.matchesMethodCall(aaa, a).alwaysMatches()); - ex = p.parsePointcutExpression("execution(* *.*(..))"); - assertTrue("Should not match call to A.aa", ex.matchesMethodCall(aa, a).neverMatches()); - // this - ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.Client)"); - assertTrue("Should match Client", ex.matchesMethodCall(a, foo).alwaysMatches()); - assertTrue("Should not match A", ex.matchesMethodCall(a, a).neverMatches()); - ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); - assertTrue("Should maybe match B", ex.matchesMethodCall(bsaa, a).maybeMatches()); - assertFalse("Should maybe match B", ex.matchesMethodCall(bsaa, a).alwaysMatches()); - // target - ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.Client)"); - assertTrue("Should not match Client", ex.matchesMethodCall(a, a).neverMatches()); - ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.A)"); - assertTrue("Should match A", ex.matchesMethodCall(a, a).alwaysMatches()); - ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); - assertTrue("Should maybe match A", ex.matchesMethodCall(aa, a).maybeMatches()); - assertFalse("Should maybe match A", ex.matchesMethodCall(aa, a).alwaysMatches()); - // test args - ex = p.parsePointcutExpression("args(..,int)"); - assertTrue("Should match A.aa", ex.matchesMethodCall(aa, a).alwaysMatches()); - assertTrue("Should match A.aaa", ex.matchesMethodCall(aaa, a).alwaysMatches()); - assertTrue("Should not match A.a", ex.matchesMethodCall(a, a).neverMatches()); - // within - ex = p.parsePointcutExpression("within(*..A)"); - assertTrue("Matches in class A", ex.matchesMethodCall(a, a).alwaysMatches()); - assertTrue("Does not match in class B", ex.matchesMethodCall(a, b).neverMatches()); - assertTrue("Matches in class A", ex.matchesMethodCall(a, A.class).alwaysMatches()); - assertTrue("Does not match in class B", ex.matchesMethodCall(a, B.class).neverMatches()); - // withincode - ex = p.parsePointcutExpression("withincode(* a*(..))"); - assertTrue("Should match", ex.matchesMethodCall(b, bsaa).alwaysMatches()); - assertTrue("Should not match", ex.matchesMethodCall(b, b).neverMatches()); - } - - public void testMatchesMethodExecution() { - PointcutExpression ex = p.parsePointcutExpression("execution(* *..A.aa(..))"); - assertTrue("Should match execution of A.aa", ex.matchesMethodExecution(aa).alwaysMatches()); - assertTrue("Should match execution of B.aa", ex.matchesMethodExecution(bsaa).alwaysMatches()); - assertTrue("Should not match execution of A.a", ex.matchesMethodExecution(a).neverMatches()); - ex = p.parsePointcutExpression("call(* *..A.a*(int))"); - assertTrue("Should not match execution of A.a", ex.matchesMethodExecution(a).neverMatches()); - - // test this - ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.A)"); - assertTrue("Should match A", ex.matchesMethodExecution(a).alwaysMatches()); - ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); - assertTrue("Maybe matches B", ex.matchesMethodExecution(a).maybeMatches()); - assertFalse("Maybe matches B", ex.matchesMethodExecution(a).alwaysMatches()); - - // test target - ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.A)"); - assertTrue("Should match A", ex.matchesMethodExecution(a).alwaysMatches()); - ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); - assertTrue("Maybe matches B", ex.matchesMethodExecution(a).maybeMatches()); - assertFalse("Maybe matches B", ex.matchesMethodExecution(a).alwaysMatches()); - - // test args - ex = p.parsePointcutExpression("args(..,int)"); - assertTrue("Should match A.aa", ex.matchesMethodExecution(aa).alwaysMatches()); - assertTrue("Should match A.aaa", ex.matchesMethodExecution(aaa).alwaysMatches()); - assertTrue("Should not match A.a", ex.matchesMethodExecution(a).neverMatches()); - - // within - ex = p.parsePointcutExpression("within(*..A)"); - assertTrue("Matches in class A", ex.matchesMethodExecution(a).alwaysMatches()); - assertTrue("Does not match in class B", ex.matchesMethodExecution(bsaa).neverMatches()); - - // withincode - ex = p.parsePointcutExpression("withincode(* a*(..))"); - assertTrue("Should not match", ex.matchesMethodExecution(a).neverMatches()); - } - - public void testMatchesConstructorCall() { - PointcutExpression ex = p.parsePointcutExpression("call(new(String))"); - assertTrue("Should match A(String)", ex.matchesConstructorCall(asCons, b).alwaysMatches()); - assertTrue("Should match B(String)", ex.matchesConstructorCall(bsStringCons, b).alwaysMatches()); - assertTrue("Should not match B()", ex.matchesConstructorCall(bsCons, foo).neverMatches()); - ex = p.parsePointcutExpression("call(*..A.new(String))"); - assertTrue("Should match A(String)", ex.matchesConstructorCall(asCons, b).alwaysMatches()); - assertTrue("Should not match B(String)", ex.matchesConstructorCall(bsStringCons, foo).neverMatches()); - // this - ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.Client)"); - assertTrue("Should match Client", ex.matchesConstructorCall(asCons, foo).alwaysMatches()); - assertTrue("Should not match A", ex.matchesConstructorCall(asCons, a).neverMatches()); - ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); - assertTrue("Should maybe match B", ex.matchesConstructorCall(asCons, a).maybeMatches()); - assertFalse("Should maybe match B", ex.matchesConstructorCall(asCons, a).alwaysMatches()); - // target - ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.Client)"); - assertTrue("Should not match Client", ex.matchesConstructorCall(asCons, foo).neverMatches()); - ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.A)"); - assertTrue("Should not match A (no target)", ex.matchesConstructorCall(asCons, a).neverMatches()); - // args - ex = p.parsePointcutExpression("args(String)"); - assertTrue("Should match A(String)", ex.matchesConstructorCall(asCons, b).alwaysMatches()); - assertTrue("Should match B(String)", ex.matchesConstructorCall(bsStringCons, foo).alwaysMatches()); - assertTrue("Should not match B()", ex.matchesConstructorCall(bsCons, foo).neverMatches()); - // within - ex = p.parsePointcutExpression("within(*..A)"); - assertTrue("Matches in class A", ex.matchesConstructorCall(asCons, a).alwaysMatches()); - assertTrue("Does not match in class B", ex.matchesConstructorCall(asCons, b).neverMatches()); - // withincode - ex = p.parsePointcutExpression("withincode(* a*(..))"); - assertTrue("Should match", ex.matchesConstructorCall(bsCons, aa).alwaysMatches()); - assertTrue("Should not match", ex.matchesConstructorCall(bsCons, b).neverMatches()); - } - - public void testMatchesConstructorExecution() { - PointcutExpression ex = p.parsePointcutExpression("execution(new(String))"); - assertTrue("Should match A(String)", ex.matchesConstructorExecution(asCons).alwaysMatches()); - assertTrue("Should match B(String)", ex.matchesConstructorExecution(bsStringCons).alwaysMatches()); - assertTrue("Should not match B()", ex.matchesConstructorExecution(bsCons).neverMatches()); - ex = p.parsePointcutExpression("execution(*..A.new(String))"); - assertTrue("Should match A(String)", ex.matchesConstructorExecution(asCons).alwaysMatches()); - assertTrue("Should not match B(String)", ex.matchesConstructorExecution(bsStringCons).neverMatches()); - - // test this - ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.A)"); - assertTrue("Should match A", ex.matchesConstructorExecution(asCons).alwaysMatches()); - ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); - assertTrue("Maybe matches B", ex.matchesConstructorExecution(asCons).maybeMatches()); - assertFalse("Maybe matches B", ex.matchesConstructorExecution(asCons).alwaysMatches()); - assertTrue("Should match B", ex.matchesConstructorExecution(bsCons).alwaysMatches()); - assertTrue("Does not match client", ex.matchesConstructorExecution(clientCons).neverMatches()); - - // test target - ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.A)"); - assertTrue("Should match A", ex.matchesConstructorExecution(asCons).alwaysMatches()); - ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); - assertTrue("Maybe matches B", ex.matchesConstructorExecution(asCons).maybeMatches()); - assertFalse("Maybe matches B", ex.matchesConstructorExecution(asCons).alwaysMatches()); - assertTrue("Should match B", ex.matchesConstructorExecution(bsCons).alwaysMatches()); - assertTrue("Does not match client", ex.matchesConstructorExecution(clientCons).neverMatches()); - - // within - ex = p.parsePointcutExpression("within(*..A)"); - assertTrue("Matches in class A", ex.matchesConstructorExecution(asCons).alwaysMatches()); - assertTrue("Does not match in class B", ex.matchesConstructorExecution(bsCons).neverMatches()); - - // withincode - ex = p.parsePointcutExpression("withincode(* a*(..))"); - assertTrue("Does not match", ex.matchesConstructorExecution(bsCons).neverMatches()); - - // args - ex = p.parsePointcutExpression("args(String)"); - assertTrue("Should match A(String)", ex.matchesConstructorExecution(asCons).alwaysMatches()); - assertTrue("Should match B(String)", ex.matchesConstructorExecution(bsStringCons).alwaysMatches()); - assertTrue("Should not match B()", ex.matchesConstructorExecution(bsCons).neverMatches()); - } - - public void testMatchesAdviceExecution() { - PointcutExpression ex = p.parsePointcutExpression("adviceexecution()"); - assertTrue("Should match (advice) A.a", ex.matchesAdviceExecution(a).alwaysMatches()); - // test this - ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.Client)"); - assertTrue("Should match Client", ex.matchesAdviceExecution(foo).alwaysMatches()); - ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); - assertTrue("Maybe matches B", ex.matchesAdviceExecution(a).maybeMatches()); - assertFalse("Maybe matches B", ex.matchesAdviceExecution(a).alwaysMatches()); - assertTrue("Does not match client", ex.matchesAdviceExecution(foo).neverMatches()); - - // test target - ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.Client)"); - assertTrue("Should match Client", ex.matchesAdviceExecution(foo).alwaysMatches()); - ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); - assertTrue("Maybe matches B", ex.matchesAdviceExecution(a).maybeMatches()); - assertFalse("Maybe matches B", ex.matchesAdviceExecution(a).alwaysMatches()); - assertTrue("Does not match client", ex.matchesAdviceExecution(foo).neverMatches()); - - // test within - ex = p.parsePointcutExpression("within(*..A)"); - assertTrue("Matches in class A", ex.matchesAdviceExecution(a).alwaysMatches()); - assertTrue("Does not match in class B", ex.matchesAdviceExecution(b).neverMatches()); - - // withincode - ex = p.parsePointcutExpression("withincode(* a*(..))"); - assertTrue("Does not match", ex.matchesAdviceExecution(a).neverMatches()); - - // test args - ex = p.parsePointcutExpression("args(..,int)"); - assertTrue("Should match A.aa", ex.matchesAdviceExecution(aa).alwaysMatches()); - assertTrue("Should match A.aaa", ex.matchesAdviceExecution(aaa).alwaysMatches()); - assertTrue("Should not match A.a", ex.matchesAdviceExecution(a).neverMatches()); - } - - public void testMatchesHandler() { - PointcutExpression ex = p.parsePointcutExpression("handler(Exception)"); - assertTrue("Should match catch(Exception)", ex.matchesHandler(Exception.class, Client.class).alwaysMatches()); - assertTrue("Should not match catch(Throwable)", ex.matchesHandler(Throwable.class, Client.class).neverMatches()); - // test this - ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.Client)"); - assertTrue("Should match Client", ex.matchesHandler(Exception.class, foo).alwaysMatches()); - ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); - assertTrue("Maybe matches B", ex.matchesHandler(Exception.class, a).maybeMatches()); - assertFalse("Maybe matches B", ex.matchesHandler(Exception.class, a).alwaysMatches()); - assertTrue("Does not match client", ex.matchesHandler(Exception.class, foo).neverMatches()); - // target - no target for exception handlers - ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.Client)"); - assertTrue("Should match Client", ex.matchesHandler(Exception.class, foo).neverMatches()); - // args - ex = p.parsePointcutExpression("args(Exception)"); - assertTrue("Should match Exception", ex.matchesHandler(Exception.class, foo).alwaysMatches()); - assertTrue("Should match RuntimeException", ex.matchesHandler(RuntimeException.class, foo).alwaysMatches()); - assertTrue("Should not match String", ex.matchesHandler(String.class, foo).neverMatches()); - assertTrue("Maybe matches Throwable", ex.matchesHandler(Throwable.class, foo).maybeMatches()); - assertFalse("Maybe matches Throwable", ex.matchesHandler(Throwable.class, foo).alwaysMatches()); - // within - ex = p.parsePointcutExpression("within(*..Client)"); - assertTrue("Matches in class Client", ex.matchesHandler(Exception.class, foo).alwaysMatches()); - assertTrue("Does not match in class B", ex.matchesHandler(Exception.class, b).neverMatches()); - // withincode - ex = p.parsePointcutExpression("withincode(* a*(..))"); - assertTrue("Matches within aa", ex.matchesHandler(Exception.class, aa).alwaysMatches()); - assertTrue("Does not match within b", ex.matchesHandler(Exception.class, b).neverMatches()); - } - - public void testMatchesInitialization() { - PointcutExpression ex = p.parsePointcutExpression("initialization(new(String))"); - assertTrue("Should match A(String)", ex.matchesInitialization(asCons).alwaysMatches()); - assertTrue("Should match B(String)", ex.matchesInitialization(bsStringCons).alwaysMatches()); - assertTrue("Should not match B()", ex.matchesInitialization(bsCons).neverMatches()); - ex = p.parsePointcutExpression("initialization(*..A.new(String))"); - assertTrue("Should match A(String)", ex.matchesInitialization(asCons).alwaysMatches()); - assertTrue("Should not match B(String)", ex.matchesInitialization(bsStringCons).neverMatches()); - // test this - ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.A)"); - assertTrue("Should match A", ex.matchesInitialization(asCons).alwaysMatches()); - ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); - assertTrue("Maybe matches B", ex.matchesInitialization(asCons).maybeMatches()); - assertFalse("Maybe matches B", ex.matchesInitialization(asCons).alwaysMatches()); - - // test target - ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.A)"); - assertTrue("Should match A", ex.matchesInitialization(asCons).alwaysMatches()); - ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); - assertTrue("Maybe matches B", ex.matchesInitialization(asCons).maybeMatches()); - assertFalse("Maybe matches B", ex.matchesInitialization(asCons).alwaysMatches()); - // within - ex = p.parsePointcutExpression("within(*..A)"); - assertTrue("Matches in class A", ex.matchesInitialization(asCons).alwaysMatches()); - assertTrue("Does not match in class B", ex.matchesInitialization(bsCons).neverMatches()); - // withincode - ex = p.parsePointcutExpression("withincode(* a*(..))"); - assertTrue("Does not match", ex.matchesInitialization(bsCons).neverMatches()); - // args - ex = p.parsePointcutExpression("args(String)"); - assertTrue("Should match A(String)", ex.matchesInitialization(asCons).alwaysMatches()); - assertTrue("Should match B(String)", ex.matchesInitialization(bsStringCons).alwaysMatches()); - assertTrue("Should not match B()", ex.matchesInitialization(bsCons).neverMatches()); - } - - public void testMatchesPreInitialization() { - PointcutExpression ex = p.parsePointcutExpression("preinitialization(new(String))"); - assertTrue("Should match A(String)", ex.matchesPreInitialization(asCons).alwaysMatches()); - assertTrue("Should match B(String)", ex.matchesPreInitialization(bsStringCons).alwaysMatches()); - assertTrue("Should not match B()", ex.matchesPreInitialization(bsCons).neverMatches()); - ex = p.parsePointcutExpression("preinitialization(*..A.new(String))"); - assertTrue("Should match A(String)", ex.matchesPreInitialization(asCons).alwaysMatches()); - assertTrue("Should not match B(String)", ex.matchesPreInitialization(bsStringCons).neverMatches()); - // test this - ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.A)"); - assertTrue("No match, no this at preinit", ex.matchesPreInitialization(asCons).neverMatches()); - - // test target - ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.A)"); - assertTrue("No match, no target at preinit", ex.matchesPreInitialization(asCons).neverMatches()); - - // within - ex = p.parsePointcutExpression("within(*..A)"); - assertTrue("Matches in class A", ex.matchesPreInitialization(asCons).alwaysMatches()); - assertTrue("Does not match in class B", ex.matchesPreInitialization(bsCons).neverMatches()); - // withincode - ex = p.parsePointcutExpression("withincode(* a*(..))"); - assertTrue("Does not match", ex.matchesPreInitialization(bsCons).neverMatches()); - // args - ex = p.parsePointcutExpression("args(String)"); - assertTrue("Should match A(String)", ex.matchesPreInitialization(asCons).alwaysMatches()); - assertTrue("Should match B(String)", ex.matchesPreInitialization(bsStringCons).alwaysMatches()); - assertTrue("Should not match B()", ex.matchesPreInitialization(bsCons).neverMatches()); - } - - public void testMatchesStaticInitialization() { - // staticinit - PointcutExpression ex = p.parsePointcutExpression("staticinitialization(*..A+)"); - assertTrue("Matches A", ex.matchesStaticInitialization(A.class).alwaysMatches()); - assertTrue("Matches B", ex.matchesStaticInitialization(B.class).alwaysMatches()); - assertTrue("Doesn't match Client", ex.matchesStaticInitialization(Client.class).neverMatches()); - // this - ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.A)"); - assertTrue("No this", ex.matchesStaticInitialization(A.class).neverMatches()); - // target - ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.A)"); - assertTrue("No target", ex.matchesStaticInitialization(A.class).neverMatches()); - - // args - ex = p.parsePointcutExpression("args()"); - assertTrue("No args", ex.matchesStaticInitialization(A.class).alwaysMatches()); - ex = p.parsePointcutExpression("args(String)"); - assertTrue("No args", ex.matchesStaticInitialization(A.class).neverMatches()); - - // within - ex = p.parsePointcutExpression("within(*..A)"); - assertTrue("Matches in class A", ex.matchesStaticInitialization(A.class).alwaysMatches()); - assertTrue("Does not match in class B", ex.matchesStaticInitialization(B.class).neverMatches()); - - // withincode - ex = p.parsePointcutExpression("withincode(* a*(..))"); - assertTrue("Does not match", ex.matchesStaticInitialization(A.class).neverMatches()); - } - - public void testMatchesFieldSet() { - PointcutExpression ex = p.parsePointcutExpression("set(* *..A+.*)"); - assertTrue("matches x", ex.matchesFieldSet(x, a).alwaysMatches()); - assertTrue("matches y", ex.matchesFieldSet(y, foo).alwaysMatches()); - assertTrue("does not match n", ex.matchesFieldSet(n, foo).neverMatches()); - // this - ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.Client)"); - assertTrue("matches Client", ex.matchesFieldSet(x, foo).alwaysMatches()); - assertTrue("does not match A", ex.matchesFieldSet(n, a).neverMatches()); - ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); - assertTrue("maybe matches A", ex.matchesFieldSet(x, a).maybeMatches()); - assertFalse("maybe matches A", ex.matchesFieldSet(x, a).alwaysMatches()); - // target - ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); - assertTrue("matches B", ex.matchesFieldSet(y, foo).alwaysMatches()); - assertTrue("maybe matches A", ex.matchesFieldSet(x, foo).maybeMatches()); - assertFalse("maybe matches A", ex.matchesFieldSet(x, foo).alwaysMatches()); - // args - ex = p.parsePointcutExpression("args(int)"); - assertTrue("matches x", ex.matchesFieldSet(x, a).alwaysMatches()); - assertTrue("matches y", ex.matchesFieldSet(y, a).alwaysMatches()); - assertTrue("does not match n", ex.matchesFieldSet(n, a).neverMatches()); - // within - ex = p.parsePointcutExpression("within(*..A)"); - assertTrue("Matches in class A", ex.matchesFieldSet(x, a).alwaysMatches()); - assertTrue("Does not match in class B", ex.matchesFieldSet(x, b).neverMatches()); - // withincode - ex = p.parsePointcutExpression("withincode(* a*(..))"); - assertTrue("Should match", ex.matchesFieldSet(x, aa).alwaysMatches()); - assertTrue("Should not match", ex.matchesFieldSet(x, b).neverMatches()); - } - - public void testMatchesFieldGet() { - PointcutExpression ex = p.parsePointcutExpression("get(* *..A+.*)"); - assertTrue("matches x", ex.matchesFieldGet(x, a).alwaysMatches()); - assertTrue("matches y", ex.matchesFieldGet(y, foo).alwaysMatches()); - assertTrue("does not match n", ex.matchesFieldGet(n, foo).neverMatches()); - // this - ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.Client)"); - assertTrue("matches Client", ex.matchesFieldGet(x, foo).alwaysMatches()); - assertTrue("does not match A", ex.matchesFieldGet(n, a).neverMatches()); - ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); - assertTrue("maybe matches A", ex.matchesFieldGet(x, a).maybeMatches()); - assertFalse("maybe matches A", ex.matchesFieldGet(x, a).alwaysMatches()); - // target - ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); - assertTrue("matches B", ex.matchesFieldGet(y, foo).alwaysMatches()); - assertTrue("maybe matches A", ex.matchesFieldGet(x, foo).maybeMatches()); - assertFalse("maybe matches A", ex.matchesFieldGet(x, foo).alwaysMatches()); - // args - no args at get join point - ex = p.parsePointcutExpression("args(int)"); - assertTrue("matches x", ex.matchesFieldGet(x, a).neverMatches()); - // within - ex = p.parsePointcutExpression("within(*..A)"); - assertTrue("Matches in class A", ex.matchesFieldGet(x, a).alwaysMatches()); - assertTrue("Does not match in class B", ex.matchesFieldGet(x, b).neverMatches()); - // withincode - ex = p.parsePointcutExpression("withincode(* a*(..))"); - assertTrue("Should match", ex.matchesFieldGet(x, aa).alwaysMatches()); - assertTrue("Should not match", ex.matchesFieldGet(x, b).neverMatches()); - } - - public void testArgsMatching() { - // too few args - PointcutExpression ex = p.parsePointcutExpression("args(*,*,*,*)"); - assertTrue("Too few args", ex.matchesMethodExecution(foo).neverMatches()); - assertTrue("Matching #args", ex.matchesMethodExecution(bar).alwaysMatches()); - // one too few + ellipsis - ex = p.parsePointcutExpression("args(*,*,*,..)"); - assertTrue("Matches with ellipsis", ex.matchesMethodExecution(foo).alwaysMatches()); - // exact number + ellipsis - assertTrue("Matches with ellipsis", ex.matchesMethodExecution(bar).alwaysMatches()); - assertTrue("Does not match with ellipsis", ex.matchesMethodExecution(a).neverMatches()); - // too many + ellipsis - ex = p.parsePointcutExpression("args(*,..,*)"); - assertTrue("Matches with ellipsis", ex.matchesMethodExecution(bar).alwaysMatches()); - assertTrue("Does not match with ellipsis", ex.matchesMethodExecution(a).neverMatches()); - assertTrue("Matches with ellipsis", ex.matchesMethodExecution(aaa).alwaysMatches()); - // exact match - ex = p.parsePointcutExpression("args(String,int,Number)"); - assertTrue("Matches exactly", ex.matchesMethodExecution(foo).alwaysMatches()); - // maybe match - ex = p.parsePointcutExpression("args(String,int,Double)"); - assertTrue("Matches maybe", ex.matchesMethodExecution(foo).maybeMatches()); - assertFalse("Matches maybe", ex.matchesMethodExecution(foo).alwaysMatches()); - // never match - ex = p.parsePointcutExpression("args(String,Integer,Number)"); - if (LangUtil.is15VMOrGreater()) { - assertTrue("matches", ex.matchesMethodExecution(foo).alwaysMatches()); - } else { - assertTrue("Does not match", ex.matchesMethodExecution(foo).neverMatches()); - } - } - - // public void testMatchesDynamically() { - // // everything other than this,target,args should just return true - // PointcutExpression ex = p.parsePointcutExpression("call(* *.*(..)) && execution(* *.*(..)) &&" + - // "get(* *) && set(* *) && initialization(new(..)) && preinitialization(new(..)) &&" + - // "staticinitialization(X) && adviceexecution() && within(Y) && withincode(* *.*(..)))"); - // assertTrue("Matches dynamically",ex.matchesDynamically(a,b,new Object[0])); - // // this - // ex = p.parsePointcutExpression("this(String)"); - // assertTrue("String matches",ex.matchesDynamically("",this,new Object[0])); - // assertFalse("Object doesn't match",ex.matchesDynamically(new Object(),this,new Object[0])); - // ex = p.parsePointcutExpression("this(org.aspectj.weaver.tools.PointcutExpressionTest.A)"); - // assertTrue("A matches",ex.matchesDynamically(new A(""),this,new Object[0])); - // assertTrue("B matches",ex.matchesDynamically(new B(""),this,new Object[0])); - // // target - // ex = p.parsePointcutExpression("target(String)"); - // assertTrue("String matches",ex.matchesDynamically(this,"",new Object[0])); - // assertFalse("Object doesn't match",ex.matchesDynamically(this,new Object(),new Object[0])); - // ex = p.parsePointcutExpression("target(org.aspectj.weaver.tools.PointcutExpressionTest.A)"); - // assertTrue("A matches",ex.matchesDynamically(this,new A(""),new Object[0])); - // assertTrue("B matches",ex.matchesDynamically(this,new B(""),new Object[0])); - // // args - // ex = p.parsePointcutExpression("args(*,*,*,*)"); - // assertFalse("Too few args",ex.matchesDynamically(null,null,new Object[]{a,b})); - // assertTrue("Matching #args",ex.matchesDynamically(null,null,new Object[]{a,b,aa,aaa})); - // // one too few + ellipsis - // ex = p.parsePointcutExpression("args(*,*,*,..)"); - // assertTrue("Matches with ellipsis",ex.matchesDynamically(null,null,new Object[]{a,b,aa,aaa})); - // // exact number + ellipsis - // assertTrue("Matches with ellipsis",ex.matchesDynamically(null,null,new Object[]{a,b,aa})); - // assertFalse("Does not match with ellipsis",ex.matchesDynamically(null,null,new Object[]{a,b})); - // // too many + ellipsis - // ex = p.parsePointcutExpression("args(*,..,*)"); - // assertTrue("Matches with ellipsis",ex.matchesDynamically(null,null,new Object[]{a,b,aa,aaa})); - // assertFalse("Does not match with ellipsis",ex.matchesDynamically(null,null,new Object[]{a})); - // assertTrue("Matches with ellipsis",ex.matchesDynamically(null,null,new Object[]{a,b})); - // // exact match - // ex = p.parsePointcutExpression("args(String,int,Number)"); - // assertTrue("Matches exactly",ex.matchesDynamically(null,null,new Object[]{"",new Integer(5),new Double(5.0)})); - // ex = p.parsePointcutExpression("args(String,Integer,Number)"); - // assertTrue("Matches exactly",ex.matchesDynamically(null,null,new Object[]{"",new Integer(5),new Double(5.0)})); - // // never match - // ex = p.parsePointcutExpression("args(String,Integer,Number)"); - // assertFalse("Does not match",ex.matchesDynamically(null,null,new Object[]{a,b,aa})); - // } - - public void testGetPointcutExpression() { - PointcutExpression ex = p.parsePointcutExpression("staticinitialization(*..A+)"); - assertEquals("staticinitialization(*..A+)", ex.getPointcutExpression()); - } - - public void testCouldMatchJoinPointsInType() { - PointcutExpression ex = p.parsePointcutExpression("execution(* org.aspectj.weaver.tools.PointcutExpressionTest.B.*(..))"); - assertFalse("Could maybe match String (as best we know at this point)", ex.couldMatchJoinPointsInType(String.class)); - assertTrue("Will always match B", ex.couldMatchJoinPointsInType(B.class)); - ex = p.parsePointcutExpression("within(org.aspectj.weaver.tools.PointcutExpressionTest.B)"); - assertFalse("Will never match String", ex.couldMatchJoinPointsInType(String.class)); - assertTrue("Will always match B", ex.couldMatchJoinPointsInType(B.class)); - } - - public void testMayNeedDynamicTest() { - PointcutExpression ex = p.parsePointcutExpression("execution(* org.aspectj.weaver.tools.PointcutExpressionTest.B.*(..))"); - assertFalse("No dynamic test needed", ex.mayNeedDynamicTest()); - ex = p - .parsePointcutExpression("execution(* org.aspectj.weaver.tools.PointcutExpressionTest.B.*(..)) && args(org.aspectj.weaver.tools.PointcutExpressionTest.X)"); - assertTrue("Dynamic test needed", ex.mayNeedDynamicTest()); - } - - protected void setUp() throws Exception { - super.setUp(); - p = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingSpecifiedClassloaderForResolution(this.getClass() - .getClassLoader()); - asCons = A.class.getConstructor(new Class[] { String.class }); - bsCons = B.class.getConstructor(new Class[0]); - bsStringCons = B.class.getConstructor(new Class[] { String.class }); - a = A.class.getMethod("a", new Class[0]); - aa = A.class.getMethod("aa", new Class[] { int.class }); - aaa = A.class.getMethod("aaa", new Class[] { String.class, int.class }); - x = A.class.getDeclaredField("x"); - y = B.class.getDeclaredField("y"); - b = B.class.getMethod("b", new Class[0]); - bsaa = B.class.getMethod("aa", new Class[] { int.class }); - clientCons = Client.class.getConstructor(new Class[0]); - n = Client.class.getDeclaredField("n"); - foo = Client.class.getDeclaredMethod("foo", new Class[] { String.class, int.class, Number.class }); - bar = Client.class.getDeclaredMethod("bar", new Class[] { String.class, int.class, Integer.class, Number.class }); - } - - static class A { - public A(String s) { - } - - public void a() { - } - - public void aa(int i) { - } - - public void aaa(String s, int i) { - } - - int x; - } - - static class B extends A { - public B() { - super(""); - } - - public B(String s) { - super(s); - } - - public String b() { - return null; - } - - public void aa(int i) { - } - - int y; - } - - static class Client { - public Client() { - } - - Number n; - - public void foo(String s, int i, Number n) { - } - - public void bar(String s, int i, Integer i2, Number n) { - } - } - - static class X { - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/tools/PointcutParserTest.java b/weaver/testsrc/org/aspectj/weaver/tools/PointcutParserTest.java deleted file mode 100644 index 4654b049d..000000000 --- a/weaver/testsrc/org/aspectj/weaver/tools/PointcutParserTest.java +++ /dev/null @@ -1,391 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2004 IBM Corporation. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * ******************************************************************/ -package org.aspectj.weaver.tools; - -import java.util.HashSet; -import java.util.Properties; -import java.util.Set; - -import junit.framework.Assert; -import junit.framework.TestCase; - -import org.aspectj.bridge.AbortException; -import org.aspectj.bridge.IMessage; -import org.aspectj.bridge.IMessage.Kind; -import org.aspectj.bridge.IMessageHandler; -import org.aspectj.util.LangUtil; -import org.aspectj.weaver.patterns.PatternParser; -import org.aspectj.weaver.patterns.Pointcut; -import org.aspectj.weaver.patterns.PointcutRewriter; - -/** - * Test cases for the PointcutParser class - */ -public class PointcutParserTest extends TestCase { - - private boolean needToSkip = false; - - /** this condition can occur on the build machine only, and is way too complex to fix right now... */ - private boolean needToSkipPointcutParserTests() { - if (!LangUtil.is15VMOrGreater()) { - return false; - } - try { - Class.forName("org.aspectj.weaver.reflect.Java15ReflectionBasedReferenceTypeDelegate", false, this.getClass() - .getClassLoader());// ReflectionBasedReferenceTypeDelegate.class.getClassLoader()); - } catch (ClassNotFoundException cnfEx) { - return true; - } - return false; - } - - protected void setUp() throws Exception { - super.setUp(); - needToSkip = needToSkipPointcutParserTests(); - } - - public void testGetAllSupportedPointcutPrimitives() { - if (needToSkip) { - return; - } - - Set s = PointcutParser.getAllSupportedPointcutPrimitives(); - assertEquals("Should be 21 elements in the set", 21, s.size()); - assertFalse("Should not contain if pcd", s.contains(PointcutPrimitive.IF)); - assertFalse("Should not contain cflow pcd", s.contains(PointcutPrimitive.CFLOW)); - assertFalse("Should not contain cflowbelow pcd", s.contains(PointcutPrimitive.CFLOW_BELOW)); - } - - public void testEmptyConstructor() { - if (needToSkip) { - return; - } - - PointcutParser parser = PointcutParser - .getPointcutParserSupportingAllPrimitivesAndUsingSpecifiedClassloaderForResolution(this.getClass().getClassLoader()); - Set s = parser.getSupportedPrimitives(); - assertEquals("Should be 21 elements in the set", 21, s.size()); - assertFalse("Should not contain if pcd", s.contains(PointcutPrimitive.IF)); - assertFalse("Should not contain cflow pcd", s.contains(PointcutPrimitive.CFLOW)); - assertFalse("Should not contain cflowbelow pcd", s.contains(PointcutPrimitive.CFLOW_BELOW)); - } - - public void testSetConstructor() { - if (needToSkip) { - return; - } - - Set p = PointcutParser.getAllSupportedPointcutPrimitives(); - PointcutParser parser = PointcutParser - .getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(p, this.getClass() - .getClassLoader()); - assertEquals("Should use the set we pass in", p, parser.getSupportedPrimitives()); - Set q = new HashSet(); - q.add(PointcutPrimitive.ARGS); - parser = PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(q, this - .getClass().getClassLoader()); - assertEquals("Should have only one element in set", 1, parser.getSupportedPrimitives().size()); - assertEquals("Should only have ARGS pcd", PointcutPrimitive.ARGS, parser.getSupportedPrimitives().iterator().next()); - } - - public void testParsePointcutExpression() { - if (needToSkip) { - return; - } - - PointcutParser p = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingSpecifiedClassloaderForResolution(this - .getClass().getClassLoader()); - IMessageHandler current = p.setCustomMessageHandler(new IgnoreWarningsMessageHandler()); - try { - p.parsePointcutExpression("(adviceexecution() || execution(* *.*(..)) || handler(Exception) || " - + "call(Foo Bar+.*(Goo)) || get(* foo) || set(Foo+ (Goo||Moo).s*) || " - + "initialization(Foo.new(..)) || preinitialization(*.new(Foo,..)) || " - + "staticinitialization(org.xzy.abc..*)) && (this(Foo) || target(Boo) ||" + "args(A,B,C)) && !handler(X)"); - } finally { - p.setCustomMessageHandler(current); - } - try { - p.parsePointcutExpression("gobble-de-gook()"); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException ex) { - } - } - - public void testParseExceptionErrorMessages() { - if (needToSkip) { - return; - } - - PointcutParser p = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingSpecifiedClassloaderForResolution(this - .getClass().getClassLoader()); - try { - p.parsePointcutExpression("execution(int Foo.*(..) && args(Double)"); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException ex) { - assertTrue("Pointcut is not well-formed message", - ex.getMessage().startsWith("Pointcut is not well-formed: expecting ')' at character position 24")); - } - } - - public void testOperatorPrecedence_319190() throws Exception { - if (needToSkip) { - return; - } - - String s = null; - Pointcut p = null; - - s = "(execution(* A.credit(float)) || execution(* A.debit(float))) && this(acc) && args(am) || execution(* C.*(Account, float)) && args(acc, am)"; - p = new PatternParser(s).parsePointcut(); - Assert.assertEquals( - "(((execution(* A.credit(float)) || execution(* A.debit(float))) && (this(acc) && args(am))) || (execution(* C.*(Account, float)) && args(acc, am)))", - p.toString()); - - s = "(if(true) || if(false)) && this(acc) && args(am) || if(true) && args(acc, am)"; - p = new PatternParser(s).parsePointcut(); - // bugged was: ((if(true) || if(false)) && (this(acc) && (args(am) || (if(true) && args(acc, am))))) - Assert.assertEquals("(((if(true) || if(false)) && (this(acc) && args(am))) || (if(true) && args(acc, am)))", p.toString()); - p = new PointcutRewriter().rewrite(p); - Assert.assertEquals("(((this(acc) && args(am)) && if(true)) || (args(acc, am) && if(true)))", p.toString()); - - s = "if(true) && if(false) || if(true)"; - p = new PatternParser(s).parsePointcut(); - assertEquals("((if(true) && if(false)) || if(true))", p.toString()); - p = new PointcutRewriter().rewrite(p); - Assert.assertEquals("if(true)", p.toString()); - } - - public void testParseIfPCD() { - if (needToSkip) { - return; - } - - PointcutParser p = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingSpecifiedClassloaderForResolution(this - .getClass().getClassLoader()); - try { - p.parsePointcutExpression("if(true)"); - fail("Expected UnsupportedPointcutPrimitiveException"); - } catch (UnsupportedPointcutPrimitiveException ex) { - assertEquals("Should not support IF", PointcutPrimitive.IF, ex.getUnsupportedPrimitive()); - } - } - - public void testParseCflowPCDs() { - if (needToSkip) { - return; - } - - PointcutParser p = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingSpecifiedClassloaderForResolution(this - .getClass().getClassLoader()); - try { - p.parsePointcutExpression("cflow(this(t))"); - fail("Expected UnsupportedPointcutPrimitiveException"); - } catch (UnsupportedPointcutPrimitiveException ex) { - assertEquals("Should not support CFLOW", PointcutPrimitive.CFLOW, ex.getUnsupportedPrimitive()); - } - try { - p.parsePointcutExpression("cflowbelow(this(t))"); - fail("Expected UnsupportedPointcutPrimitiveException"); - } catch (UnsupportedPointcutPrimitiveException ex) { - assertEquals("Should not support CFLOW_BELOW", PointcutPrimitive.CFLOW_BELOW, ex.getUnsupportedPrimitive()); - } - } - - public void testParseReferencePCDs() { - if (needToSkip) { - return; - } - - Set pcKinds = PointcutParser.getAllSupportedPointcutPrimitives(); - pcKinds.remove(PointcutPrimitive.REFERENCE); - PointcutParser p = PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution( - pcKinds, this.getClass().getClassLoader()); - try { - p.parsePointcutExpression("bananas(String)"); - fail("Expected UnsupportedPointcutPrimitiveException"); - } catch (UnsupportedPointcutPrimitiveException ex) { - assertTrue(ex.getUnsupportedPrimitive() == PointcutPrimitive.REFERENCE); - } - } - - public void testParseUnsupportedPCDs() { - if (needToSkip) { - return; - } - - Set s = new HashSet(); - PointcutParser p = PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution( - s, this.getClass().getClassLoader()); - try { - p.parsePointcutExpression("args(x)"); - fail("Expected UnsupportedPointcutPrimitiveException"); - } catch (UnsupportedPointcutPrimitiveException ex) { - assertEquals("Args", PointcutPrimitive.ARGS, ex.getUnsupportedPrimitive()); - } - try { - p.parsePointcutExpression("within(x)"); - fail("Expected UnsupportedPointcutPrimitiveException"); - } catch (UnsupportedPointcutPrimitiveException ex) { - assertEquals("Within", PointcutPrimitive.WITHIN, ex.getUnsupportedPrimitive()); - } - try { - p.parsePointcutExpression("withincode(new(..))"); - fail("Expected UnsupportedPointcutPrimitiveException"); - } catch (UnsupportedPointcutPrimitiveException ex) { - assertEquals("Withincode", PointcutPrimitive.WITHIN_CODE, ex.getUnsupportedPrimitive()); - } - try { - p.parsePointcutExpression("handler(Exception)"); - fail("Expected UnsupportedPointcutPrimitiveException"); - } catch (UnsupportedPointcutPrimitiveException ex) { - assertEquals("handler", PointcutPrimitive.HANDLER, ex.getUnsupportedPrimitive()); - } - try { - p.parsePointcutExpression("this(X)"); - fail("Expected UnsupportedPointcutPrimitiveException"); - } catch (UnsupportedPointcutPrimitiveException ex) { - assertEquals("this", PointcutPrimitive.THIS, ex.getUnsupportedPrimitive()); - } - try { - p.parsePointcutExpression("target(X)"); - fail("Expected UnsupportedPointcutPrimitiveException"); - } catch (UnsupportedPointcutPrimitiveException ex) { - assertEquals("target", PointcutPrimitive.TARGET, ex.getUnsupportedPrimitive()); - } - try { - p.parsePointcutExpression("this(X) && target(Y)"); - fail("Expected UnsupportedPointcutPrimitiveException"); - } catch (UnsupportedPointcutPrimitiveException ex) { - assertEquals("This", PointcutPrimitive.THIS, ex.getUnsupportedPrimitive()); - } - try { - p.parsePointcutExpression("this(X) || target(Y)"); - fail("Expected UnsupportedPointcutPrimitiveException"); - } catch (UnsupportedPointcutPrimitiveException ex) { - assertEquals("This", PointcutPrimitive.THIS, ex.getUnsupportedPrimitive()); - } - try { - p.parsePointcutExpression("!this(X)"); - fail("Expected UnsupportedPointcutPrimitiveException"); - } catch (UnsupportedPointcutPrimitiveException ex) { - assertEquals("This", PointcutPrimitive.THIS, ex.getUnsupportedPrimitive()); - } - try { - p.parsePointcutExpression("call(* *.*(..))"); - fail("Expected UnsupportedPointcutPrimitiveException"); - } catch (UnsupportedPointcutPrimitiveException ex) { - assertEquals("Call", PointcutPrimitive.CALL, ex.getUnsupportedPrimitive()); - } - try { - p.parsePointcutExpression("execution(* *.*(..))"); - fail("Expected UnsupportedPointcutPrimitiveException"); - } catch (UnsupportedPointcutPrimitiveException ex) { - assertEquals("Execution", PointcutPrimitive.EXECUTION, ex.getUnsupportedPrimitive()); - } - try { - p.parsePointcutExpression("get(* *)"); - fail("Expected UnsupportedPointcutPrimitiveException"); - } catch (UnsupportedPointcutPrimitiveException ex) { - assertEquals("Get", PointcutPrimitive.GET, ex.getUnsupportedPrimitive()); - } - try { - p.parsePointcutExpression("set(* *)"); - fail("Expected UnsupportedPointcutPrimitiveException"); - } catch (UnsupportedPointcutPrimitiveException ex) { - assertEquals("Set", PointcutPrimitive.SET, ex.getUnsupportedPrimitive()); - } - try { - p.parsePointcutExpression("initialization(new(..))"); - fail("Expected UnsupportedPointcutPrimitiveException"); - } catch (UnsupportedPointcutPrimitiveException ex) { - assertEquals("Initialization", PointcutPrimitive.INITIALIZATION, ex.getUnsupportedPrimitive()); - } - try { - p.parsePointcutExpression("preinitialization(new(..))"); - fail("Expected UnsupportedPointcutPrimitiveException"); - } catch (UnsupportedPointcutPrimitiveException ex) { - assertEquals("Prc-init", PointcutPrimitive.PRE_INITIALIZATION, ex.getUnsupportedPrimitive()); - } - try { - p.parsePointcutExpression("staticinitialization(T)"); - fail("Expected UnsupportedPointcutPrimitiveException"); - } catch (UnsupportedPointcutPrimitiveException ex) { - assertEquals("Staticinit", PointcutPrimitive.STATIC_INITIALIZATION, ex.getUnsupportedPrimitive()); - } - } - - public void testFormals() { - if (needToSkip) { - return; - } - - PointcutParser parser = PointcutParser - .getPointcutParserSupportingAllPrimitivesAndUsingSpecifiedClassloaderForResolution(this.getClass().getClassLoader()); - PointcutParameter param = parser.createPointcutParameter("x", String.class); - PointcutExpression pc = parser.parsePointcutExpression("args(x)", null, new PointcutParameter[] { param }); - assertEquals("args(x)", pc.getPointcutExpression()); - - try { - pc = parser.parsePointcutExpression("args(String)", null, new PointcutParameter[] { param }); - fail("Expecting IllegalArgumentException"); - } catch (IllegalArgumentException ex) { - assertTrue("formal unbound", ex.getMessage().indexOf("formal unbound") != -1); - } - - try { - pc = parser.parsePointcutExpression("args(y)"); - fail("Expecting IllegalArgumentException"); - } catch (IllegalArgumentException ex) { - assertTrue("no match for type name", ex.getMessage().indexOf("warning no match for this type name: y") != -1); - } - } - - public void testXLintConfiguration() { - if (needToSkip) { - return; - } - - PointcutParser p = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingSpecifiedClassloaderForResolution(this - .getClass().getClassLoader()); - try { - p.parsePointcutExpression("this(FooBar)"); - } catch (IllegalArgumentException ex) { - assertTrue("should have xlint:invalidAbsoluteTypeName", ex.getMessage().indexOf("Xlint:invalidAbsoluteTypeName") != -1); - } - Properties props = new Properties(); - props.put("invalidAbsoluteTypeName", "ignore"); - p.setLintProperties(props); - p.parsePointcutExpression("this(FooBar)"); - } - - private static class IgnoreWarningsMessageHandler implements IMessageHandler { - - public boolean handleMessage(IMessage message) throws AbortException { - if (message.getKind() != IMessage.WARNING) { - throw new RuntimeException("unexpected message: " + message.toString()); - } - return true; - } - - public boolean isIgnoring(Kind kind) { - if (kind != IMessage.ERROR) { - return true; - } - return false; - } - - public void dontIgnore(Kind kind) { - } - - public void ignore(Kind kind) { - } - - } -} diff --git a/weaver/testsrc/org/aspectj/weaver/tools/ReadingAttributes.java b/weaver/testsrc/org/aspectj/weaver/tools/ReadingAttributes.java deleted file mode 100644 index 180331c93..000000000 --- a/weaver/testsrc/org/aspectj/weaver/tools/ReadingAttributes.java +++ /dev/null @@ -1,64 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2009 Contributors - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Andy Clement - * ******************************************************************/ -package org.aspectj.weaver.tools; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; - -import junit.framework.TestCase; - -import org.aspectj.apache.bcel.classfile.Attribute; -import org.aspectj.apache.bcel.classfile.JavaClass; -import org.aspectj.apache.bcel.classfile.Unknown; -import org.aspectj.apache.bcel.util.ClassPath; -import org.aspectj.apache.bcel.util.SyntheticRepository; -import org.aspectj.weaver.VersionedDataInputStream; -import org.aspectj.weaver.WeaverStateInfo; - -public class ReadingAttributes extends TestCase { - - public void testWeaverStateInfo() throws ClassNotFoundException, IOException { - - JavaClass jc = getClassFrom(new File("n:/temp"), "com.springsource.petclinic.domain.Visit"); - assertNotNull(jc); - Attribute[] attrs = jc.getAttributes(); - for (int i = 0; i < attrs.length; i++) { - System.out.println(attrs[i].getName()); - if (attrs[i].getName().endsWith("WeaverState")) { - Unknown u = (Unknown) attrs[i]; - VersionedDataInputStream vdis = new VersionedDataInputStream(new ByteArrayInputStream(u.getBytes()), null); - // WeaverStateInfo wsi = - WeaverStateInfo.read(vdis, null); - // System.out.println(wsi); - } - } - // Method[] meths = jc.getMethods(); - // Method oneWeWant = null; - // for (int i = 0; i < meths.length && oneWeWant == null; i++) { - // Method method = meths[i]; - // if (method.getName().equals("main")) { - // oneWeWant = meths[i]; - // } - // } - } - - public SyntheticRepository createRepos(File cpentry) { - ClassPath cp = new ClassPath(cpentry + File.pathSeparator + System.getProperty("java.class.path")); - return SyntheticRepository.getInstance(cp); - } - - protected JavaClass getClassFrom(File where, String clazzname) throws ClassNotFoundException { - SyntheticRepository repos = createRepos(where); - return repos.loadClass(clazzname); - } -} diff --git a/weaver/testsrc/org/aspectj/weaver/tools/ToolsTests.java b/weaver/testsrc/org/aspectj/weaver/tools/ToolsTests.java deleted file mode 100644 index 636fd7a1f..000000000 --- a/weaver/testsrc/org/aspectj/weaver/tools/ToolsTests.java +++ /dev/null @@ -1,34 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2004 IBM Corporation. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * ******************************************************************/ -package org.aspectj.weaver.tools; - -import org.aspectj.testing.util.TestUtil; - -import junit.framework.Test; -import junit.framework.TestSuite; - -public class ToolsTests { - - public static Test suite() { - TestSuite suite = new TestSuite(ToolsTests.class.getName()); - //$JUnit-BEGIN$ - /* FIXME maw The CLASSPATH is wrong so run them in weaver5 instead */ - if (!TestUtil.is15VMOrGreater()) { - suite.addTestSuite(PointcutExpressionTest.class); - } else { - suite.addTest(TestUtil.testNamed("run from weaver5 under 1.5")); - } - suite.addTestSuite(PointcutParserTest.class); - suite.addTestSuite(TypePatternMatcherTest.class); - suite.addTestSuite(PointcutDesignatorHandlerTests.class); - //$JUnit-END$ - return suite; - } -} diff --git a/weaver/testsrc/org/aspectj/weaver/tools/TypePatternMatcherTest.java b/weaver/testsrc/org/aspectj/weaver/tools/TypePatternMatcherTest.java deleted file mode 100644 index 523ee4a83..000000000 --- a/weaver/testsrc/org/aspectj/weaver/tools/TypePatternMatcherTest.java +++ /dev/null @@ -1,55 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * IBM Corporation - initial API and implementation - *******************************************************************************/ -package org.aspectj.weaver.tools; - -import java.util.HashMap; -import java.util.Map; - -import org.aspectj.util.LangUtil; - -import junit.framework.TestCase; - -public class TypePatternMatcherTest extends TestCase { - - TypePatternMatcher tpm; - - private boolean needToSkip = false; - - /** this condition can occur on the build machine only, and is way too complex to fix right now... */ - private boolean needToSkipPointcutParserTests() { - if (!LangUtil.is15VMOrGreater()) return false; - try { - Class.forName("org.aspectj.weaver.reflect.Java15ReflectionBasedReferenceTypeDelegate",false,this.getClass().getClassLoader());//ReflectionBasedReferenceTypeDelegate.class.getClassLoader()); - } catch (ClassNotFoundException cnfEx) { - return true; - } - return false; - } - - public void testMatching() { - if (needToSkip) return; - - assertTrue("Map+ matches Map",tpm.matches(Map.class)); - assertTrue("Map+ matches HashMap",tpm.matches(HashMap.class)); - assertFalse("Map+ does not match String",tpm.matches(String.class)); - } - - protected void setUp() throws Exception { - super.setUp(); - needToSkip = needToSkipPointcutParserTests(); - if (needToSkip) return; - PointcutParser pp = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingSpecifiedClassloaderForResolution(this.getClass().getClassLoader()); - tpm = pp.parseTypePattern("java.util.Map+"); - } - - - -} diff --git a/weaver/testsrc/org/aspectj/weaver/tools/cache/AbstractCacheBackingTestSupport.java b/weaver/testsrc/org/aspectj/weaver/tools/cache/AbstractCacheBackingTestSupport.java deleted file mode 100644 index d8c1f27b6..000000000 --- a/weaver/testsrc/org/aspectj/weaver/tools/cache/AbstractCacheBackingTestSupport.java +++ /dev/null @@ -1,379 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * Lyor Goldstein (vmware) add support for weaved class being re-defined - *******************************************************************************/ -package org.aspectj.weaver.tools.cache; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.ObjectOutputStream; -import java.io.StreamCorruptedException; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.security.CodeSource; -import java.security.ProtectionDomain; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; - -import junit.framework.TestCase; - -import org.aspectj.util.FileUtil; -import org.aspectj.util.LangUtil; -import org.aspectj.weaver.tools.cache.AbstractIndexedFileCacheBacking.IndexEntry; - -/** - */ -public abstract class AbstractCacheBackingTestSupport extends TestCase { - public static final String JAR_FILE_SUFFIX=".jar"; - /** - * Prefix used in URL(s) that reference a resource inside a JAR - */ - public static final String JAR_URL_PREFIX="jar:"; - /** - * Separator used in URL(s) that reference a resource inside a JAR - * to denote the sub-path inside the JAR - */ - public static final char RESOURCE_SUBPATH_SEPARATOR='!'; - - private File targetFolder; - private File testTempFolder; - protected File root; - - public static final String TEMP_SUBFOLDER_NAME="temp"; - - protected AbstractCacheBackingTestSupport() { - super(); - } - - protected AbstractCacheBackingTestSupport(String name) { - super(name); - } - - @Override - public void setUp() throws Exception { - super.setUp(); - if (root == null) { - root = createTempFile("aspectj", "testdir"); - FileUtil.deleteContents(root); - } - } - - @Override - public void tearDown() throws Exception { - if (root != null) { - FileUtil.deleteContents(root); - root = null; - } - - if (targetFolder != null) { - FileUtil.deleteContents(targetFolder); - } - - super.tearDown(); - } - - protected File ensureTempFolderExists () throws IllegalStateException { - synchronized(TEMP_SUBFOLDER_NAME) { - if (testTempFolder == null) { - File parent=detectTargetFolder(); - testTempFolder = new File(parent, TEMP_SUBFOLDER_NAME); - } - } - - return ensureFolderExists(testTempFolder); - } - - protected File detectTargetFolder () throws IllegalStateException { - synchronized(TEMP_SUBFOLDER_NAME) { - if (targetFolder == null) { - if ((targetFolder=detectTargetFolder(getClass())) == null) { - throw new IllegalStateException("Failed to detect target folder"); - } - } - } - - return targetFolder; - } - - protected File createTempFile (String prefix, String suffix) throws IOException { - File destFolder=ensureTempFolderExists(); - return File.createTempFile(prefix, suffix, destFolder); - } - - public static final File ensureFolderExists (File folder) throws IllegalStateException { - if (folder == null) { - throw new IllegalArgumentException("No folder to ensure existence"); - } - - if ((!folder.exists()) && (!folder.mkdirs())) { - throw new IllegalStateException("Failed to create " + folder.getAbsolutePath()); - } - - return folder; - } - /** - * @param anchor An anchor {@link Class} whose container we want to use - * as the starting point for the "target" folder lookup up the - * hierarchy - * @return The "target" folder - null if not found - * @see #detectTargetFolder(File) - */ - public static final File detectTargetFolder (Class anchor) { - return detectTargetFolder(getClassContainerLocationFile(anchor)); - } - - /** - * @param anchorFile An anchor {@link File) we want to use - * as the starting point for the "target" folder lookup up the - * hierarchy - * @return The "target" folder - null if not found - */ - public static final File detectTargetFolder (File anchorFile) { - for (File file=anchorFile; file != null; file=file.getParentFile()) { - if (!file.isDirectory()) { - continue; - } - - String name=file.getName(); - if ("bin".equals(name) || "src".equals(name)) { - File parent=file.getParentFile(); - return new File(parent, "target"); - } - } - - return null; - } - - /** - * @param clazz A {@link Class} object - * @return A {@link File} of the location of the class bytes container - * - e.g., the root folder, the containing JAR, etc.. Returns - * null if location could not be resolved - * @throws IllegalArgumentException If location is not a valid - * {@link File} location - * @see #getClassContainerLocationURI(Class) - * @see File#File(URI) - */ - public static File getClassContainerLocationFile (Class clazz) - throws IllegalArgumentException { - try { - URI uri=getClassContainerLocationURI(clazz); - return (uri == null) ? null : new File(uri); - } catch(URISyntaxException e) { - throw new IllegalArgumentException(e.getClass().getSimpleName() + ": " + e.getMessage(), e); - } - } - - /** - * @param clazz A {@link Class} object - * @return A {@link URI} to the location of the class bytes container - * - e.g., the root folder, the containing JAR, etc.. Returns - * null if location could not be resolved - * @throws URISyntaxException if location is not a valid URI - * @see #getClassContainerLocationURL(Class) - */ - public static URI getClassContainerLocationURI (Class clazz) throws URISyntaxException { - URL url=getClassContainerLocationURL(clazz); - return (url == null) ? null : url.toURI(); - } - - /** - * @param clazz A {@link Class} object - * @return A {@link URL} to the location of the class bytes container - * - e.g., the root folder, the containing JAR, etc.. Returns - * null if location could not be resolved - */ - public static URL getClassContainerLocationURL (Class clazz) { - ProtectionDomain pd=clazz.getProtectionDomain(); - CodeSource cs=(pd == null) ? null : pd.getCodeSource(); - URL url=(cs == null) ? null : cs.getLocation(); - if (url == null) { - ClassLoader cl=getDefaultClassLoader(clazz); - String className=clazz.getName().replace('.', '/') + ".class"; - if ((url=cl.getResource(className)) == null) { - return null; - } - - String srcForm=getURLSource(url); - if (LangUtil.isEmpty(srcForm)) { - return null; - } - - try { - url = new URL(srcForm); - } catch(MalformedURLException e) { - throw new IllegalArgumentException("getClassContainerLocationURL(" + clazz.getName() + ")" - + "Failed to create URL=" + srcForm + " from " + url.toExternalForm() - + ": " + e.getMessage()); - } - } - - return url; - } - /** - * @param anchor An "anchor" {@link Class} to be used in case - * no thread context loader is available - * @return A {@link ClassLoader} to be used by the caller. The loader is - * resolved in the following manner:


- *
    - *
  • - * If a non-null loader is returned from the - * {@link Thread#getContextClassLoader()} call then use it. - *
  • - * - *
  • - * Otherwise, use the same loader that was used to load the anchor class. - *
  • - *
- * @throws IllegalArgumentException if no anchor class provided (regardless of - * whether it is used or not) - */ - public static ClassLoader getDefaultClassLoader(Class anchor) { - if (anchor == null) { - throw new IllegalArgumentException("No anchor class provided"); - } - - Thread t=Thread.currentThread(); - ClassLoader cl=t.getContextClassLoader(); - if (cl == null) { - // No thread context class loader -> use class loader of this class. - cl = anchor.getClassLoader(); - } - - if (cl == null) { // no class loader - assume system - cl = ClassLoader.getSystemClassLoader(); - } - - return cl; - - } - public static final String getURLSource (File file) { - return getURLSource((file == null) ? null : file.toURI()); - } - - public static final String getURLSource (URI uri) { - return getURLSource((uri == null) ? null : uri.toString()); - } - - /** - * @param url The {@link URL} value - ignored if null - * @return The URL(s) source path where {@link #JAR_URL_PREFIX} and - * any sub-resource are stripped - * @see #getURLSource(String) - */ - public static final String getURLSource (URL url) { - return getURLSource((url == null) ? null : url.toExternalForm()); - } - - /** - * @param externalForm The {@link URL#toExternalForm()} string - ignored if - * null/empty - * @return The URL(s) source path where {@link #JAR_URL_PREFIX} and - * any sub-resource are stripped - */ - public static final String getURLSource (String externalForm) { - String url=externalForm; - if (LangUtil.isEmpty(url)) { - return url; - } - - url = stripJarURLPrefix(externalForm); - if (LangUtil.isEmpty(url)){ - return url; - } - - int sepPos=url.indexOf(RESOURCE_SUBPATH_SEPARATOR); - if (sepPos < 0) { - return adjustURLPathValue(url); - } else { - return adjustURLPathValue(url.substring(0, sepPos)); - } - } - - /** - * @param path A URL path value - * @return The path after stripping any trailing '/' provided the path - * is not '/' itself - */ - public static final String adjustURLPathValue(final String path) { - final int pathLen=LangUtil.isEmpty(path) ? 0 : path.length(); - if ((pathLen <= 1) || (path.charAt(pathLen - 1) != '/')) { - return path; - } - - return path.substring(0, pathLen - 1); - } - - public static final String adjustURLPathValue(URL url) { - return adjustURLPathValue((url == null) ? null : url.getPath()); - } - - public static String stripJarURLPrefix(String externalForm) { - String url=externalForm; - if (LangUtil.isEmpty(url)) { - return url; - } - - if (url.startsWith(JAR_URL_PREFIX)) { - return url.substring(JAR_URL_PREFIX.length()); - } - - return url; - } - - protected static final void writeIndex (File indexFile, IndexEntry ... entries) throws IOException { - writeIndex(indexFile, LangUtil.isEmpty(entries) ? Collections.emptyList() : Arrays.asList(entries)); - } - - protected static final void writeIndex (File indexFile, Collection entries) throws IOException { - File indexDir=indexFile.getParentFile(); - if ((!indexDir.exists()) && (!indexDir.mkdirs())) { - throw new IOException("Failed to create path to " + indexFile.getAbsolutePath()); - } - - int numEntries=LangUtil.isEmpty(entries) ? 0 : entries.size(); - IndexEntry[] entryValues=(numEntries <= 0) ? null : entries.toArray(new IndexEntry[numEntries]); - // if no entries, simply delete the index file - if (LangUtil.isEmpty(entryValues)) { - if (indexFile.exists() && (!indexFile.delete())) { - throw new StreamCorruptedException("Failed to clean up index file at " + indexFile.getAbsolutePath()); - } - - return; - } - - ObjectOutputStream oos=new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(indexFile), 4096)); - try { - oos.writeObject(entryValues); - } finally { - oos.close(); - } - } - - public static final void assertArrayEquals (String msg, byte[] expected, byte[] actual) { - int eLen=LangUtil.isEmpty(expected) ? 0 : expected.length; - int aLen=LangUtil.isEmpty(actual) ? 0 : expected.length; - assertEquals(msg + "[mismatched length]", eLen, aLen); - - for (int index=0; index < eLen; index++) { - byte eb=expected[index], ab=actual[index]; - if (eb != ab) { - fail(msg + ": Mismatched value at index=" + index - + " - " + ab + " instead of " + eb - + ": expected=" + Arrays.toString(expected) + ", actual=" + Arrays.toString(actual)); - } - } - } -} diff --git a/weaver/testsrc/org/aspectj/weaver/tools/cache/AsynchronousFileCacheBackingTestSupport.java b/weaver/testsrc/org/aspectj/weaver/tools/cache/AsynchronousFileCacheBackingTestSupport.java deleted file mode 100644 index 7d1b66407..000000000 --- a/weaver/testsrc/org/aspectj/weaver/tools/cache/AsynchronousFileCacheBackingTestSupport.java +++ /dev/null @@ -1,193 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * Lyor Goldstein (vmware) add support for weaved class being re-defined - *******************************************************************************/ -package org.aspectj.weaver.tools.cache; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Map; -import java.util.Random; -import java.util.TreeMap; - -import org.aspectj.util.FileUtil; -import org.aspectj.util.LangUtil; -import org.aspectj.weaver.tools.cache.AbstractIndexedFileCacheBacking.IndexEntry; - -/** - */ -public abstract class AsynchronousFileCacheBackingTestSupport - extends AbstractCacheBackingTestSupport { - private File cacheDir, indexFile; - protected final byte[] bytes=new byte[Byte.MAX_VALUE]; - protected final Random random=new Random(System.nanoTime()); - - protected AsynchronousFileCacheBackingTestSupport() { - super(); - } - - protected AsynchronousFileCacheBackingTestSupport(String name) { - super(name); - } - - @Override - public void setUp () throws Exception { - super.setUp(); - cleanupCache(); - - random.nextBytes(bytes); - } - - @Override - public void tearDown () throws Exception { - cleanupCache(); - super.tearDown(); - } - - protected void cleanupCache() { - if (indexFile != null) { - if (FileUtil.deleteContents(indexFile) > 0) { - System.out.println("Deleted " + indexFile); - } - indexFile = null; - } - - if (cacheDir != null) { - if (FileUtil.deleteContents(cacheDir) > 0) { - System.out.println("Deleted " + cacheDir); - } - cacheDir = null; - } - } - - protected File getIndexFile () { - if (indexFile == null) { - File parent=getCacheDir(); - indexFile=new File(parent, AbstractIndexedFileCacheBacking.INDEX_FILE); - } - - return indexFile; - } - - protected File getCacheDir () { - if (cacheDir == null) { - File targetDir=detectTargetFolder(); - cacheDir = new File(targetDir, "dir-" + String.valueOf(Math.random())); - } - - return ensureFolderExists(cacheDir); - } - - protected abstract AsynchronousFileCacheBacking createFileBacking (File dir); - - public void testDeleteIndexFileOnEmptyIndex () throws Exception { - IndexEntry[] entries={ - createIndexEntry("weaved-empty", false, false, bytes, bytes), - createIndexEntry("generated-empty", true, false, bytes, bytes) - }; - File cacheIndex=getIndexFile(); - writeIndex(cacheIndex, entries); - assertTrue("No initial index file available: " + cacheIndex, cacheIndex.canRead()); - - AsynchronousFileCacheBacking cache=createFileBacking(getCacheDir()); - // the call should read an empty index since no data files exist - Map indexMap=cache.getIndexMap(); - assertEquals("Mismatched index size", 0, indexMap.size()); - - // no data files were created - Map bytesMap=cache.getBytesMap(); - assertEquals("Mismatched bytes size", 0, bytesMap.size()); - - writeIndex(cache.getIndexFile(), cache.getIndexEntries()); - - assertFalse("Index file still available: " + cacheIndex, cacheIndex.canRead()); - } - - protected long generateNewBytes () { - final long CRC=AbstractCacheBacking.crc(bytes); - long crc=CRC; - // 8 tries should be enough to find a non-matching CRC... - for (int index=0; (index < Byte.SIZE) && (CRC == crc) && (crc != -1L); index++) { - random.nextBytes(bytes); - crc = AbstractCacheBacking.crc(bytes); - } - assertTrue("Could not generate different CRC for " + CRC, crc != CRC); - - return crc; - } - - protected Map createDataFiles (IndexEntry ... entries) throws IOException { - return createDataFiles(LangUtil.isEmpty(entries) ? Collections.emptyList() : Arrays.asList(entries)); - } - - protected Map createDataFiles (Collection entries) throws IOException { - if (LangUtil.isEmpty(entries)) { - return Collections.emptyMap(); - } - - Map files=new TreeMap(); - for (IndexEntry entry : entries) { - File file=createDataFile(entry); - if (file != null) { - files.put(entry.key, file); - } - } - - return files; - } - - protected File createDataFile (IndexEntry entry) throws IOException { - return createDataFile(entry, entry.ignored ? null : bytes); - } - - protected File createDataFile (IndexEntry entry, byte[] dataBytes) throws IOException { - return createDataFile(entry.key, dataBytes); - } - - protected File createDataFile (String key, byte[] dataBytes) throws IOException { - if (LangUtil.isEmpty(dataBytes)) { - return null; - } - - File parent=getCacheDir(), file=new File(parent, key); - OutputStream out=new FileOutputStream(file); - try { - out.write(dataBytes); - } finally { - out.close(); - } - - return file; - } - - protected static final IndexEntry createIgnoredEntry (String key) { - return createIndexEntry(key, false, true, null, null); - } - - protected static final IndexEntry createIndexEntry (String key, boolean generated, boolean ignored, byte[] bytes, byte[] originalBytes) { - IndexEntry entry=new IndexEntry(); - entry.key = key; - entry.generated = generated; - entry.ignored = ignored; - if (ignored) { - assertFalse(key + " ignored cannot be generated", generated); - } else { - entry.crcClass = AbstractCacheBacking.crc(originalBytes); - entry.crcWeaved = AbstractCacheBacking.crc(bytes); - } - - return entry; - } -} diff --git a/weaver/testsrc/org/aspectj/weaver/tools/cache/CacheTests.java b/weaver/testsrc/org/aspectj/weaver/tools/cache/CacheTests.java deleted file mode 100644 index b59665b4e..000000000 --- a/weaver/testsrc/org/aspectj/weaver/tools/cache/CacheTests.java +++ /dev/null @@ -1,31 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * John Kew (vmware) initial implementation - *******************************************************************************/ - -package org.aspectj.weaver.tools.cache; - -import junit.framework.Test; -import junit.framework.TestSuite; - -/** - */ -public class CacheTests { - public static Test suite() { - TestSuite suite = new TestSuite(CacheTests.class.getName()); - suite.addTestSuite(SimpleClassCacheTest.class); - suite.addTestSuite(WeavedClassCacheTest.class); - suite.addTestSuite(DefaultCacheKeyResolverTest.class); - suite.addTestSuite(DefaultFileCacheBackingTest.class); - suite.addTestSuite(FlatFileCacheBackingTest.class); - suite.addTestSuite(ZippedFileCacheBackingTest.class); - return suite; - } -} diff --git a/weaver/testsrc/org/aspectj/weaver/tools/cache/DefaultCacheKeyResolverTest.java b/weaver/testsrc/org/aspectj/weaver/tools/cache/DefaultCacheKeyResolverTest.java deleted file mode 100644 index 139488b3c..000000000 --- a/weaver/testsrc/org/aspectj/weaver/tools/cache/DefaultCacheKeyResolverTest.java +++ /dev/null @@ -1,95 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * John Kew (vmware) initial implementation - *******************************************************************************/ - -package org.aspectj.weaver.tools.cache; - -import junit.framework.TestCase; - -import java.net.URL; -import java.net.URLClassLoader; -import java.util.Arrays; -import java.util.Collections; - -/** - */ -public class DefaultCacheKeyResolverTest extends TestCase { - byte[] FAKE_BYTES = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - String FAKE_CLASS = "com.example.foo.Bar"; - - DefaultCacheKeyResolver resolver = new DefaultCacheKeyResolver(); - - class BasicTestCL extends ClassLoader { - public BasicTestCL () { - super(); - } - } - - class URLTestCL extends URLClassLoader { - public URLTestCL(URL... urls) { - super(urls); - } - } - - - public void testNonURLClassLoaderScope() throws Exception { - String scope = resolver.createClassLoaderScope(new BasicTestCL(), Collections.emptyList()); - assertTrue(scope.startsWith(BasicTestCL.class.getSimpleName())); - } - - public void testCreateURLClassLoaderScope() throws Exception { - URL testURLA = new URL("http://example.com"); - URL testURLB = new URL("file:///tmp"); - URL testURLC = new URL("ftp://ftp.example.com"); - URLTestCL A = new URLTestCL(testURLA); - URLTestCL AB = new URLTestCL(testURLA, testURLB); - URLTestCL BC = new URLTestCL(testURLB, testURLC); - URLTestCL BC2 = new URLTestCL(testURLC, testURLB); - String[] a = {"one", "two", "three", "four"}; - String[] a2 = {"one", "two", "three"}; - String scopeAa = resolver.createClassLoaderScope(A, Arrays.asList(a)); - String scopeABa = resolver.createClassLoaderScope(AB, Arrays.asList(a)); - String scopeBCa = resolver.createClassLoaderScope(BC, Arrays.asList(a)); - String scopeBC2a = resolver.createClassLoaderScope(BC2, Arrays.asList(a)); - String scopeAa2 = resolver.createClassLoaderScope(A, Arrays.asList(a2)); - String scopeABa2 = resolver.createClassLoaderScope(AB, Arrays.asList(a2)); - String scopeBCa2 = resolver.createClassLoaderScope(BC, Arrays.asList(a2)); - String scopeBC2a2 = resolver.createClassLoaderScope(BC2, Arrays.asList(a2)); - - assertFalse(scopeAa.equals(scopeABa)); - assertFalse(scopeAa.equals(scopeBCa)); - assertFalse(scopeABa.equals(scopeBCa)); - assertTrue(scopeBC2a.equals(scopeBCa)); - assertFalse(scopeAa.equals(scopeAa2)); - assertFalse(scopeABa.equals(scopeABa2)); - assertFalse(scopeBCa.equals(scopeBCa2)); - assertFalse(scopeBC2a.equals(scopeBC2a2)); - - - } - - - public void testCreateGeneratedCacheKey() throws Exception { - CachedClassReference ref = resolver.generatedKey(FAKE_CLASS); - assertTrue(ref.getKey().startsWith(FAKE_CLASS)); - assertTrue(ref.getKey().matches(resolver.getGeneratedRegex())); - assertEquals(FAKE_CLASS, resolver.keyToClass(ref.getKey())); - } - - public void testCreateCacheKey() throws Exception { - // crc hashing - CachedClassReference ref = resolver.weavedKey(FAKE_CLASS, FAKE_BYTES); - assertTrue("key " + ref.getKey() + " does not match " + resolver.getWeavedRegex(), ref.getKey().matches(resolver.getWeavedRegex())); - String className = resolver.keyToClass(ref.getKey()); - assertEquals("class " + FAKE_CLASS + " != " + className, FAKE_CLASS, className); - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/tools/cache/DefaultFileCacheBackingTest.java b/weaver/testsrc/org/aspectj/weaver/tools/cache/DefaultFileCacheBackingTest.java deleted file mode 100644 index 2d5ec0c77..000000000 --- a/weaver/testsrc/org/aspectj/weaver/tools/cache/DefaultFileCacheBackingTest.java +++ /dev/null @@ -1,179 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * John Kew (vmware) initial implementation - * Lyor Goldstein (vmware) add support for weaved class being re-defined - *******************************************************************************/ - -package org.aspectj.weaver.tools.cache; - -import java.io.File; -import java.util.zip.CRC32; - -import org.aspectj.util.LangUtil; -import org.aspectj.weaver.tools.cache.AbstractIndexedFileCacheBacking.IndexEntry; - -/** - */ -public class DefaultFileCacheBackingTest extends AbstractCacheBackingTestSupport { - private final byte[] FAKE_BYTES = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - private final String FAKE_CLASS = "com.example.foo.Bar"; - private final CacheKeyResolver resolver = new DefaultCacheKeyResolver(); - private final CachedClassReference fakeRef = resolver.weavedKey(FAKE_CLASS, FAKE_BYTES); - private final String fakeKey=fakeRef.getKey(); - - public DefaultFileCacheBackingTest () { - super(); - } - - public void testCreateBacking() throws Exception { - CacheBacking backing = DefaultFileCacheBacking.createBacking(root); - assertNotNull(backing); - assertTrue("Root folder not created: " + root, root.exists()); - assertTrue("Root folder not a directory: " + root, root.isDirectory()); - } - - public void testClear() { - CacheBacking backing = DefaultFileCacheBacking.createBacking(root); - backing.put(new CachedClassEntry(fakeRef, FAKE_BYTES, CachedClassEntry.EntryType.WEAVED), FAKE_BYTES); - assertNotNull(backing.get(fakeRef, FAKE_BYTES)); - backing.clear(); - assertNull(backing.get(fakeRef, FAKE_BYTES)); - } - - private CachedClassEntry createTestEntry(String key) { - return new CachedClassEntry(new CachedClassReference(key, key), FAKE_BYTES, CachedClassEntry.EntryType.WEAVED); - } - - public void testGetKeys() throws Exception { - CacheBacking backing = DefaultFileCacheBacking.createBacking(root); - backing.put(createTestEntry("apple"), FAKE_BYTES); - backing.put(createTestEntry("apply"), FAKE_BYTES); - backing.put(createTestEntry("orange"), FAKE_BYTES); - String[] matches = backing.getKeys("app.*"); - assertEquals(2, matches.length); - matches = backing.getKeys("orange"); - assertEquals(1, matches.length); - assertEquals("orange", matches[0]); - } - - public void testPut() throws Exception { - CacheBacking backing = DefaultFileCacheBacking.createBacking(root); - backing.put(new CachedClassEntry(fakeRef, FAKE_BYTES, CachedClassEntry.EntryType.WEAVED), FAKE_BYTES); - File cachedFile = new File(root, fakeKey); - assertTrue(cachedFile.exists()); - assertTrue(cachedFile.isFile()); - assertEquals(FAKE_BYTES.length, cachedFile.length()); - } - - public void testGet() throws Exception { - DefaultFileCacheBacking backing = DefaultFileCacheBacking.createBacking(root); - assertNull(backing.get(fakeRef, FAKE_BYTES)); - backing.put(new CachedClassEntry(fakeRef, FAKE_BYTES, CachedClassEntry.EntryType.WEAVED), FAKE_BYTES); - File cachedFile = new File(root, fakeKey); - assertTrue(cachedFile.isFile()); - assertEquals(FAKE_BYTES.length, cachedFile.length()); - CRC32 expectedCRC = new CRC32(); - expectedCRC.update(FAKE_BYTES); - assertTrue(indexEntryExists(backing, fakeKey, expectedCRC.getValue())); - CachedClassEntry entry = backing.get(fakeRef, FAKE_BYTES); - assertEquals(FAKE_BYTES.length, entry.getBytes().length); - } - - public void testRemove() throws Exception { - DefaultFileCacheBacking backing = DefaultFileCacheBacking.createBacking(root); - backing.put(new CachedClassEntry(fakeRef, FAKE_BYTES, CachedClassEntry.EntryType.WEAVED), FAKE_BYTES); - File cachedFile = new File(root, fakeKey); - assertTrue("Cached file not found: " + cachedFile, cachedFile.exists()); - assertTrue("Cached file not a file: " + cachedFile, cachedFile.isFile()); - CRC32 expectedCRC = new CRC32(); - expectedCRC.update(FAKE_BYTES); - assertTrue("Cached entry index not found", indexEntryExists(backing, fakeKey, expectedCRC.getValue())); - backing.remove(fakeRef); - - assertFalse("CacheFile Still exists: " + cachedFile, cachedFile.exists()); - assertFalse("Cached file is a file: " + cachedFile, cachedFile.isFile()); - assertFalse("Cached entry index not removed", indexEntryExists(backing, fakeKey, expectedCRC.getValue())); - } - - public void testMultiFile() throws Exception { - CachedClassEntry entry; - File cachedFile; - CRC32 expectedCRC = new CRC32(); - expectedCRC.update(FAKE_BYTES); - DefaultFileCacheBacking backing = DefaultFileCacheBacking.createBacking(root); - // add weaved - CachedClassReference wref = resolver.weavedKey(FAKE_CLASS + "WEAVED", FAKE_BYTES); - entry = new CachedClassEntry(wref, FAKE_BYTES, CachedClassEntry.EntryType.WEAVED); - backing.put(entry, FAKE_BYTES); - cachedFile = new File(root, wref.getKey()); - assertTrue(cachedFile.exists()); - assertTrue(cachedFile.isFile()); - assertTrue(indexEntryExists(backing, wref.getKey(), expectedCRC.getValue())); - - // add generated - CachedClassReference gref = resolver.generatedKey(FAKE_CLASS + "GENERATED"); - entry = new CachedClassEntry(gref, FAKE_BYTES, CachedClassEntry.EntryType.GENERATED); - backing.put(entry, FAKE_BYTES); - cachedFile = new File(root, gref.getKey()); - assertTrue(cachedFile.exists()); - assertTrue(cachedFile.isFile()); - assertTrue(indexEntryExists(backing, gref.getKey(), expectedCRC.getValue())); - - // add ignored - CachedClassReference iref = resolver.generatedKey(FAKE_CLASS + "IGNORED"); - entry = new CachedClassEntry(iref, FAKE_BYTES, CachedClassEntry.EntryType.IGNORED); - backing.put(entry, FAKE_BYTES); - cachedFile = new File(root, iref.getKey()); - assertFalse(cachedFile.exists()); - assertTrue(indexEntryExists(backing, iref.getKey(), expectedCRC.getValue())); - - backing.remove(wref); - backing.remove(gref); - backing.remove(iref); - } - - public void testOriginalClassBytesChanged () { - DefaultFileCacheBacking backing = DefaultFileCacheBacking.createBacking(root); - backing.put(new CachedClassEntry(fakeRef, FAKE_BYTES, CachedClassEntry.EntryType.WEAVED), FAKE_BYTES); - - CachedClassEntry entry = backing.get(fakeRef, FAKE_BYTES); - assertNotNull("No initial entry", entry); - - byte[] newBytes=new byte[FAKE_BYTES.length]; - for (int index=0; index < FAKE_BYTES.length; index++) { - newBytes[index] = (byte) (0 - FAKE_BYTES[index]); - } - - entry = backing.get(fakeRef, newBytes); - assertNull("Unexpected modified bytes entry: " + entry, entry); - - File cachedFile = new File(root, fakeKey); - assertFalse("Cache file not removed", cachedFile.exists()); - } - - private boolean indexEntryExists(AbstractIndexedFileCacheBacking cache, String key, long expectedCRC) throws Exception { - long storedCRC = 0L; - IndexEntry[] index = cache.readIndex(new File(root, AbstractIndexedFileCacheBacking.INDEX_FILE)); - if (LangUtil.isEmpty(index)) { - return false; - } - - for (IndexEntry ie : index) { - if (ie.key.equals(key)) { - storedCRC = ie.crcWeaved; - if (!ie.ignored) { - assertEquals(expectedCRC, storedCRC); - } - return true; - } - } - return false; - } -} diff --git a/weaver/testsrc/org/aspectj/weaver/tools/cache/FlatFileCacheBackingTest.java b/weaver/testsrc/org/aspectj/weaver/tools/cache/FlatFileCacheBackingTest.java deleted file mode 100644 index 8c6df7ad8..000000000 --- a/weaver/testsrc/org/aspectj/weaver/tools/cache/FlatFileCacheBackingTest.java +++ /dev/null @@ -1,154 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012 VMware, Inc. - * - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Lyor Goldstein - *******************************************************************************/ - -package org.aspectj.weaver.tools.cache; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.Map; -import java.util.TreeMap; - -import org.aspectj.weaver.tools.cache.AbstractIndexedFileCacheBacking.IndexEntry; - -/** - * @author Lyor Goldstein - */ -public class FlatFileCacheBackingTest extends AsynchronousFileCacheBackingTestSupport { - public FlatFileCacheBackingTest() { - super(); - } - - @Override - protected FlatFileCacheBacking createFileBacking(File dir) { - return new FlatFileCacheBacking(dir); - } - - public void testReadIndex () throws IOException { - IndexEntry[] entries={ - createIgnoredEntry("ignored"), - createIndexEntry("weaved", false, false, bytes, bytes), - createIndexEntry("generated", true, false, bytes, bytes) - }; - File indexFile=getIndexFile(); - writeIndex(indexFile, entries); - Map dataFiles=createDataFiles(entries); - - File cacheDir=getCacheDir(); - AsynchronousFileCacheBacking cache=createFileBacking(cacheDir); - Map indexMap=cache.getIndexMap(); - assertEquals("Mismatched index size", entries.length, indexMap.size()); - - Map bytesMap=cache.getBytesMap(); - assertEquals("Mismatched bytes size", dataFiles.size() /* the ignored one has no file */, bytesMap.size()); - - for (IndexEntry entry : entries) { - String key=entry.key; - assertNotNull("Missing entry for key=" + key, indexMap.get(key)); - - if (entry.ignored) { - assertNull("Unexpected bytes for ignored key=" + key, bytesMap.get(key)); - } else { - assertArrayEquals("Mismatched contents for key=" + key, bytes, bytesMap.get(key)); - } - } - } - - public void testIgnoredBadCrcDataFiles () throws Exception { - IndexEntry[] entries={ - createIndexEntry("weaved-goodData", false, false, bytes, bytes), - createIndexEntry("badData-weaved", false, false, bytes, bytes), - createIndexEntry("generated-goodData", true, false, bytes, bytes), - createIndexEntry("badData-generated", true, false, bytes, bytes) - }; - File indexFile=getIndexFile(); - writeIndex(indexFile, entries); - - Map dataFiles=createDataFiles(entries); - long newCrc=generateNewBytes(); - assertTrue("Bad new CRC", newCrc != (-1L)); - - Map badFiles=new TreeMap(); - for (IndexEntry entry : entries) { - String key=entry.key; - if (key.startsWith("badData")) { - File file=dataFiles.get(key); - OutputStream out=new FileOutputStream(file); - try { - out.write(bytes); - } finally { - out.close(); - } - dataFiles.remove(key); - badFiles.put(key, file); - } - } - - File cacheDir=getCacheDir(); - FlatFileCacheBacking cache=createFileBacking(cacheDir); - Map indexMap=cache.getIndexMap(); - assertEquals("Mismatched index size", dataFiles.size(), indexMap.size()); - - Map bytesMap=cache.getBytesMap(); - assertEquals("Mismatched bytes size", dataFiles.size(), bytesMap.size()); - - for (Map.Entry badEntry : badFiles.entrySet()) { - String key=badEntry.getKey(); - assertFalse("Unexpectedly indexed: " + key, indexMap.containsKey(key)); - assertFalse("Unexpectedly loaded: " + key, bytesMap.containsKey(key)); - - File file=badEntry.getValue(); - assertFalse("Unexpectedly still readable: " + key, file.canRead()); - } - } - - public void testSkipMissingDataFileOnReadIndex () throws IOException { - IndexEntry[] entries={ - createIndexEntry("weaved-noData", false, false, null, null), - createIndexEntry("withData-weaved", false, false, bytes, bytes), - createIndexEntry("generated-noData", true, false, null, null), - createIndexEntry("withData-generated", true, false, bytes, bytes) - }; - File indexFile=getIndexFile(); - writeIndex(indexFile, entries); - - Map dataFiles=new TreeMap(); - for (IndexEntry entry : entries) { - String key=entry.key; - if (key.startsWith("withData")) { - dataFiles.put(key, createDataFile(entry, bytes)); - } - } - - File cacheDir=getCacheDir(); - FlatFileCacheBacking cache=createFileBacking(cacheDir); - Map indexMap=cache.getIndexMap(); - assertEquals("Mismatched index size", dataFiles.size(), indexMap.size()); - - Map bytesMap=cache.getBytesMap(); - assertEquals("Mismatched bytes size", dataFiles.size(), bytesMap.size()); - - for (IndexEntry entry : entries) { - String key=entry.key; - if (key.startsWith("withData")) { - assertTrue("Not indexed: " + key, indexMap.containsKey(key)); - assertTrue("Not loaded: " + key, bytesMap.containsKey(key)); - } else { - assertFalse("Unexpectedly indexed: " + key, indexMap.containsKey(key)); - assertFalse("Unexpectedly loaded: " + key, bytesMap.containsKey(key)); - } - } - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/tools/cache/SimpleClassCacheTest.java b/weaver/testsrc/org/aspectj/weaver/tools/cache/SimpleClassCacheTest.java deleted file mode 100644 index 68fac6913..000000000 --- a/weaver/testsrc/org/aspectj/weaver/tools/cache/SimpleClassCacheTest.java +++ /dev/null @@ -1,71 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * Abraham Nevado (lucierna) initial implementation - ********************************************************************************/ - -package org.aspectj.weaver.tools.cache; - -import java.io.File; - -import junit.framework.TestCase; - -/** - */ -public class SimpleClassCacheTest extends TestCase { - byte[] FAKE_BYTES_V1 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - byte[] FAKE_BYTES_V2 = {1, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - - byte[] FAKE_WOVEN_BYTES_V1 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10}; - byte[] FAKE_WOVEN_BYTES_V2 = {1, 1, 2, 3, 4, 5, 6, 7, 8, 9,10}; - - - private SimpleCache createCache() throws Exception { - return new SimpleCache(System.getProperty("java.io.tmpdir"),true); - } - - - public void testCache() throws Exception { - String classA = "com.generated.A"; - SimpleCache cache = createCache(); - - cache.put(classA, FAKE_BYTES_V1, FAKE_WOVEN_BYTES_V1); - - - // Test the returned woven bytes are the original one - byte result[] = cache.getAndInitialize(classA, FAKE_BYTES_V1, null, null); - for(int i = 0; i < result.length; i ++){ - assertEquals("Cached version byte[" +i+"] should be equal to the original woven classe",result[i],FAKE_WOVEN_BYTES_V1[i]); - } - - // Assure the class is properly backed up in the backing folder - File f = new File (System.getProperty("java.io.tmpdir") + File.separator + "com.generated.A-1164760902"); - assertTrue("Class should be backed up to backing folder, with te CRC:1164760902 ",f.exists()); - - } - - public void testDifferentVersionCache() throws Exception { - String classA = "com.generated.A"; - SimpleCache cache = createCache(); - cache.put(classA, FAKE_BYTES_V1, FAKE_WOVEN_BYTES_V1); - cache.put(classA, FAKE_BYTES_V2, FAKE_WOVEN_BYTES_V2); - - // Test the returned woven bytes are the original one for v1 - byte result[] = cache.getAndInitialize(classA, FAKE_BYTES_V1, null, null); - for(int i = 0; i < result.length; i ++){ - assertEquals("Cached version v1 byte[" +i+"] should be equal to the original woven classe",result[i],FAKE_WOVEN_BYTES_V1[i]); - } - - // Test the returned woven bytes are the original one for v2 - result = cache.getAndInitialize(classA, FAKE_BYTES_V2, null, null); - for(int i = 0; i < result.length; i ++){ - assertEquals("Cached version v2 byte[" +i+"] should be equal to the original woven classe",result[i],FAKE_WOVEN_BYTES_V2[i]); - } - } -} \ No newline at end of file diff --git a/weaver/testsrc/org/aspectj/weaver/tools/cache/WeavedClassCacheTest.java b/weaver/testsrc/org/aspectj/weaver/tools/cache/WeavedClassCacheTest.java deleted file mode 100644 index a02400eb8..000000000 --- a/weaver/testsrc/org/aspectj/weaver/tools/cache/WeavedClassCacheTest.java +++ /dev/null @@ -1,152 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * John Kew (vmware) initial implementation - *******************************************************************************/ - -package org.aspectj.weaver.tools.cache; - -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - -import org.aspectj.bridge.AbortException; -import org.aspectj.bridge.IMessage; -import org.aspectj.bridge.IMessageHandler; -import org.aspectj.weaver.tools.GeneratedClassHandler; - -/** - */ -public class WeavedClassCacheTest extends AbstractCacheBackingTestSupport { - String FAKE_CLASS = "com.example.foo.Bar"; - byte[] FAKE_BYTES = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - - public WeavedClassCacheTest () { - super(); - } - - public class MemoryCacheBacking implements CacheBacking { - HashMap cache = new HashMap(); - - public String[] getKeys(String regex) { - Set keys = cache.keySet(); - List matches = new LinkedList(); - for (String key : keys) { - if (key.matches(regex)) { - matches.add(key); - } - } - return matches.toArray(new String[0]); - } - - public void remove(CachedClassReference ref) { - cache.remove(ref.getKey()); - } - - public void clear() { - cache.clear(); - } - - public CachedClassEntry get(CachedClassReference ref, byte[] originalBytes) { - return cache.get(ref.getKey()); - } - - public void put(CachedClassEntry entry, byte[] originalBytes) { - assertNotNull("put(" + entry + ") no original bytes", originalBytes); - cache.put(entry.getKey(), entry); - } - } - - MemoryCacheBacking memoryBacking = new MemoryCacheBacking(); - - IMessageHandler messageHandler = new IMessageHandler() { - public boolean handleMessage(IMessage message) throws AbortException { - return true; - } - - public boolean isIgnoring(IMessage.Kind kind) { - return true; - } - - public void dontIgnore(IMessage.Kind kind) { - // do nothing - } - - public void ignore(IMessage.Kind kind) { - // do nothing - } - }; - - public class TestGeneratedClassHandler implements GeneratedClassHandler { - public int accepts = 0; - public List classesISaw = new LinkedList(); - - public void acceptClass (String name, byte[] originalBytes, byte[] wovenBytes) { - accepts++; - classesISaw.add(name); - } - } - - TestGeneratedClassHandler generatedClassHandler = new TestGeneratedClassHandler(); - - CacheKeyResolver resolver = new DefaultCacheKeyResolver(); - - private WeavedClassCache createCache() throws Exception { - return new WeavedClassCache(generatedClassHandler, messageHandler, "test", memoryBacking, resolver); - } - - private void reset() throws Exception { - memoryBacking.cache.clear(); - generatedClassHandler.accepts = 0; - generatedClassHandler.classesISaw.clear(); - } - - public void testGetCachingClassHandler() throws Exception { - WeavedClassCache cache = createCache(); - GeneratedClassHandler newHandle = cache.getCachingClassHandler(); - assertTrue(generatedClassHandler != newHandle); - assertTrue(newHandle instanceof GeneratedCachedClassHandler); - } - - public void testCache() throws Exception { - reset(); - WeavedClassCache cache = createCache(); - CacheStatistics stats = cache.getStats(); - CachedClassReference ref = cache.createCacheKey(FAKE_CLASS, FAKE_BYTES); - assertNull(cache.get(ref, FAKE_BYTES)); - cache.put(ref, FAKE_BYTES, FAKE_BYTES); - assertNotNull(cache.get(ref, FAKE_BYTES)); - - assertEquals(new String(FAKE_BYTES), new String(cache.get(ref, FAKE_BYTES).getBytes())); - - ref = cache.createGeneratedCacheKey(FAKE_CLASS); - assertNull(cache.get(ref, FAKE_BYTES)); - cache.put(ref, FAKE_BYTES, FAKE_BYTES); - assertNotNull(cache.get(ref, FAKE_BYTES)); - assertEquals(new String(FAKE_BYTES), new String(cache.get(ref, FAKE_BYTES).getBytes())); - - assertEquals(4, stats.getHits()); - assertEquals(2, stats.getMisses()); - - - } - - public void testRemove() throws Exception { - reset(); - WeavedClassCache cache = createCache(); - CachedClassReference ref = cache.createCacheKey(FAKE_CLASS, FAKE_BYTES); - assertNull(cache.get(ref, FAKE_BYTES)); - cache.put(ref, FAKE_BYTES, FAKE_BYTES); - assertNotNull(cache.get(ref, FAKE_BYTES)); - cache.remove(ref); - assertNull(cache.get(ref, FAKE_BYTES)); - } - -} diff --git a/weaver/testsrc/org/aspectj/weaver/tools/cache/ZippedFileCacheBackingTest.java b/weaver/testsrc/org/aspectj/weaver/tools/cache/ZippedFileCacheBackingTest.java deleted file mode 100644 index 4c41c1807..000000000 --- a/weaver/testsrc/org/aspectj/weaver/tools/cache/ZippedFileCacheBackingTest.java +++ /dev/null @@ -1,154 +0,0 @@ -/** - * Copyright (c) 2012 VMware, Inc. - * - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Lyor Goldstein - */ - -package org.aspectj.weaver.tools.cache; - -import java.io.File; -import java.io.IOException; -import java.util.Map; -import java.util.TreeMap; - -import org.aspectj.util.FileUtil; -import org.aspectj.weaver.tools.cache.AbstractIndexedFileCacheBacking.IndexEntry; - -/** - * - */ -public class ZippedFileCacheBackingTest extends AsynchronousFileCacheBackingTestSupport { - private File zipTestFile; - - public ZippedFileCacheBackingTest() { - super(); - } - - public void testReadIndex () throws Exception { - IndexEntry[] entries={ - createIgnoredEntry("ignored"), - createIndexEntry("weaved", false, false, bytes, bytes), - createIndexEntry("generated", true, false, bytes, bytes) - }; - File indexFile=getIndexFile(); - writeIndex(indexFile, entries); - - Map entriesMap=new TreeMap(); - for (IndexEntry ie : entries) { - if (ie.ignored) { - continue; - } - - entriesMap.put(ie.key, bytes); - } - - File zipFile=getZipFile(); - ZippedFileCacheBacking.writeZipClassBytes(zipFile, entriesMap); - - File cacheDir=getCacheDir(); - AsynchronousFileCacheBacking cache=createFileBacking(cacheDir); - Map indexMap=cache.getIndexMap(); - assertEquals("Mismatched index size", entries.length, indexMap.size()); - - Map bytesMap=cache.getBytesMap(); - assertEquals("Mismatched bytes size", entriesMap.size() /* the ignored one has no file */, bytesMap.size()); - - for (IndexEntry entry : entries) { - String key=entry.key; - assertNotNull("Missing entry for key=" + key, indexMap.get(key)); - - if (entry.ignored) { - assertNull("Unexpected bytes for ignored key=" + key, bytesMap.get(key)); - } else { - assertArrayEquals("Mismatched contents for key=" + key, bytes, bytesMap.get(key)); - } - } - } - - public void testReadWriteZipClassBytes () throws IOException { - Map entriesMap=new TreeMap(); - for (int index=0; index < Byte.SIZE; index++) { - String name="classBytes#" + index; - random.nextBytes(bytes); - entriesMap.put(name, bytes); - } - - File zipFile=getZipFile(); - ZippedFileCacheBacking.writeZipClassBytes(zipFile, entriesMap); - - Map bytesMap=ZippedFileCacheBacking.readZipClassBytes(zipFile); - assertEquals("Mismatched recovered entries size", entriesMap.size(), bytesMap.size()); - for (Map.Entry bytesEntry : entriesMap.entrySet()) { - String key=bytesEntry.getKey(); - byte[] expected=bytesEntry.getValue(), actual=bytesMap.get(key); - assertArrayEquals("Mismatched data for " + key, expected, actual); - } - } - - public void testReadClassBytes () throws IOException { - IndexEntry[] entries={ - createIgnoredEntry("ignoredReadClassBytes"), - createIndexEntry("weavedReadClassBytes", false, false, bytes, bytes), - createIndexEntry("generatedReadClassBytes", true, false, bytes, bytes) - }; - File indexFile=getIndexFile(); - writeIndex(indexFile, entries); - - long newCrc=generateNewBytes(); - assertTrue("Bad new CRC", newCrc != (-1L)); - - Map entriesMap=new TreeMap(); - for (IndexEntry ie : entries) { - if (ie.ignored) { - continue; - } - - entriesMap.put(ie.key, bytes); - } - - File zipFile=getZipFile(); - ZippedFileCacheBacking.writeZipClassBytes(zipFile, entriesMap); - - File cacheDir=getCacheDir(); - AsynchronousFileCacheBacking cache=createFileBacking(cacheDir); - Map indexMap=cache.getIndexMap(); - assertEquals("Mismatched index size", 1 /* only the ignored entry */, indexMap.size()); - - Map bytesMap=cache.getBytesMap(); - assertEquals("Non empty data bytes", 0, bytesMap.size()); - assertFalse("Zip file not deleted: " + zipFile, zipFile.canRead()); - } - - protected File getZipFile () { - if (zipTestFile == null) { - File cacheDir=getCacheDir(); - zipTestFile = new File(cacheDir, ZippedFileCacheBacking.ZIP_FILE); - } - - return zipTestFile; - } - - @Override - protected void cleanupCache() { - if (zipTestFile != null) { - if (FileUtil.deleteContents(zipTestFile) > 0) { - System.out.println("Deleted " + zipTestFile); - } - zipTestFile = null; - } - - super.cleanupCache(); - } - - @Override - protected ZippedFileCacheBacking createFileBacking(File dir) { - return new ZippedFileCacheBacking(dir); - } -} \ No newline at end of file diff --git a/weaver/testsrc/reflect/tests/C.java b/weaver/testsrc/reflect/tests/C.java deleted file mode 100644 index f52043b5a..000000000 --- a/weaver/testsrc/reflect/tests/C.java +++ /dev/null @@ -1,33 +0,0 @@ -/* ******************************************************************* - * Copyright (c) 2005 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution and is available at - * http://eclipse.org/legal/epl-v10.html - * - * Contributors: - * Adrian Colyer Initial implementation - * ******************************************************************/ -package reflect.tests; - -/** - * @author colyer - * Part of the testdata for the org.aspectj.weaver.reflect tests - */ -public class C { - - public String foo(Object a) throws Exception { - return null; - } - - private void bar() {} - - public int f; - private String s; -} - -class D extends C implements java.io.Serializable { - public int getNumberOfThingies() { return 0; } - private Object o; -} \ No newline at end of file -- cgit v1.2.3