From eb5b2857783d3060a5a3188a3811f5cbb8f81aa5 Mon Sep 17 00:00:00 2001 From: aclement Date: Wed, 22 Oct 2008 05:45:52 +0000 Subject: [PATCH] 246125: the SPLIT --- .../aspectj/weaver/AbstractAnnotationAJ.java | 158 ++ .../weaver/AbstractReferenceTypeDelegate.java | 128 + .../src/org/aspectj/weaver/Advice.java | 444 ++++ .../src/org/aspectj/weaver/AdviceKind.java | 117 + .../src/org/aspectj/weaver/AjAttribute.java | 699 ++++++ .../org/aspectj/weaver/AjcMemberMaker.java | 621 +++++ .../org/aspectj/weaver/AnnotatedElement.java | 21 + .../src/org/aspectj/weaver/AnnotationAJ.java | 102 + .../weaver/AnnotationAnnotationValue.java | 35 + .../weaver/AnnotationNameValuePair.java | 47 + .../weaver/AnnotationOnTypeMunger.java | 57 + .../aspectj/weaver/AnnotationTargetKind.java | 51 + .../org/aspectj/weaver/AnnotationValue.java | 72 + .../aspectj/weaver/ArrayAnnotationValue.java | 57 + .../aspectj/weaver/ArrayReferenceType.java | 196 ++ .../src/org/aspectj/weaver/BCException.java | 58 + .../src/org/aspectj/weaver/BindingScope.java | 58 + .../aspectj/weaver/BoundedReferenceType.java | 334 +++ .../src/org/aspectj/weaver/Checker.java | 151 ++ .../aspectj/weaver/ClassAnnotationValue.java | 31 + .../aspectj/weaver/ConcreteTypeMunger.java | 144 ++ .../src/org/aspectj/weaver/Constants.java | 30 + .../aspectj/weaver/CrosscuttingMembers.java | 533 ++++ .../weaver/CrosscuttingMembersSet.java | 305 +++ .../aspectj/weaver/CustomMungerFactory.java | 58 + .../src/org/aspectj/weaver/Dump.java | 506 ++++ .../aspectj/weaver/EnumAnnotationValue.java | 37 + .../org/aspectj/weaver/ExposeTypeMunger.java | 30 + .../GeneratedReferenceTypeDelegate.java | 158 ++ .../aspectj/weaver/IClassFileProvider.java | 43 + .../src/org/aspectj/weaver/IClassWeaver.java | 29 + .../weaver/ICrossReferenceHandler.java | 23 + .../aspectj/weaver/IEclipseSourceContext.java | 16 + .../src/org/aspectj/weaver/IHasPosition.java | 33 + .../aspectj/weaver/IHasSourceLocation.java | 21 + .../org/aspectj/weaver/ISourceContext.java | 23 + .../org/aspectj/weaver/IUnwovenClassFile.java | 26 + .../org/aspectj/weaver/IWeaveRequestor.java | 38 + .../org/aspectj/weaver/IWeavingSupport.java | 44 + .../src/org/aspectj/weaver/IntMap.java | 147 ++ .../src/org/aspectj/weaver/Iterators.java | 264 ++ .../aspectj/weaver/JoinPointSignature.java | 409 +++ .../weaver/JoinPointSignatureIterator.java | 251 ++ .../src/org/aspectj/weaver/Lint.java | 315 +++ .../src/org/aspectj/weaver/LintMessage.java | 47 + .../src/org/aspectj/weaver/Member.java | 102 + .../src/org/aspectj/weaver/MemberImpl.java | 565 +++++ .../src/org/aspectj/weaver/MemberKind.java | 37 + .../weaver/MethodDelegateTypeMunger.java | 202 ++ ...MissingResolvedTypeWithKnownSignature.java | 212 ++ .../src/org/aspectj/weaver/NameMangler.java | 334 +++ .../weaver/NewConstructorTypeMunger.java | 144 ++ .../aspectj/weaver/NewFieldTypeMunger.java | 124 + .../aspectj/weaver/NewMethodTypeMunger.java | 146 ++ .../aspectj/weaver/NewParentTypeMunger.java | 51 + .../weaver/PerObjectInterfaceTypeMunger.java | 91 + .../weaver/PerTypeWithinTargetTypeMunger.java | 81 + .../aspectj/weaver/PoliceExtensionUse.java | 80 + .../src/org/aspectj/weaver/Position.java | 32 + .../weaver/PrivilegedAccessMunger.java | 69 + .../src/org/aspectj/weaver/ReferenceType.java | 810 ++++++ .../aspectj/weaver/ReferenceTypeDelegate.java | 105 + .../org/aspectj/weaver/ResolvedMember.java | 191 ++ .../aspectj/weaver/ResolvedMemberImpl.java | 1156 +++++++++ .../weaver/ResolvedPointcutDefinition.java | 165 ++ .../src/org/aspectj/weaver/ResolvedType.java | 2201 +++++++++++++++++ .../aspectj/weaver/ResolvedTypeMunger.java | 446 ++++ .../src/org/aspectj/weaver/Shadow.java | 666 +++++ .../src/org/aspectj/weaver/ShadowMunger.java | 191 ++ .../org/aspectj/weaver/SignatureUtils.java | 246 ++ .../aspectj/weaver/SimpleAnnotationValue.java | 82 + .../org/aspectj/weaver/SourceContextImpl.java | 109 + .../aspectj/weaver/StandardAnnotation.java | 153 ++ .../weaver/StaticJoinPointFactory.java | 56 + .../aspectj/weaver/TemporaryTypeMunger.java | 35 + .../src/org/aspectj/weaver/TypeFactory.java | 293 +++ .../src/org/aspectj/weaver/TypeVariable.java | 372 +++ .../weaver/TypeVariableDeclaringElement.java | 23 + .../aspectj/weaver/TypeVariableReference.java | 22 + .../weaver/TypeVariableReferenceType.java | 146 ++ .../org/aspectj/weaver/UnresolvedType.java | 914 +++++++ .../UnresolvedTypeVariableReferenceType.java | 102 + .../src/org/aspectj/weaver/Utils.java | 43 + .../weaver/VersionedDataInputStream.java | 32 + .../weaver/WeakClassLoaderReference.java | 63 + .../org/aspectj/weaver/WeaverMessages.java | 210 ++ .../org/aspectj/weaver/WeaverStateInfo.java | 476 ++++ .../weaver/WildcardedUnresolvedType.java | 69 + .../src/org/aspectj/weaver/World.java | 1286 ++++++++++ .../aspectj/weaver/XlintDefault.properties | 47 + .../src/org/aspectj/weaver/ast/ASTNode.java | 22 + .../src/org/aspectj/weaver/ast/And.java | 51 + .../src/org/aspectj/weaver/ast/Call.java | 41 + .../src/org/aspectj/weaver/ast/CallExpr.java | 48 + .../src/org/aspectj/weaver/ast/Expr.java | 34 + .../src/org/aspectj/weaver/ast/FieldGet.java | 44 + .../org/aspectj/weaver/ast/FieldGetCall.java | 47 + .../org/aspectj/weaver/ast/HasAnnotation.java | 59 + .../org/aspectj/weaver/ast/IExprVisitor.java | 23 + .../org/aspectj/weaver/ast/ITestVisitor.java | 31 + .../org/aspectj/weaver/ast/Instanceof.java | 56 + .../src/org/aspectj/weaver/ast/Literal.java | 39 + .../src/org/aspectj/weaver/ast/Not.java | 49 + .../src/org/aspectj/weaver/ast/Or.java | 56 + .../src/org/aspectj/weaver/ast/Test.java | 95 + .../src/org/aspectj/weaver/ast/Var.java | 47 + .../tools/MatchingContextBasedTest.java | 43 + ...ointcutDesignatorHandlerBasedPointcut.java | 157 ++ .../tools/PointcutExpressionImpl.java | 343 +++ .../tools/TypePatternMatcherImpl.java | 34 + .../patterns/AbstractPatternNodeVisitor.java | 467 ++++ .../patterns/AndAnnotationTypePattern.java | 127 + .../aspectj/weaver/patterns/AndPointcut.java | 129 + .../weaver/patterns/AndTypePattern.java | 176 ++ .../patterns/AnnotationPatternList.java | 203 ++ .../weaver/patterns/AnnotationPointcut.java | 318 +++ .../patterns/AnnotationTypePattern.java | 130 + .../patterns/AnyAnnotationTypePattern.java | 55 + .../patterns/ArgsAnnotationPointcut.java | 245 ++ .../aspectj/weaver/patterns/ArgsPointcut.java | 307 +++ .../aspectj/weaver/patterns/BasicToken.java | 75 + .../weaver/patterns/BasicTokenSource.java | 183 ++ .../BindingAnnotationFieldTypePattern.java | 171 ++ .../BindingAnnotationTypePattern.java | 134 + .../weaver/patterns/BindingPattern.java | 19 + .../weaver/patterns/BindingTypePattern.java | 97 + .../org/aspectj/weaver/patterns/Bindings.java | 136 + .../weaver/patterns/CflowPointcut.java | 353 +++ .../patterns/ConcreteCflowPointcut.java | 184 ++ .../org/aspectj/weaver/patterns/Declare.java | 87 + .../weaver/patterns/DeclareAnnotation.java | 408 +++ .../patterns/DeclareErrorOrWarning.java | 129 + .../weaver/patterns/DeclareParents.java | 352 +++ .../weaver/patterns/DeclarePrecedence.java | 166 ++ .../aspectj/weaver/patterns/DeclareSoft.java | 137 + .../ExactAnnotationFieldTypePattern.java | 206 ++ .../patterns/ExactAnnotationTypePattern.java | 469 ++++ .../weaver/patterns/ExactTypePattern.java | 268 ++ .../aspectj/weaver/patterns/ExposedState.java | 98 + .../weaver/patterns/FastMatchInfo.java | 47 + .../weaver/patterns/FormalBinding.java | 82 + .../weaver/patterns/HandlerPointcut.java | 139 ++ .../weaver/patterns/HasMemberTypePattern.java | 160 ++ .../patterns/HasMemberTypePatternFinder.java | 33 + ...pePatternMatchingStuffAnywhereVisitor.java | 50 + .../org/aspectj/weaver/patterns/IScope.java | 51 + .../org/aspectj/weaver/patterns/IToken.java | 53 + .../aspectj/weaver/patterns/ITokenSource.java | 26 + .../patterns/IVerificationRequired.java | 22 + .../aspectj/weaver/patterns/IfPointcut.java | 506 ++++ .../weaver/patterns/KindedPointcut.java | 393 +++ .../weaver/patterns/ModifiersPattern.java | 99 + .../weaver/patterns/NameBindingPointcut.java | 52 + .../aspectj/weaver/patterns/NamePattern.java | 171 ++ .../patterns/NotAnnotationTypePattern.java | 120 + .../aspectj/weaver/patterns/NotPointcut.java | 123 + .../weaver/patterns/NotTypePattern.java | 154 ++ .../patterns/OrAnnotationTypePattern.java | 123 + .../aspectj/weaver/patterns/OrPointcut.java | 132 + .../weaver/patterns/OrTypePattern.java | 184 ++ .../weaver/patterns/ParserException.java | 31 + .../aspectj/weaver/patterns/PatternNode.java | 89 + .../weaver/patterns/PatternNodeVisitor.java | 95 + .../weaver/patterns/PatternParser.java | 1585 ++++++++++++ .../org/aspectj/weaver/patterns/PerCflow.java | 171 ++ .../aspectj/weaver/patterns/PerClause.java | 91 + .../aspectj/weaver/patterns/PerFromSuper.java | 135 + .../aspectj/weaver/patterns/PerObject.java | 187 ++ .../aspectj/weaver/patterns/PerSingleton.java | 173 ++ .../PerThisOrTargetPointcutVisitor.java | 216 ++ .../weaver/patterns/PerTypeWithin.java | 230 ++ .../org/aspectj/weaver/patterns/Pointcut.java | 396 +++ .../PointcutEvaluationExpenseComparator.java | 114 + .../weaver/patterns/PointcutRewriter.java | 440 ++++ .../weaver/patterns/ReferencePointcut.java | 407 +++ .../patterns/ScopeWithTypeVariables.java | 130 + .../weaver/patterns/SignaturePattern.java | 754 ++++++ .../aspectj/weaver/patterns/SimpleScope.java | 151 ++ .../ThisOrTargetAnnotationPointcut.java | 287 +++ .../weaver/patterns/ThisOrTargetPointcut.java | 220 ++ .../weaver/patterns/ThrowsPattern.java | 142 ++ .../aspectj/weaver/patterns/TypePattern.java | 624 +++++ .../weaver/patterns/TypePatternList.java | 333 +++ .../weaver/patterns/TypePatternQuestions.java | 107 + .../weaver/patterns/TypeVariablePattern.java | 209 ++ .../patterns/TypeVariablePatternList.java | 85 + .../patterns/WildAnnotationTypePattern.java | 369 +++ .../weaver/patterns/WildTypePattern.java | 1289 ++++++++++ .../patterns/WithinAnnotationPointcut.java | 217 ++ .../WithinCodeAnnotationPointcut.java | 219 ++ .../weaver/patterns/WithinPointcut.java | 152 ++ .../weaver/patterns/WithincodePointcut.java | 142 ++ .../weaver/reflect/AnnotationFinder.java | 45 + .../GenericSignatureInformationProvider.java | 31 + .../weaver/reflect/IReflectionWorld.java | 19 + ...14GenericSignatureInformationProvider.java | 60 + .../weaver/reflect/JoinPointMatchImpl.java | 53 + .../weaver/reflect/PointcutParameterImpl.java | 43 + .../ReflectionBasedReferenceTypeDelegate.java | 442 ++++ ...tionBasedReferenceTypeDelegateFactory.java | 218 ++ .../ReflectionBasedResolvedMemberImpl.java | 216 ++ .../reflect/ReflectionFastMatchInfo.java | 42 + .../weaver/reflect/ReflectionShadow.java | 345 +++ .../aspectj/weaver/reflect/ReflectionVar.java | 169 ++ .../weaver/reflect/ReflectionWorld.java | 140 ++ .../weaver/reflect/ShadowMatchImpl.java | 189 ++ .../aspectj/weaver/tools/AbstractTrace.java | 192 ++ .../aspectj/weaver/tools/CommonsTrace.java | 106 + .../weaver/tools/CommonsTraceFactory.java | 23 + .../weaver/tools/ContextBasedMatcher.java | 65 + .../weaver/tools/DefaultMatchingContext.java | 56 + .../aspectj/weaver/tools/DefaultTrace.java | 120 + .../weaver/tools/DefaultTraceFactory.java | 50 + .../aspectj/weaver/tools/FuzzyBoolean.java | 37 + .../weaver/tools/GeneratedClassHandler.java | 29 + .../weaver/tools/ISupportsMessageContext.java | 18 + .../aspectj/weaver/tools/JoinPointMatch.java | 31 + .../aspectj/weaver/tools/MatchingContext.java | 43 + .../tools/PointcutDesignatorHandler.java | 49 + .../weaver/tools/PointcutExpression.java | 204 ++ .../weaver/tools/PointcutParameter.java | 36 + .../aspectj/weaver/tools/PointcutParser.java | 546 ++++ .../weaver/tools/PointcutPrimitive.java | 50 + .../org/aspectj/weaver/tools/ShadowMatch.java | 58 + .../src/org/aspectj/weaver/tools/Trace.java | 62 + .../aspectj/weaver/tools/TraceFactory.java | 83 + .../org/aspectj/weaver/tools/Traceable.java | 17 + .../weaver/tools/TypePatternMatcher.java | 23 + ...UnsupportedPointcutPrimitiveException.java | 44 + .../weaver/tools/WeavingClassLoader.java | 31 + .../aspectj/weaver/weaver-messages.properties | 199 ++ 231 files changed, 43789 insertions(+) create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/AbstractAnnotationAJ.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/AbstractReferenceTypeDelegate.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/Advice.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/AdviceKind.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/AjAttribute.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/AjcMemberMaker.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/AnnotatedElement.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/AnnotationAJ.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/AnnotationAnnotationValue.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/AnnotationNameValuePair.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/AnnotationOnTypeMunger.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/AnnotationTargetKind.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/AnnotationValue.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/ArrayAnnotationValue.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/ArrayReferenceType.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/BCException.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/BindingScope.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/BoundedReferenceType.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/Checker.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/ClassAnnotationValue.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/ConcreteTypeMunger.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/Constants.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/CrosscuttingMembers.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/CrosscuttingMembersSet.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/CustomMungerFactory.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/Dump.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/EnumAnnotationValue.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/ExposeTypeMunger.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/GeneratedReferenceTypeDelegate.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/IClassFileProvider.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/IClassWeaver.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/ICrossReferenceHandler.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/IEclipseSourceContext.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/IHasPosition.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/IHasSourceLocation.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/ISourceContext.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/IUnwovenClassFile.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/IWeaveRequestor.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/IWeavingSupport.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/IntMap.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/Iterators.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/JoinPointSignature.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/JoinPointSignatureIterator.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/Lint.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/LintMessage.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/Member.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/MemberImpl.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/MemberKind.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/MethodDelegateTypeMunger.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/MissingResolvedTypeWithKnownSignature.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/NameMangler.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/NewConstructorTypeMunger.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/NewFieldTypeMunger.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/NewMethodTypeMunger.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/NewParentTypeMunger.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/PerObjectInterfaceTypeMunger.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/PerTypeWithinTargetTypeMunger.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/PoliceExtensionUse.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/Position.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/PrivilegedAccessMunger.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/ReferenceType.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/ReferenceTypeDelegate.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/ResolvedMember.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/ResolvedMemberImpl.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/ResolvedPointcutDefinition.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/ResolvedType.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/ResolvedTypeMunger.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/Shadow.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/ShadowMunger.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/SignatureUtils.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/SimpleAnnotationValue.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/SourceContextImpl.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/StandardAnnotation.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/StaticJoinPointFactory.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/TemporaryTypeMunger.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/TypeFactory.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/TypeVariable.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/TypeVariableDeclaringElement.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/TypeVariableReference.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/TypeVariableReferenceType.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/UnresolvedType.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/UnresolvedTypeVariableReferenceType.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/Utils.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/VersionedDataInputStream.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/WeakClassLoaderReference.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/WeaverMessages.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/WeaverStateInfo.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/WildcardedUnresolvedType.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/World.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/XlintDefault.properties create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/ast/ASTNode.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/ast/And.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/ast/Call.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/ast/CallExpr.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/ast/Expr.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/ast/FieldGet.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/ast/FieldGetCall.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/ast/HasAnnotation.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/ast/IExprVisitor.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/ast/ITestVisitor.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/ast/Instanceof.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/ast/Literal.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/ast/Not.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/ast/Or.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/ast/Test.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/ast/Var.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/internal/tools/MatchingContextBasedTest.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/internal/tools/PointcutDesignatorHandlerBasedPointcut.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/internal/tools/PointcutExpressionImpl.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/internal/tools/TypePatternMatcherImpl.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/AbstractPatternNodeVisitor.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/AndAnnotationTypePattern.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/AndPointcut.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/AndTypePattern.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/AnnotationPatternList.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/AnnotationPointcut.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/AnnotationTypePattern.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/AnyAnnotationTypePattern.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/ArgsAnnotationPointcut.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/ArgsPointcut.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/BasicToken.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/BasicTokenSource.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/BindingAnnotationFieldTypePattern.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/BindingAnnotationTypePattern.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/BindingPattern.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/BindingTypePattern.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/Bindings.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/CflowPointcut.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/ConcreteCflowPointcut.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/Declare.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/DeclareAnnotation.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/DeclareErrorOrWarning.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/DeclareParents.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/DeclarePrecedence.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/DeclareSoft.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/ExactAnnotationFieldTypePattern.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/ExactAnnotationTypePattern.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/ExactTypePattern.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/ExposedState.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/FastMatchInfo.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/FormalBinding.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/HandlerPointcut.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/HasMemberTypePattern.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/HasMemberTypePatternFinder.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/IScope.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/IToken.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/ITokenSource.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/IVerificationRequired.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/IfPointcut.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/KindedPointcut.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/ModifiersPattern.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/NameBindingPointcut.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/NamePattern.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/NotAnnotationTypePattern.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/NotPointcut.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/NotTypePattern.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/OrAnnotationTypePattern.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/OrPointcut.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/OrTypePattern.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/ParserException.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/PatternNode.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/PatternNodeVisitor.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/PatternParser.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/PerCflow.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/PerClause.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/PerFromSuper.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/PerObject.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/PerSingleton.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/PerThisOrTargetPointcutVisitor.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/PerTypeWithin.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/Pointcut.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/PointcutEvaluationExpenseComparator.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/PointcutRewriter.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/ReferencePointcut.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/ScopeWithTypeVariables.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/SignaturePattern.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/SimpleScope.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/ThisOrTargetAnnotationPointcut.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/ThisOrTargetPointcut.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/ThrowsPattern.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/TypePattern.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/TypePatternList.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/TypePatternQuestions.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/TypeVariablePattern.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/TypeVariablePatternList.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/WildAnnotationTypePattern.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/WildTypePattern.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/WithinAnnotationPointcut.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/WithinCodeAnnotationPointcut.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/WithinPointcut.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/patterns/WithincodePointcut.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/reflect/AnnotationFinder.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/reflect/GenericSignatureInformationProvider.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/reflect/IReflectionWorld.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/reflect/Java14GenericSignatureInformationProvider.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/reflect/JoinPointMatchImpl.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/reflect/PointcutParameterImpl.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/reflect/ReflectionBasedReferenceTypeDelegate.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/reflect/ReflectionBasedReferenceTypeDelegateFactory.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/reflect/ReflectionBasedResolvedMemberImpl.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/reflect/ReflectionFastMatchInfo.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/reflect/ReflectionShadow.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/reflect/ReflectionVar.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/reflect/ReflectionWorld.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/reflect/ShadowMatchImpl.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/tools/AbstractTrace.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/tools/CommonsTrace.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/tools/CommonsTraceFactory.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/tools/ContextBasedMatcher.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/tools/DefaultMatchingContext.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/tools/DefaultTrace.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/tools/DefaultTraceFactory.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/tools/FuzzyBoolean.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/tools/GeneratedClassHandler.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/tools/ISupportsMessageContext.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/tools/JoinPointMatch.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/tools/MatchingContext.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/tools/PointcutDesignatorHandler.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/tools/PointcutExpression.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/tools/PointcutParameter.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/tools/PointcutParser.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/tools/PointcutPrimitive.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/tools/ShadowMatch.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/tools/Trace.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/tools/TraceFactory.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/tools/Traceable.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/tools/TypePatternMatcher.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/tools/UnsupportedPointcutPrimitiveException.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/tools/WeavingClassLoader.java create mode 100644 org.aspectj.matcher/src/org/aspectj/weaver/weaver-messages.properties diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/AbstractAnnotationAJ.java b/org.aspectj.matcher/src/org/aspectj/weaver/AbstractAnnotationAJ.java new file mode 100644 index 000000000..f9922cf97 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/AbstractAnnotationAJ.java @@ -0,0 +1,158 @@ +/* ******************************************************************* + * 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; + +import java.util.Collections; +import java.util.Iterator; +import java.util.Set; + +public abstract class AbstractAnnotationAJ implements AnnotationAJ { + + protected final ResolvedType type; + + private Set supportedTargets = null; // @target meta annotation + + public AbstractAnnotationAJ(ResolvedType type) { + this.type = type; + } + + /** + * {@inheritDoc} + */ + public final ResolvedType getType() { + return type; + } + + /** + * {@inheritDoc} + */ + public final String getTypeSignature() { + return type.getSignature(); + } + + /** + * {@inheritDoc} + */ + public final String getTypeName() { + return type.getName(); + } + + /** + * {@inheritDoc} + */ + public final boolean allowedOnAnnotationType() { + ensureAtTargetInitialized(); + if (supportedTargets.isEmpty()) { + return true; + } + return supportedTargets.contains("ANNOTATION_TYPE"); + } + + /** + * {@inheritDoc} + */ + public final boolean allowedOnField() { + ensureAtTargetInitialized(); + if (supportedTargets.isEmpty()) { + return true; + } + return supportedTargets.contains("FIELD"); + } + + /** + * {@inheritDoc} + */ + public final boolean allowedOnRegularType() { + ensureAtTargetInitialized(); + if (supportedTargets.isEmpty()) { + return true; + } + return supportedTargets.contains("TYPE"); + } + + /** + * {@inheritDoc} + */ + public final void ensureAtTargetInitialized() { + if (supportedTargets == null) { + AnnotationAJ atTargetAnnotation = retrieveAnnotationOnAnnotation(UnresolvedType.AT_TARGET); + if (atTargetAnnotation == null) { + supportedTargets = Collections.EMPTY_SET; + } else { + supportedTargets = atTargetAnnotation.getTargets(); + } + } + } + + /** + * {@inheritDoc} + */ + public final String getValidTargets() { + StringBuffer sb = new StringBuffer(); + sb.append("{"); + for (Iterator iter = supportedTargets.iterator(); iter.hasNext();) { + String evalue = (String) iter.next(); + sb.append(evalue); + if (iter.hasNext()) { + sb.append(","); + } + } + sb.append("}"); + return sb.toString(); + } + + /** + * {@inheritDoc} + */ + public final boolean specifiesTarget() { + ensureAtTargetInitialized(); + return !supportedTargets.isEmpty(); + } + + /** + * Helper method to retrieve an annotation on an annotation e.g. retrieveAnnotationOnAnnotation(UnresolvedType.AT_TARGET) + */ + private final AnnotationAJ retrieveAnnotationOnAnnotation(UnresolvedType requiredAnnotationSignature) { + AnnotationAJ[] annos = type.getAnnotations(); + for (int i = 0; i < annos.length; i++) { + AnnotationAJ a = annos[i]; + if (a.getTypeSignature().equals(requiredAnnotationSignature.getSignature())) { + return annos[i]; + } + } + return null; + } + + /** + * {@inheritDoc} + */ + public abstract boolean isRuntimeVisible(); + + /** + * {@inheritDoc} + */ + public abstract Set getTargets(); + + /** + * {@inheritDoc} + */ + public abstract boolean hasNameValuePair(String name, String value); + + /** + * {@inheritDoc} + */ + public abstract boolean hasNamedValue(String name); + + /** + * {@inheritDoc} + */ + public abstract String stringify(); + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/AbstractReferenceTypeDelegate.java b/org.aspectj.matcher/src/org/aspectj/weaver/AbstractReferenceTypeDelegate.java new file mode 100644 index 000000000..52d0ade35 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/AbstractReferenceTypeDelegate.java @@ -0,0 +1,128 @@ +/* ******************************************************************* + * 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 + * Andy Clement - June 2005 - separated out from ResolvedType + * ******************************************************************/ +package org.aspectj.weaver; + +import java.util.ArrayList; +import java.util.List; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.util.GenericSignature; +import org.aspectj.util.GenericSignatureParser; +import org.aspectj.util.GenericSignature.ClassSignature; + +public abstract class AbstractReferenceTypeDelegate implements ReferenceTypeDelegate { + + private String sourcefilename = UNKNOWN_SOURCE_FILE; + private ISourceContext sourceContext = SourceContextImpl.UNKNOWN_SOURCE_CONTEXT; + + protected boolean exposedToWeaver; + protected ReferenceType resolvedTypeX; + protected ClassSignature cachedGenericClassTypeSignature; + + // Happens to match Bcel javaClass default of '' + public final static String UNKNOWN_SOURCE_FILE = ""; + + public AbstractReferenceTypeDelegate(ReferenceType resolvedTypeX, boolean exposedToWeaver) { + this.resolvedTypeX = resolvedTypeX; + this.exposedToWeaver = exposedToWeaver; + } + + public final boolean isClass() { + return !isAspect() && !isInterface(); + } + + /** + * Designed to be overriden by EclipseType to disable collection of shadow mungers during pre-weave compilation phase + */ + public boolean doesNotExposeShadowMungers() { + return false; + } + + public boolean isExposedToWeaver() { + return exposedToWeaver; + } + + public ReferenceType getResolvedTypeX() { + return resolvedTypeX; + } + + public final String getSourcefilename() { + return sourcefilename; + } + + public final void setSourcefilename(String sourceFileName) { + sourcefilename = sourceFileName; + if (sourceFileName != null && sourceFileName.equals(AbstractReferenceTypeDelegate.UNKNOWN_SOURCE_FILE)) { + sourcefilename = "Type '" + getResolvedTypeX().getName() + "' (no debug info available)"; + } else { + String pname = getResolvedTypeX().getPackageName(); + if (pname != null) { + sourcefilename = pname.replace('.', '/') + '/' + sourceFileName; + } + } + if (sourcefilename != null && sourceContext instanceof SourceContextImpl) { + ((SourceContextImpl) sourceContext).setSourceFileName(sourcefilename); + } + } + + public ISourceLocation getSourceLocation() { + return getSourceContext().makeSourceLocation(0, 0); + } + + public ISourceContext getSourceContext() { + return sourceContext; + } + + public void setSourceContext(ISourceContext isc) { + sourceContext = isc; + } + + public GenericSignature.ClassSignature getGenericClassTypeSignature() { + if (cachedGenericClassTypeSignature == null) { + String sig = getDeclaredGenericSignature(); + if (sig != null) { + GenericSignatureParser parser = new GenericSignatureParser(); + cachedGenericClassTypeSignature = parser.parseAsClassSignature(sig); + } + } + return cachedGenericClassTypeSignature; + } + + protected GenericSignature.FormalTypeParameter[] getFormalTypeParametersFromOuterClass() { + List typeParameters = new ArrayList(); + ReferenceType outer = (ReferenceType) getOuterClass(); + ReferenceTypeDelegate outerDelegate = outer.getDelegate(); + AbstractReferenceTypeDelegate outerObjectType = (AbstractReferenceTypeDelegate) outerDelegate; + if (outerObjectType.isNested()) { + GenericSignature.FormalTypeParameter[] parentParams = outerObjectType.getFormalTypeParametersFromOuterClass(); + for (int i = 0; i < parentParams.length; i++) { + typeParameters.add(parentParams[i]); + } + } + GenericSignature.ClassSignature outerSig = outerObjectType.getGenericClassTypeSignature(); + if (outerSig != null) { + for (int i = 0; i < outerSig.formalTypeParameters.length; i++) { + typeParameters.add(outerSig.formalTypeParameters[i]); + } + } + + GenericSignature.FormalTypeParameter[] ret = new GenericSignature.FormalTypeParameter[typeParameters.size()]; + typeParameters.toArray(ret); + return ret; + } + + public boolean copySourceContext() { + return true; + } + +} \ No newline at end of file diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/Advice.java b/org.aspectj.matcher/src/org/aspectj/weaver/Advice.java new file mode 100644 index 000000000..0472b5701 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/Advice.java @@ -0,0 +1,444 @@ +/* ******************************************************************* + * 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.util.Collections; +import java.util.List; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.weaver.patterns.AndPointcut; +import org.aspectj.weaver.patterns.PerClause; +import org.aspectj.weaver.patterns.Pointcut; +import org.aspectj.weaver.patterns.TypePattern; + +public abstract class Advice extends ShadowMunger { + + protected AjAttribute.AdviceAttribute attribute; // the pointcut field is + // ignored + + protected AdviceKind kind; // alias of attribute.getKind() + protected Member signature; + protected boolean hasMatchedAtLeastOnce = false; + + // not necessarily declaring aspect, this is a semantics change from 1.0 + protected ResolvedType concreteAspect; // null until after concretize + + protected List innerCflowEntries = Collections.EMPTY_LIST; // just for + // cflow*Entry + // kinds + protected int nFreeVars; // just for cflow*Entry kinds + + protected TypePattern exceptionType; // just for Softener kind + + // if we are parameterized, these type may be different to the advice + // signature types + protected UnresolvedType[] bindingParameterTypes; + + protected List/* Lint.Kind */suppressedLintKinds = null; // based on + // annotations on + // this advice + + public ISourceLocation lastReportedMonitorExitJoinpointLocation = null; + + public static Advice makeCflowEntry(World world, Pointcut entry, boolean isBelow, Member stackField, int nFreeVars, + List innerCflowEntries, ResolvedType inAspect) { + Advice ret = world.createAdviceMunger(isBelow ? AdviceKind.CflowBelowEntry : AdviceKind.CflowEntry, entry, stackField, 0, + entry); + // 0); + ret.innerCflowEntries = innerCflowEntries; + ret.nFreeVars = nFreeVars; + ret.concreteAspect = inAspect; + return ret; + } + + public static Advice makePerCflowEntry(World world, Pointcut entry, boolean isBelow, Member stackField, ResolvedType inAspect, + List innerCflowEntries) { + Advice ret = world.createAdviceMunger(isBelow ? AdviceKind.PerCflowBelowEntry : AdviceKind.PerCflowEntry, entry, + stackField, 0, entry); + ret.innerCflowEntries = innerCflowEntries; + ret.concreteAspect = inAspect; + return ret; + } + + public static Advice makePerObjectEntry(World world, Pointcut entry, boolean isThis, ResolvedType inAspect) { + Advice ret = world.createAdviceMunger(isThis ? AdviceKind.PerThisEntry : AdviceKind.PerTargetEntry, entry, null, 0, entry); + + ret.concreteAspect = inAspect; + return ret; + } + + // PTWIMPL per type within entry advice is what initializes the aspect + // instance in the matched type + public static Advice makePerTypeWithinEntry(World world, Pointcut p, ResolvedType inAspect) { + Advice ret = world.createAdviceMunger(AdviceKind.PerTypeWithinEntry, p, null, 0, p); + ret.concreteAspect = inAspect; + return ret; + } + + public static Advice makeSoftener(World world, Pointcut entry, TypePattern exceptionType, ResolvedType inAspect, + IHasSourceLocation loc) { + Advice ret = world.createAdviceMunger(AdviceKind.Softener, entry, null, 0, loc); + + ret.exceptionType = exceptionType; + ret.concreteAspect = inAspect; + // System.out.println("made ret: " + ret + " with " + exceptionType); + return ret; + } + + public Advice(AjAttribute.AdviceAttribute attribute, Pointcut pointcut, Member signature) { + super(pointcut, attribute.getStart(), attribute.getEnd(), attribute.getSourceContext()); + this.attribute = attribute; + kind = attribute.getKind(); // alias + this.signature = signature; + if (signature != null) { + bindingParameterTypes = signature.getParameterTypes(); + } else { + bindingParameterTypes = new UnresolvedType[0]; + } + } + + public boolean match(Shadow shadow, World world) { + if (super.match(shadow, world)) { + if (shadow.getKind() == Shadow.ExceptionHandler) { + if (kind.isAfter() || kind == AdviceKind.Around) { + world.showMessage(IMessage.WARNING, WeaverMessages.format(WeaverMessages.ONLY_BEFORE_ON_HANDLER), + getSourceLocation(), shadow.getSourceLocation()); + return false; + } + } + if (shadow.getKind() == Shadow.SynchronizationLock || shadow.getKind() == Shadow.SynchronizationUnlock) { + if (kind == AdviceKind.Around + // Don't work, see comments in SynchronizationTests + // && attribute.getProceedCallSignatures()!=null + // && attribute.getProceedCallSignatures().length!=0 + ) { + world.showMessage(IMessage.WARNING, WeaverMessages.format(WeaverMessages.NO_AROUND_ON_SYNCHRONIZATION), + getSourceLocation(), shadow.getSourceLocation()); + return false; + } + } + + if (hasExtraParameter() && kind == AdviceKind.AfterReturning) { + ResolvedType resolvedExtraParameterType = getExtraParameterType().resolve(world); + ResolvedType shadowReturnType = shadow.getReturnType().resolve(world); + boolean matches = (resolvedExtraParameterType.isConvertableFrom(shadowReturnType) && shadow.getKind() + .hasReturnValue()); + if (matches && resolvedExtraParameterType.isParameterizedType()) { + maybeIssueUncheckedMatchWarning(resolvedExtraParameterType, shadowReturnType, shadow, world); + } + return matches; + } else if (hasExtraParameter() && kind == AdviceKind.AfterThrowing) { // pr119749 + ResolvedType exceptionType = getExtraParameterType().resolve(world); + if (!exceptionType.isCheckedException()) + return true; + UnresolvedType[] shadowThrows = shadow.getSignature().getExceptions(world); + boolean matches = false; + for (int i = 0; i < shadowThrows.length && !matches; i++) { + ResolvedType type = shadowThrows[i].resolve(world); + if (exceptionType.isAssignableFrom(type)) + matches = true; + } + return matches; + } else if (kind == AdviceKind.PerTargetEntry) { + return shadow.hasTarget(); + } else if (kind == AdviceKind.PerThisEntry) { + return shadow.hasThis(); + } else if (kind == AdviceKind.Around) { + if (shadow.getKind() == Shadow.PreInitialization) { + world.showMessage(IMessage.WARNING, WeaverMessages.format(WeaverMessages.AROUND_ON_PREINIT), + getSourceLocation(), shadow.getSourceLocation()); + return false; + } else if (shadow.getKind() == Shadow.Initialization) { + world.showMessage(IMessage.WARNING, WeaverMessages.format(WeaverMessages.AROUND_ON_INIT), getSourceLocation(), + shadow.getSourceLocation()); + return false; + } else if (shadow.getKind() == Shadow.StaticInitialization + && shadow.getEnclosingType().resolve(world).isInterface()) { + world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.AROUND_ON_INTERFACE_STATICINIT, shadow + .getEnclosingType().getName()), getSourceLocation(), shadow.getSourceLocation()); + return false; + } else { + // System.err.println(getSignature().getReturnType() + + // " from " + shadow.getReturnType()); + if (getSignature().getReturnType() == ResolvedType.VOID) { + if (shadow.getReturnType() != ResolvedType.VOID) { + world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.NON_VOID_RETURN, shadow), + getSourceLocation(), shadow.getSourceLocation()); + return false; + } + } else if (getSignature().getReturnType().equals(UnresolvedType.OBJECT)) { + return true; + } else { + ResolvedType shadowReturnType = shadow.getReturnType().resolve(world); + ResolvedType adviceReturnType = getSignature().getGenericReturnType().resolve(world); + + if (shadowReturnType.isParameterizedType() && adviceReturnType.isRawType()) { // Set + // < + // Integer + // > + // and + // Set + ResolvedType shadowReturnGenericType = shadowReturnType.getGenericType(); // Set + ResolvedType adviceReturnGenericType = adviceReturnType.getGenericType(); // Set + if (shadowReturnGenericType.isAssignableFrom(adviceReturnGenericType) + && world.getLint().uncheckedAdviceConversion.isEnabled()) { + world.getLint().uncheckedAdviceConversion.signal(new String[] { shadow.toString(), + shadowReturnType.getName(), adviceReturnType.getName() }, shadow.getSourceLocation(), + new ISourceLocation[] { getSourceLocation() }); + } + } else if (!shadowReturnType.isAssignableFrom(adviceReturnType)) { + // System.err.println(this + ", " + sourceContext + + // ", " + start); + world.showMessage(IMessage.ERROR, WeaverMessages + .format(WeaverMessages.INCOMPATIBLE_RETURN_TYPE, shadow), getSourceLocation(), shadow + .getSourceLocation()); + return false; + } + } + } + } + return true; + } else { + return false; + } + } + + /** + * In after returning advice if we are binding the extra parameter to a parameterized type we may not be able to do a type-safe + * conversion. + * + * @param resolvedExtraParameterType the type in the after returning declaration + * @param shadowReturnType the type at the shadow + * @param world + */ + private void maybeIssueUncheckedMatchWarning(ResolvedType afterReturningType, ResolvedType shadowReturnType, Shadow shadow, + World world) { + boolean inDoubt = !afterReturningType.isAssignableFrom(shadowReturnType); + if (inDoubt && world.getLint().uncheckedArgument.isEnabled()) { + String uncheckedMatchWith = afterReturningType.getSimpleBaseName(); + if (shadowReturnType.isParameterizedType() && (shadowReturnType.getRawType() == afterReturningType.getRawType())) { + uncheckedMatchWith = shadowReturnType.getSimpleName(); + } + if (!Utils.isSuppressing(getSignature().getAnnotations(), "uncheckedArgument")) { + world.getLint().uncheckedArgument.signal(new String[] { afterReturningType.getSimpleName(), uncheckedMatchWith, + afterReturningType.getSimpleBaseName(), shadow.toResolvedString(world) }, getSourceLocation(), + new ISourceLocation[] { shadow.getSourceLocation() }); + } + } + } + + // ---- + + public AdviceKind getKind() { + return kind; + } + + public Member getSignature() { + return signature; + } + + public boolean hasExtraParameter() { + return (getExtraParameterFlags() & ExtraArgument) != 0; + } + + protected int getExtraParameterFlags() { + return attribute.getExtraParameterFlags(); + } + + protected int getExtraParameterCount() { + return countOnes(getExtraParameterFlags() & ParameterMask); + } + + public UnresolvedType[] getBindingParameterTypes() { + return bindingParameterTypes; + } + + public void setBindingParameterTypes(UnresolvedType[] types) { + bindingParameterTypes = types; + } + + public static int countOnes(int bits) { + int ret = 0; + while (bits != 0) { + if ((bits & 1) != 0) + ret += 1; + bits = bits >> 1; + } + return ret; + } + + public int getBaseParameterCount() { + return getSignature().getParameterTypes().length - getExtraParameterCount(); + } + + public String[] getBaseParameterNames(World world) { + String[] allNames = getSignature().getParameterNames(world); + int extras = getExtraParameterCount(); + if (extras == 0) + return allNames; + String[] result = new String[getBaseParameterCount()]; + for (int i = 0; i < result.length; i++) { + result[i] = allNames[i]; + } + return result; + } + + public UnresolvedType getExtraParameterType() { + if (!hasExtraParameter()) + return ResolvedType.MISSING; + if (signature instanceof ResolvedMember) { + if (getConcreteAspect().isAnnotationStyleAspect()) { + // bug 122742 - if we're an annotation style aspect then one + // of the extra parameters could be JoinPoint which we want + // to ignore + int baseParmCnt = getBaseParameterCount(); + UnresolvedType[] genericParameterTypes = ((ResolvedMember) signature).getGenericParameterTypes(); + while ((baseParmCnt + 1 < genericParameterTypes.length) + && (genericParameterTypes[baseParmCnt].equals(AjcMemberMaker.TYPEX_JOINPOINT) + || genericParameterTypes[baseParmCnt].equals(AjcMemberMaker.TYPEX_STATICJOINPOINT) || genericParameterTypes[baseParmCnt] + .equals(AjcMemberMaker.TYPEX_ENCLOSINGSTATICJOINPOINT))) { + baseParmCnt++; + } + return ((ResolvedMember) signature).getGenericParameterTypes()[baseParmCnt]; + } + return ((ResolvedMember) signature).getGenericParameterTypes()[getBaseParameterCount()]; + } else { + return signature.getParameterTypes()[getBaseParameterCount()]; + } + } + + public UnresolvedType getDeclaringAspect() { + return getOriginalSignature().getDeclaringType(); + } + + protected Member getOriginalSignature() { + return signature; + } + + protected String extraParametersToString() { + if (getExtraParameterFlags() == 0) { + return ""; + } else { + return "(extraFlags: " + getExtraParameterFlags() + ")"; + } + } + + public Pointcut getPointcut() { + return pointcut; + } + + // ---- + + /** + * @param fromType is guaranteed to be a non-abstract aspect + * @param clause has been concretized at a higher level + */ + public ShadowMunger concretize(ResolvedType fromType, World world, PerClause clause) { + // assert !fromType.isAbstract(); + Pointcut p = pointcut.concretize(fromType, getDeclaringType(), signature.getArity(), this); + if (clause != null) { + Pointcut oldP = p; + p = new AndPointcut(clause, p); + p.copyLocationFrom(oldP); + p.state = Pointcut.CONCRETE; + + // FIXME ? ATAJ copy unbound bindings to ignore + p.m_ignoreUnboundBindingForNames = oldP.m_ignoreUnboundBindingForNames; + } + + Advice munger = world.getWeavingSupport().createAdviceMunger(attribute, p, signature); + munger.concreteAspect = fromType; + munger.bindingParameterTypes = bindingParameterTypes; + munger.setDeclaringType(getDeclaringType()); + // System.err.println("concretizing here " + p + " with clause " + + // clause); + return munger; + } + + // ---- from object + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append("(").append(getKind()).append(extraParametersToString()); + sb.append(": ").append(pointcut).append("->").append(signature).append(")"); + return sb.toString(); + // return "(" + // + getKind() + // + extraParametersToString() + // + ": " + // + pointcut + // + "->" + // + signature + // + ")"; + } + + // XXX this perhaps ought to take account of the other fields in advice ... + public boolean equals(Object other) { + if (!(other instanceof Advice)) + return false; + Advice o = (Advice) other; + return o.kind.equals(kind) && ((o.pointcut == null) ? (pointcut == null) : o.pointcut.equals(pointcut)) + && ((o.signature == null) ? (signature == null) : o.signature.equals(signature)); + // && (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; + + public int hashCode() { + if (hashCode == 0) { + int result = 17; + result = 37 * result + kind.hashCode(); + result = 37 * result + ((pointcut == null) ? 0 : pointcut.hashCode()); + result = 37 * result + ((signature == null) ? 0 : signature.hashCode()); + hashCode = result; + } + return hashCode; + } + + // ---- fields + + public static final int ExtraArgument = 1; + public static final int ThisJoinPoint = 2; + public static final int ThisJoinPointStaticPart = 4; + public static final int ThisEnclosingJoinPointStaticPart = 8; + public static final int ParameterMask = 0xf; + + public static final int CanInline = 0x40; + + // for testing only + public void setLexicalPosition(int lexicalPosition) { + start = lexicalPosition; + } + + public ResolvedType getConcreteAspect() { + return concreteAspect; + } + + public boolean hasMatchedSomething() { + return hasMatchedAtLeastOnce; + } + + public void setHasMatchedSomething(boolean hasMatchedSomething) { + hasMatchedAtLeastOnce = hasMatchedSomething; + } + + public abstract boolean hasDynamicTests(); + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/AdviceKind.java b/org.aspectj.matcher/src/org/aspectj/weaver/AdviceKind.java new file mode 100644 index 000000000..7242ef616 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/AdviceKind.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; + +import java.io.IOException; + +import org.aspectj.util.TypeSafeEnum; + +/** + * The five kinds of advice in AspectJ. + * + * @author Erik Hilsdale + * @author Jim Hugunin + */ +public class AdviceKind extends TypeSafeEnum { + private int precedence; + private boolean isAfter; + private boolean isCflow; + public AdviceKind(String name, int key, int precedence, boolean isAfter, boolean isCflow) { + super(name, key); + this.precedence = precedence; + this.isAfter = isAfter; + this.isCflow = isCflow; + } + + public static AdviceKind read(VersionedDataInputStream s) throws IOException { + int key = s.readByte(); + switch(key) { + case 1: return Before; + case 2: return After; + case 3: return AfterThrowing; + case 4: return AfterReturning; + case 5: return Around; + case 6: return CflowEntry; + case 7: return CflowBelowEntry; + + case 8: return InterInitializer; + + case 9: return PerCflowEntry; + case 10: return PerCflowBelowEntry; + case 11: return PerThisEntry; + case 12: return PerTargetEntry; + + case 13: return Softener; + + case 14: return PerTypeWithinEntry; + } + throw new RuntimeException("unimplemented kind: " + key); + } + + public static final AdviceKind Before = new AdviceKind("before", 1, 0, false, false); + public static final AdviceKind After = new AdviceKind("after", 2, 0, true, false); + public static final AdviceKind AfterThrowing = new AdviceKind("afterThrowing", 3, 0, true, false); + public static final AdviceKind AfterReturning = new AdviceKind("afterReturning", 4, 0, true, false); + public static final AdviceKind Around = new AdviceKind("around", 5, 0, false, false); + + // these kinds can't be declared, but are used by the weaver + public static final AdviceKind CflowEntry = new AdviceKind("cflowEntry", 6, 1, false, true); + public static final AdviceKind CflowBelowEntry = new AdviceKind("cflowBelowEntry", 7, -1, false, true); //XXX resolve precednece with the below + public static final AdviceKind InterInitializer = new AdviceKind("interInitializer", 8, -2, false, false); + + public static final AdviceKind PerCflowEntry = new AdviceKind("perCflowEntry", 9, 1, false, true); + public static final AdviceKind PerCflowBelowEntry = new AdviceKind("perCflowBelowEntry", 10, -1, false, true); + + public static final AdviceKind PerThisEntry = new AdviceKind("perThisEntry", 11, 1, false, false); + public static final AdviceKind PerTargetEntry = new AdviceKind("perTargetEntry", 12, 1, false, false); + + public static final AdviceKind Softener = new AdviceKind("softener", 13, 1, false, false); + + // PTWIMPL Advice representing when aspect should be initialized + public static final AdviceKind PerTypeWithinEntry = new AdviceKind("perTypeWithinEntry",14,1,false,false); + + + public static AdviceKind stringToKind(String s) { + if (s.equals(Before.getName())) return Before; + if (s.equals(After.getName())) return After; + if (s.equals(AfterThrowing.getName())) return AfterThrowing; + if (s.equals(AfterReturning.getName())) return AfterReturning; + if (s.equals(Around.getName())) return Around; + throw new IllegalArgumentException("unknown kind: " + "\"" + s + "\""); + } + + public boolean isAfter() { + return this.isAfter; + } + + public boolean isCflow() { + return this.isCflow; + } + + public int getPrecedence() { + return precedence; + } + + public boolean isPerEntry() { + return + this == PerCflowEntry || this == PerCflowBelowEntry || + this == PerThisEntry || this == PerTargetEntry || + this == PerTypeWithinEntry; // PTWIMPL Allow for PTW case + } + + public boolean isPerObjectEntry() { + return this == PerThisEntry || this == PerTargetEntry; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/AjAttribute.java b/org.aspectj.matcher/src/org/aspectj/weaver/AjAttribute.java new file mode 100644 index 000000000..00ea0b085 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/AjAttribute.java @@ -0,0 +1,699 @@ +/* ******************************************************************* + * 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.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.IOException; + +import org.aspectj.bridge.MessageUtil; +import org.aspectj.bridge.Version; +import org.aspectj.util.FileUtil; +import org.aspectj.weaver.patterns.Declare; +import org.aspectj.weaver.patterns.IScope; +import org.aspectj.weaver.patterns.PerClause; +import org.aspectj.weaver.patterns.Pointcut; + +/** + * These attributes are written to and read from .class files (see the JVM + * spec). + * + *

+ * Each member or type can have a number of AjAttributes. Each such attribute is + * in 1-1 correspondence with an Unknown bcel attribute. Creating one of these + * does NOTHING to the underlying thing, so if you really want to add an + * attribute to a particular thing, well, you'd better actually do that. + * + * @author Erik Hilsdale + * @author Jim Hugunin + */ +public abstract class AjAttribute { + + public static final String AttributePrefix = "org.aspectj.weaver"; + + protected abstract void write(DataOutputStream s) throws IOException; + + public abstract String getNameString(); + + public char[] getNameChars() { + return getNameString().toCharArray(); + } + + /** + * Just writes the contents + */ + public byte[] getBytes() { + try { + ByteArrayOutputStream b0 = new ByteArrayOutputStream(); + DataOutputStream s0 = new DataOutputStream(b0); + write(s0); + return b0.toByteArray(); + } catch (IOException e) { + // shouldn't happen with ByteArrayOutputStreams + throw new RuntimeException("sanity check"); + } + } + + /** + * Writes the full attribute, i.e. name_index, length, and contents + */ + public byte[] getAllBytes(short nameIndex) { + try { + byte[] bytes = getBytes(); + + ByteArrayOutputStream b0 = new ByteArrayOutputStream(); + DataOutputStream s0 = new DataOutputStream(b0); + + s0.writeShort(nameIndex); + s0.writeInt(bytes.length); + s0.write(bytes); + return b0.toByteArray(); + } catch (IOException e) { + // shouldn't happen with ByteArrayOutputStreams + throw new RuntimeException("sanity check"); + } + } + + public static AjAttribute read(AjAttribute.WeaverVersionInfo v, String name, byte[] bytes, ISourceContext context, World w) { + try { + if (bytes == null) + bytes = new byte[0]; + VersionedDataInputStream s = new VersionedDataInputStream(new ByteArrayInputStream(bytes)); + s.setVersion(v); + if (name.equals(Aspect.AttributeName)) { + return new Aspect(PerClause.readPerClause(s, context)); + } else if (name.equals(MethodDeclarationLineNumberAttribute.AttributeName)) { + return MethodDeclarationLineNumberAttribute.read(s); + } else if (name.equals(WeaverState.AttributeName)) { + return new WeaverState(WeaverStateInfo.read(s, context)); + } else if (name.equals(WeaverVersionInfo.AttributeName)) { + return WeaverVersionInfo.read(s); + } else if (name.equals(AdviceAttribute.AttributeName)) { + AdviceAttribute aa = AdviceAttribute.read(s, context); + aa.getPointcut().check(context, w); + return aa; + } else if (name.equals(PointcutDeclarationAttribute.AttributeName)) { + PointcutDeclarationAttribute pda = new PointcutDeclarationAttribute(ResolvedPointcutDefinition.read(s, context)); + pda.pointcutDef.getPointcut().check(context, w); + return pda; + } else if (name.equals(TypeMunger.AttributeName)) { + return new TypeMunger(ResolvedTypeMunger.read(s, context)); + } else if (name.equals(AjSynthetic.AttributeName)) { + return new AjSynthetic(); + } else if (name.equals(DeclareAttribute.AttributeName)) { + return new DeclareAttribute(Declare.read(s, context)); + } else if (name.equals(PrivilegedAttribute.AttributeName)) { + return PrivilegedAttribute.read(s, context); + } else if (name.equals(SourceContextAttribute.AttributeName)) { + return SourceContextAttribute.read(s); + } else if (name.equals(EffectiveSignatureAttribute.AttributeName)) { + return EffectiveSignatureAttribute.read(s, context); + } else { + // We have to tell the user about this... + if (w == null || w.getMessageHandler() == null) + throw new BCException("unknown attribute" + name); + w.getMessageHandler().handleMessage(MessageUtil.warn("unknown attribute encountered " + name)); + return null; + } + } catch (BCException e) { + throw new BCException("malformed " + name + " attribute (length:" + bytes.length + ")" + e); + } catch (IOException e) { + throw new BCException("malformed " + name + " attribute (length:" + bytes.length + ")" + e); + } + } + + // ---- + + /** + * Synthetic members should have NO advice put on them or on their contents. + * This attribute is currently unused as we consider all members starting + * with NameMangler.PREFIX to automatically be synthetic. As we use this we + * might find that we want multiple kinds of synthetic. In particular, if we + * want to treat the call to a synthetic getter (say, of an introduced + * field) as a field reference itself, then a method might want a particular + * kind of AjSynthetic attribute that also includes a signature of what it + * stands for. + */ + public static class AjSynthetic extends AjAttribute { + public static final String AttributeName = "org.aspectj.weaver.AjSynthetic"; + + public String getNameString() { + return AttributeName; + } + + // private ResolvedTypeMunger munger; + public AjSynthetic() { + } + + public void write(DataOutputStream s) throws IOException { + } + } + + public static class TypeMunger extends AjAttribute { + public static final String AttributeName = "org.aspectj.weaver.TypeMunger"; + + public String getNameString() { + return AttributeName; + } + + private final ResolvedTypeMunger munger; + + public TypeMunger(ResolvedTypeMunger munger) { + this.munger = munger; + } + + public void write(DataOutputStream s) throws IOException { + munger.write(s); + } + + public ConcreteTypeMunger reify(World world, ResolvedType aspectType) { + return world.getWeavingSupport().concreteTypeMunger(munger, aspectType); + } + } + + public static class WeaverState extends AjAttribute { + public static final String AttributeName = "org.aspectj.weaver.WeaverState"; + + public String getNameString() { + return AttributeName; + } + + private final WeaverStateInfo kind; + + public WeaverState(WeaverStateInfo kind) { + this.kind = kind; + } + + public void write(DataOutputStream s) throws IOException { + kind.write(s); + } + + public WeaverStateInfo reify() { + return kind; + } + } + + public static class WeaverVersionInfo extends AjAttribute { + public static final String AttributeName = "org.aspectj.weaver.WeaverVersion"; + + // If you change the format of an AspectJ class file, you have two + // options: + // - changing the minor version means you have not added anything that + // prevents + // previous versions of the weaver from operating (e.g. + // MethodDeclarationLineNumber attribute) + // - changing the major version means you have added something that + // prevents previous + // versions of the weaver from operating correctly. + // + // The user will get a warning for any org.aspectj.weaver attributes the + // weaver does + // not recognize. + + // When we don't know ... (i.e. pre 1.2.1) + public final static short WEAVER_VERSION_MAJOR_UNKNOWN = 0; + public final static short WEAVER_VERSION_MINOR_UNKNOWN = 0; + + // These are the weaver major/minor numbers for AspectJ 1.2.1 + public final static short WEAVER_VERSION_MAJOR_AJ121 = 1; + public final static short WEAVER_VERSION_MINOR_AJ121 = 0; + + // These are the weaver major/minor numbers for AspectJ 1.5.0 + public final static short WEAVER_VERSION_MAJOR_AJ150M4 = 3; + public final static short WEAVER_VERSION_MAJOR_AJ150 = 2; + public final static short WEAVER_VERSION_MINOR_AJ150 = 0; + + // These are the weaver major/minor numbers for AspectJ 1.6.0 + public final static short WEAVER_VERSION_MAJOR_AJ160M2 = 5; + public final static short WEAVER_VERSION_MAJOR_AJ160 = 4; + public final static short WEAVER_VERSION_MINOR_AJ160 = 0; + + // These are the weaver major/minor numbers for AspectJ 1.6.1 + public final static short WEAVER_VERSION_MAJOR_AJ161 = 6; // annotation + // value + // binding + public final static short WEAVER_VERSION_MINOR_AJ161 = 0; + + // These are the weaver major/minor versions for *this* weaver + private final static short CURRENT_VERSION_MAJOR = WEAVER_VERSION_MAJOR_AJ161; + private final static short CURRENT_VERSION_MINOR = WEAVER_VERSION_MINOR_AJ161; + + public final static WeaverVersionInfo UNKNOWN = new WeaverVersionInfo(WEAVER_VERSION_MAJOR_UNKNOWN, + WEAVER_VERSION_MINOR_UNKNOWN); + public final static WeaverVersionInfo CURRENT = new WeaverVersionInfo(CURRENT_VERSION_MAJOR, CURRENT_VERSION_MINOR); + + // These are the versions read in from a particular class file. + private final short major_version; + private final short minor_version; + + private long buildstamp = Version.NOTIME; + + public String getNameString() { + return AttributeName; + } + + // Default ctor uses the current version numbers + public WeaverVersionInfo() { + major_version = CURRENT_VERSION_MAJOR; + minor_version = CURRENT_VERSION_MINOR; + } + + public WeaverVersionInfo(short major, short minor) { + major_version = major; + minor_version = minor; + } + + public void write(DataOutputStream s) throws IOException { + s.writeShort(CURRENT_VERSION_MAJOR); + s.writeShort(CURRENT_VERSION_MINOR); + s.writeLong(Version.getTime()); // build used to construct the + // class... + } + + public static WeaverVersionInfo read(VersionedDataInputStream s) throws IOException { + short major = s.readShort(); + short minor = s.readShort(); + WeaverVersionInfo wvi = new WeaverVersionInfo(major, minor); + if (s.getMajorVersion() >= WEAVER_VERSION_MAJOR_AJ150M4) { + long stamp = 0; + try { + stamp = s.readLong(); + wvi.setBuildstamp(stamp); + } catch (EOFException eof) { + // didnt find that build stamp - its not the end of the + // world + } + } + return wvi; + } + + public short getMajorVersion() { + return major_version; + } + + public short getMinorVersion() { + return minor_version; + } + + public static short getCurrentWeaverMajorVersion() { + return CURRENT_VERSION_MAJOR; + } + + public static short getCurrentWeaverMinorVersion() { + return CURRENT_VERSION_MINOR; + } + + public void setBuildstamp(long stamp) { + buildstamp = stamp; + } + + public long getBuildstamp() { + return buildstamp; + } + + public String toString() { + return major_version + "." + minor_version; + } + + public static String toCurrentVersionString() { + return CURRENT_VERSION_MAJOR + "." + CURRENT_VERSION_MINOR; + } + + } + + public static class SourceContextAttribute extends AjAttribute { + public static final String AttributeName = "org.aspectj.weaver.SourceContext"; + + public String getNameString() { + return AttributeName; + } + + private final String sourceFileName; + private final int[] lineBreaks; + + public SourceContextAttribute(String sourceFileName, int[] lineBreaks) { + this.sourceFileName = sourceFileName; + this.lineBreaks = lineBreaks; + } + + public void write(DataOutputStream s) throws IOException { + s.writeUTF(sourceFileName); + FileUtil.writeIntArray(lineBreaks, s); + } + + public static SourceContextAttribute read(VersionedDataInputStream s) throws IOException { + return new SourceContextAttribute(s.readUTF(), FileUtil.readIntArray(s)); + } + + public int[] getLineBreaks() { + return lineBreaks; + } + + public String getSourceFileName() { + return sourceFileName; + } + } + + public static class MethodDeclarationLineNumberAttribute extends AjAttribute { + + public static final String AttributeName = "org.aspectj.weaver.MethodDeclarationLineNumber"; + + public String getNameString() { + return AttributeName; + } + + private final int lineNumber; + + // AV: added in 1.5 M3 thus handling cases where we don't have that + // information + private final int offset; + + public MethodDeclarationLineNumberAttribute(int line, int offset) { + lineNumber = line; + this.offset = offset; + } + + public int getLineNumber() { + return lineNumber; + } + + public int getOffset() { + return offset; + } + + public void write(DataOutputStream s) throws IOException { + s.writeInt(lineNumber); + s.writeInt(offset); + } + + public static MethodDeclarationLineNumberAttribute read(VersionedDataInputStream s) throws IOException { + int line = s.readInt(); + int offset = 0; + if (s.available() > 0) { + offset = s.readInt(); + } + return new MethodDeclarationLineNumberAttribute(line, offset); + } + + public String toString() { + return AttributeName + ": " + lineNumber + ":" + offset; + } + } + + public static class PointcutDeclarationAttribute extends AjAttribute { + public static final String AttributeName = "org.aspectj.weaver.PointcutDeclaration"; + + public String getNameString() { + return AttributeName; + } + + private final ResolvedPointcutDefinition pointcutDef; + + public PointcutDeclarationAttribute(ResolvedPointcutDefinition pointcutDef) { + this.pointcutDef = pointcutDef; + } + + public void write(DataOutputStream s) throws IOException { + pointcutDef.write(s); + } + + public ResolvedPointcutDefinition reify() { + return pointcutDef; + } + } + + public static class DeclareAttribute extends AjAttribute { + public static final String AttributeName = "org.aspectj.weaver.Declare"; + + public String getNameString() { + return AttributeName; + } + + private final Declare declare; + + public DeclareAttribute(Declare declare) { + this.declare = declare; + } + + public void write(DataOutputStream s) throws IOException { + declare.write(s); + } + + public Declare getDeclare() { + return declare; + } + } + + public static class AdviceAttribute extends AjAttribute { + public static final String AttributeName = "org.aspectj.weaver.Advice"; + + public String getNameString() { + return AttributeName; + } + + private final AdviceKind kind; + private final Pointcut pointcut; + private final int extraParameterFlags; + private final int start; + private final int end; + private final ISourceContext sourceContext; + + // these are only used by around advice + private boolean proceedInInners; + private ResolvedMember[] proceedCallSignatures; // size == # of proceed + // calls in body + private boolean[] formalsUnchangedToProceed; // size == formals.size + private UnresolvedType[] declaredExceptions; + + /** + * @param lexicalPosition must be greater than the lexicalPosition of + * any advice declared before this one in an aspect, + * otherwise, it can be any value. + */ + public AdviceAttribute(AdviceKind kind, Pointcut pointcut, int extraArgumentFlags, int start, int end, + ISourceContext sourceContext) { + this.kind = kind; + this.pointcut = pointcut; + extraParameterFlags = extraArgumentFlags; + this.start = start; + this.end = end; + this.sourceContext = sourceContext; + + // XXX put this back when testing works better (or fails better) + // if (kind == AdviceKind.Around) throw new + // IllegalArgumentException("not for around"); + } + + public AdviceAttribute(AdviceKind kind, Pointcut pointcut, int extraArgumentFlags, int start, int end, + ISourceContext sourceContext, boolean proceedInInners, ResolvedMember[] proceedCallSignatures, + boolean[] formalsUnchangedToProceed, UnresolvedType[] declaredExceptions) { + this.kind = kind; + this.pointcut = pointcut; + extraParameterFlags = extraArgumentFlags; + this.start = start; + this.end = end; + this.sourceContext = sourceContext; + + if (kind != AdviceKind.Around) + throw new IllegalArgumentException("only for around"); + + this.proceedInInners = proceedInInners; + this.proceedCallSignatures = proceedCallSignatures; + this.formalsUnchangedToProceed = formalsUnchangedToProceed; + this.declaredExceptions = declaredExceptions; + } + + public static AdviceAttribute read(VersionedDataInputStream s, ISourceContext context) throws IOException { + AdviceKind kind = AdviceKind.read(s); + if (kind == AdviceKind.Around) { + return new AdviceAttribute(kind, Pointcut.read(s, context), s.readByte(), s.readInt(), s.readInt(), context, s + .readBoolean(), ResolvedMemberImpl.readResolvedMemberArray(s, context), FileUtil.readBooleanArray(s), + UnresolvedType.readArray(s)); + } else { + return new AdviceAttribute(kind, Pointcut.read(s, context), s.readByte(), s.readInt(), s.readInt(), context); + } + } + + public void write(DataOutputStream s) throws IOException { + kind.write(s); + pointcut.write(s); + s.writeByte(extraParameterFlags); + s.writeInt(start); + s.writeInt(end); + + if (kind == AdviceKind.Around) { + s.writeBoolean(proceedInInners); + ResolvedMemberImpl.writeArray(proceedCallSignatures, s); + FileUtil.writeBooleanArray(formalsUnchangedToProceed, s); + UnresolvedType.writeArray(declaredExceptions, s); + } + } + + public Advice reify(Member signature, World world) { + return world.getWeavingSupport().createAdviceMunger(this, pointcut, signature); + } + + public String toString() { + return "AdviceAttribute(" + kind + ", " + pointcut + ", " + extraParameterFlags + ", " + start + ")"; + } + + public int getExtraParameterFlags() { + return extraParameterFlags; + } + + public AdviceKind getKind() { + return kind; + } + + public Pointcut getPointcut() { + return pointcut; + } + + public UnresolvedType[] getDeclaredExceptions() { + return declaredExceptions; + } + + public boolean[] getFormalsUnchangedToProceed() { + return formalsUnchangedToProceed; + } + + public ResolvedMember[] getProceedCallSignatures() { + return proceedCallSignatures; + } + + public boolean isProceedInInners() { + return proceedInInners; + } + + public int getEnd() { + return end; + } + + public ISourceContext getSourceContext() { + return sourceContext; + } + + public int getStart() { + return start; + } + + } + + public static class Aspect extends AjAttribute { + public static final String AttributeName = "org.aspectj.weaver.Aspect"; + + public String getNameString() { + return AttributeName; + } + + private final PerClause perClause; + private IScope resolutionScope; + + public Aspect(PerClause perClause) { + this.perClause = perClause; + } + + public PerClause reify(ResolvedType inAspect) { + // XXXperClause.concretize(inAspect); + return perClause; + } + + public PerClause reifyFromAtAspectJ(ResolvedType inAspect) { + perClause.resolve(resolutionScope); + return perClause; + } + + public void write(DataOutputStream s) throws IOException { + perClause.write(s); + } + + public void setResolutionScope(IScope binding) { + resolutionScope = binding; + } + } + + public static class PrivilegedAttribute extends AjAttribute { + public static final String AttributeName = "org.aspectj.weaver.Privileged"; + + public String getNameString() { + return AttributeName; + } + + private final ResolvedMember[] accessedMembers; + + public PrivilegedAttribute(ResolvedMember[] accessedMembers) { + this.accessedMembers = accessedMembers; + } + + public void write(DataOutputStream s) throws IOException { + ResolvedMemberImpl.writeArray(accessedMembers, s); + } + + public ResolvedMember[] getAccessedMembers() { + return accessedMembers; + } + + public static PrivilegedAttribute read(VersionedDataInputStream s, ISourceContext context) throws IOException { + return new PrivilegedAttribute(ResolvedMemberImpl.readResolvedMemberArray(s, context)); + } + } + + public static class EffectiveSignatureAttribute extends AjAttribute { + public static final String AttributeName = "org.aspectj.weaver.EffectiveSignature"; + + public String getNameString() { + return AttributeName; + } + + private final ResolvedMember effectiveSignature; + private final Shadow.Kind shadowKind; + private final boolean weaveBody; + + public EffectiveSignatureAttribute(ResolvedMember effectiveSignature, Shadow.Kind shadowKind, boolean weaveBody) { + this.effectiveSignature = effectiveSignature; + this.shadowKind = shadowKind; + this.weaveBody = weaveBody; + } + + public void write(DataOutputStream s) throws IOException { + effectiveSignature.write(s); + shadowKind.write(s); + s.writeBoolean(weaveBody); + } + + public static EffectiveSignatureAttribute read(VersionedDataInputStream s, ISourceContext context) throws IOException { + return new EffectiveSignatureAttribute(ResolvedMemberImpl.readResolvedMember(s, context), Shadow.Kind.read(s), s + .readBoolean()); + } + + public ResolvedMember getEffectiveSignature() { + return effectiveSignature; + } + + public String toString() { + return "EffectiveSignatureAttribute(" + effectiveSignature + ", " + shadowKind + ")"; + } + + public Shadow.Kind getShadowKind() { + return shadowKind; + } + + public boolean isWeaveBody() { + return weaveBody; + } + + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/AjcMemberMaker.java b/org.aspectj.matcher/src/org/aspectj/weaver/AjcMemberMaker.java new file mode 100644 index 000000000..e76141577 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/AjcMemberMaker.java @@ -0,0 +1,621 @@ +/* ******************************************************************* + * 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.lang.reflect.Modifier; + +//import org.aspectj.weaver.ResolvedType.Name; + +public class AjcMemberMaker { + private static final int PUBLIC_STATIC_FINAL = Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL; + + private static final int PRIVATE_STATIC = Modifier.PRIVATE | Modifier.STATIC; + + private static final int PUBLIC_STATIC = Modifier.PUBLIC | Modifier.STATIC; + + private static final int BRIDGE = 0x0040; + + private static final int VISIBILITY = Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED; + + public static final UnresolvedType CFLOW_STACK_TYPE = UnresolvedType.forName(NameMangler.CFLOW_STACK_TYPE); + public static final UnresolvedType AROUND_CLOSURE_TYPE = UnresolvedType + .forSignature("Lorg/aspectj/runtime/internal/AroundClosure;"); + + public static final UnresolvedType CONVERSIONS_TYPE = UnresolvedType.forSignature("Lorg/aspectj/runtime/internal/Conversions;"); + + public static final UnresolvedType NO_ASPECT_BOUND_EXCEPTION = UnresolvedType + .forSignature("Lorg/aspectj/lang/NoAspectBoundException;"); + + public static ResolvedMember ajcPreClinitMethod(UnresolvedType declaringType) { + return new ResolvedMemberImpl(Member.METHOD, declaringType, PRIVATE_STATIC, NameMangler.AJC_PRE_CLINIT_NAME, "()V"); + } + + public static ResolvedMember ajcPostClinitMethod(UnresolvedType declaringType) { + return new ResolvedMemberImpl(Member.METHOD, declaringType, PRIVATE_STATIC, NameMangler.AJC_POST_CLINIT_NAME, "()V"); + } + + public static Member noAspectBoundExceptionInit() { + return new ResolvedMemberImpl(Member.METHOD, NO_ASPECT_BOUND_EXCEPTION, Modifier.PUBLIC, "", "()V"); + } + + public static Member noAspectBoundExceptionInit2() { + return new ResolvedMemberImpl(Member.METHOD, NO_ASPECT_BOUND_EXCEPTION, Modifier.PUBLIC, "", + "(Ljava/lang/String;Ljava/lang/Throwable;)V"); + } + + public static Member noAspectBoundExceptionInitWithCause() { + return new ResolvedMemberImpl(Member.METHOD, NO_ASPECT_BOUND_EXCEPTION, Modifier.PUBLIC, "", + "(Ljava/lang/String;Ljava/lang/Throwable;)V"); + } + + public static ResolvedMember perCflowPush(UnresolvedType declaringType) { + return new ResolvedMemberImpl(Member.METHOD, declaringType, PUBLIC_STATIC, NameMangler.PERCFLOW_PUSH_METHOD, "()V"); + } + + public static ResolvedMember perCflowField(UnresolvedType declaringType) { + return new ResolvedMemberImpl(Member.FIELD, declaringType, PUBLIC_STATIC_FINAL, NameMangler.PERCFLOW_FIELD_NAME, + CFLOW_STACK_TYPE.getSignature()); + } + + public static ResolvedMember perSingletonField(UnresolvedType declaringType) { + return new ResolvedMemberImpl(Member.FIELD, declaringType, PUBLIC_STATIC_FINAL, NameMangler.PERSINGLETON_FIELD_NAME, + declaringType.getSignature()); + } + + public static ResolvedMember initFailureCauseField(UnresolvedType declaringType) { + return new ResolvedMemberImpl(Member.FIELD, declaringType, PRIVATE_STATIC, NameMangler.INITFAILURECAUSE_FIELD_NAME, + UnresolvedType.THROWABLE.getSignature()); + } + + public static ResolvedMember perObjectField(UnresolvedType declaringType, ResolvedType aspectType) { + int modifiers = Modifier.PRIVATE; + if (!UnresolvedType.SERIALIZABLE.resolve(aspectType.getWorld()).isAssignableFrom(aspectType)) { + modifiers |= Modifier.TRANSIENT; + } + return new ResolvedMemberImpl(Member.FIELD, declaringType, modifiers, aspectType, NameMangler + .perObjectInterfaceField(aspectType), UnresolvedType.NONE); + } + + // PTWIMPL ResolvedMember for aspect instance field, declared in matched type + public static ResolvedMember perTypeWithinField(UnresolvedType declaringType, ResolvedType aspectType) { + int modifiers = Modifier.PRIVATE | Modifier.STATIC; + if (!isSerializableAspect(aspectType)) { + modifiers |= Modifier.TRANSIENT; + } + return new ResolvedMemberImpl(Member.FIELD, declaringType, modifiers, aspectType, NameMangler + .perTypeWithinFieldForTarget(aspectType), UnresolvedType.NONE); + } + + // PTWIMPL ResolvedMember for type instance field, declared in aspect + // (holds typename for which aspect instance exists) + public static ResolvedMember perTypeWithinWithinTypeField(UnresolvedType declaringType, ResolvedType aspectType) { + int modifiers = Modifier.PRIVATE; + if (!isSerializableAspect(aspectType)) { + modifiers |= Modifier.TRANSIENT; + } + return new ResolvedMemberImpl(Member.FIELD, declaringType, modifiers, UnresolvedType.JAVA_LANG_STRING, + NameMangler.PERTYPEWITHIN_WITHINTYPEFIELD, UnresolvedType.NONE); + } + + private static boolean isSerializableAspect(ResolvedType aspectType) { + return UnresolvedType.SERIALIZABLE.resolve(aspectType.getWorld()).isAssignableFrom(aspectType); + } + + public static ResolvedMember perObjectBind(UnresolvedType declaringType) { + return new ResolvedMemberImpl(Member.METHOD, declaringType, PUBLIC_STATIC, NameMangler.PEROBJECT_BIND_METHOD, + "(Ljava/lang/Object;)V"); + } + + // PTWIMPL ResolvedMember for getInstance() method, declared in aspect + public static ResolvedMember perTypeWithinGetInstance(UnresolvedType declaringType) { + // private static a.X ajc$getInstance(java.lang.Class) + ResolvedMemberImpl rm = new ResolvedMemberImpl(Member.METHOD, declaringType, PRIVATE_STATIC, declaringType, // return value + NameMangler.PERTYPEWITHIN_GETINSTANCE_METHOD, new UnresolvedType[] { UnresolvedType.JAVA_LANG_CLASS }); + return rm; + } + + // PTWIMPL ResolvedMember for getWithinTypeName() method + public static ResolvedMember perTypeWithinGetWithinTypeNameMethod(UnresolvedType declaringType, boolean inJava5Mode) { + // public String getWithinTypeName() + ResolvedMemberImpl rm = new ResolvedMemberImpl(Member.METHOD, declaringType, Modifier.PUBLIC, + UnresolvedType.JAVA_LANG_STRING, // return value + NameMangler.PERTYPEWITHIN_GETWITHINTYPENAME_METHOD, UnresolvedType.NONE); + return rm; + } + + public static ResolvedMember perTypeWithinCreateAspectInstance(UnresolvedType declaringType) { + // public static a.X ajc$createAspectInstance(java.lang.String) + ResolvedMemberImpl rm = new ResolvedMemberImpl(Member.METHOD, declaringType, PUBLIC_STATIC, declaringType, // return value + NameMangler.PERTYPEWITHIN_CREATEASPECTINSTANCE_METHOD, new UnresolvedType[] { UnresolvedType + .forSignature("Ljava/lang/String;") }, new UnresolvedType[] {}); + return rm; + } + + public static UnresolvedType perObjectInterfaceType(UnresolvedType aspectType) { + return UnresolvedType.forName(aspectType.getName() + "$ajcMightHaveAspect"); + } + + public static ResolvedMember perObjectInterfaceGet(UnresolvedType aspectType) { + return new ResolvedMemberImpl(Member.METHOD, perObjectInterfaceType(aspectType), Modifier.PUBLIC | Modifier.ABSTRACT, + NameMangler.perObjectInterfaceGet(aspectType), "()" + aspectType.getSignature()); + } + + public static ResolvedMember perObjectInterfaceSet(UnresolvedType aspectType) { + return new ResolvedMemberImpl(Member.METHOD, perObjectInterfaceType(aspectType), Modifier.PUBLIC | Modifier.ABSTRACT, + NameMangler.perObjectInterfaceSet(aspectType), "(" + aspectType.getSignature() + ")V"); + } + + // PTWIMPL ResolvedMember for localAspectOf() method, declared in matched type + public static ResolvedMember perTypeWithinLocalAspectOf(UnresolvedType shadowType, UnresolvedType aspectType) { + return new ResolvedMemberImpl(Member.METHOD, shadowType,// perTypeWithinInterfaceType(aspectType), + Modifier.PUBLIC | Modifier.STATIC, NameMangler.perTypeWithinLocalAspectOf(aspectType), "()" + + aspectType.getSignature()); + } + + public static ResolvedMember perSingletonAspectOfMethod(UnresolvedType declaringType) { + return new ResolvedMemberImpl(Member.METHOD, declaringType, PUBLIC_STATIC, "aspectOf", "()" + declaringType.getSignature()); + } + + public static ResolvedMember perSingletonHasAspectMethod(UnresolvedType declaringType) { + return new ResolvedMemberImpl(Member.METHOD, declaringType, PUBLIC_STATIC, "hasAspect", "()Z"); + } + + public static ResolvedMember perCflowAspectOfMethod(UnresolvedType declaringType) { + return perSingletonAspectOfMethod(declaringType); + } + + public static ResolvedMember perCflowHasAspectMethod(UnresolvedType declaringType) { + return perSingletonHasAspectMethod(declaringType); + } + + public static ResolvedMember perObjectAspectOfMethod(UnresolvedType declaringType) { + return new ResolvedMemberImpl(Member.METHOD, declaringType, PUBLIC_STATIC, "aspectOf", "(Ljava/lang/Object;)" + + declaringType.getSignature()); + } + + public static ResolvedMember perObjectHasAspectMethod(UnresolvedType declaringType) { + return new ResolvedMemberImpl(Member.METHOD, declaringType, PUBLIC_STATIC, "hasAspect", "(Ljava/lang/Object;)Z"); + } + + // PTWIMPL ResolvedMember for aspectOf(), declared in aspect + public static ResolvedMember perTypeWithinAspectOfMethod(UnresolvedType declaringType, boolean inJava5Mode) { + UnresolvedType parameterType = null; + if (inJava5Mode) { + parameterType = UnresolvedType.forRawTypeName("java.lang.Class"); + } else { + parameterType = UnresolvedType.forSignature("Ljava/lang/Class;"); + } + return new ResolvedMemberImpl(Member.METHOD, declaringType, PUBLIC_STATIC, declaringType, "aspectOf", + new UnresolvedType[] { parameterType }); + // return new ResolvedMemberImpl(Member.METHOD, + // declaringType, PUBLIC_STATIC, "aspectOf", + // "(Ljava/lang/Class;)" + declaringType.getSignature()); + } + + /* + * public static ResolvedMember perTypeWithinGetWithinTypeMethod(UnresolvedType declaringType, boolean inJava5Mode) { + * UnresolvedType returnType = null; if (inJava5Mode) { returnType = UnresolvedType.forRawTypeName("java.lang.Class"); } else { + * returnType = UnresolvedType.forSignature("Ljava/lang/Class;"); } return new + * ResolvedMemberImpl(Member.METHOD,declaringType,Modifier.PUBLIC,ResolvedType.JAVA_LANG_STRING,"getWithinType",new + * UnresolvedType[]{}); } + */ + + // PTWIMPL ResolvedMember for hasAspect(), declared in aspect + public static ResolvedMember perTypeWithinHasAspectMethod(UnresolvedType declaringType, boolean inJava5Mode) { + UnresolvedType parameterType = null; + if (inJava5Mode) { + parameterType = UnresolvedType.forRawTypeName("java.lang.Class"); + } else { + parameterType = UnresolvedType.forSignature("Ljava/lang/Class;"); + } + return new ResolvedMemberImpl(Member.METHOD, declaringType, PUBLIC_STATIC, ResolvedType.BOOLEAN, "hasAspect", + new UnresolvedType[] { parameterType }); + // return new ResolvedMemberImpl(Member.METHOD, + // declaringType, PUBLIC_STATIC, "hasAspect", + // "(Ljava/lang/Class;)Z"); + } + + // -- privileged accessors + + public static ResolvedMember privilegedAccessMethodForMethod(UnresolvedType aspectType, ResolvedMember method) { + return new ResolvedMemberImpl(Member.METHOD, method.getDeclaringType(), Modifier.PUBLIC + | (method.isStatic() ? Modifier.STATIC : 0), method.getReturnType(), NameMangler.privilegedAccessMethodForMethod( + method.getName(), method.getDeclaringType(), aspectType), method.getParameterTypes(), method.getExceptions()); + } + + public static ResolvedMember privilegedAccessMethodForFieldGet(UnresolvedType aspectType, Member field) { + String sig; + if (field.isStatic()) { + sig = "()" + field.getReturnType().getSignature(); + } else { + sig = "(" + field.getDeclaringType().getSignature() + ")" + field.getReturnType().getSignature(); + } + + return new ResolvedMemberImpl(Member.METHOD, field.getDeclaringType(), PUBLIC_STATIC, // Modifier.PUBLIC | (field.isStatic() + // ? Modifier.STATIC : 0), + NameMangler.privilegedAccessMethodForFieldGet(field.getName(), field.getDeclaringType(), aspectType), sig); + } + + public static ResolvedMember privilegedAccessMethodForFieldSet(UnresolvedType aspectType, Member field) { + String sig; + if (field.isStatic()) { + sig = "(" + field.getReturnType().getSignature() + ")V"; + } else { + sig = "(" + field.getDeclaringType().getSignature() + field.getReturnType().getSignature() + ")V"; + } + + return new ResolvedMemberImpl(Member.METHOD, field.getDeclaringType(), PUBLIC_STATIC, // Modifier.PUBLIC | (field.isStatic() + // ? Modifier.STATIC : 0), + NameMangler.privilegedAccessMethodForFieldSet(field.getName(), field.getDeclaringType(), aspectType), sig); + } + + // --- inline accessors + // ??? can eclipse handle a transform this weird without putting synthetics into the mix + public static ResolvedMember superAccessMethod(UnresolvedType baseType, ResolvedMember method) { + return new ResolvedMemberImpl(Member.METHOD, baseType, Modifier.PUBLIC, method.getReturnType(), NameMangler + .superDispatchMethod(baseType, method.getName()), method.getParameterTypes(), method.getExceptions()); + } + + public static ResolvedMember inlineAccessMethodForMethod(UnresolvedType aspectType, ResolvedMember method) { + UnresolvedType[] paramTypes = method.getParameterTypes(); + if (!method.isStatic()) { + paramTypes = UnresolvedType.insert(method.getDeclaringType(), paramTypes); + } + return new ResolvedMemberImpl(Member.METHOD, aspectType, + PUBLIC_STATIC, // ??? what about privileged and super access + // ???Modifier.PUBLIC | (method.isStatic() ? Modifier.STATIC : 0), + method.getReturnType(), + + NameMangler.inlineAccessMethodForMethod(method.getName(), method.getDeclaringType(), aspectType), paramTypes, + method.getExceptions()); + } + + public static ResolvedMember inlineAccessMethodForFieldGet(UnresolvedType aspectType, Member field) { + String sig; + if (field.isStatic()) { + sig = "()" + field.getReturnType().getSignature(); + } else { + sig = "(" + field.getDeclaringType().getSignature() + ")" + field.getReturnType().getSignature(); + } + + return new ResolvedMemberImpl(Member.METHOD, aspectType, PUBLIC_STATIC, // Modifier.PUBLIC | (field.isStatic() ? + // Modifier.STATIC : 0), + NameMangler.inlineAccessMethodForFieldGet(field.getName(), field.getDeclaringType(), aspectType), sig); + } + + public static ResolvedMember inlineAccessMethodForFieldSet(UnresolvedType aspectType, Member field) { + String sig; + if (field.isStatic()) { + sig = "(" + field.getReturnType().getSignature() + ")V"; + } else { + sig = "(" + field.getDeclaringType().getSignature() + field.getReturnType().getSignature() + ")V"; + } + + return new ResolvedMemberImpl(Member.METHOD, aspectType, PUBLIC_STATIC, // Modifier.PUBLIC | (field.isStatic() ? + // Modifier.STATIC : 0), + NameMangler.inlineAccessMethodForFieldSet(field.getName(), field.getDeclaringType(), aspectType), sig); + } + + // --- runtimeLibrary api stuff + + public static Member cflowStackPeekInstance() { + return new MemberImpl(Member.METHOD, CFLOW_STACK_TYPE, 0, "peekInstance", "()Ljava/lang/Object;"); + } + + public static Member cflowStackPushInstance() { + return new MemberImpl(Member.METHOD, CFLOW_STACK_TYPE, 0, "pushInstance", "(Ljava/lang/Object;)V"); + } + + public static Member cflowStackIsValid() { + return new MemberImpl(Member.METHOD, CFLOW_STACK_TYPE, 0, "isValid", "()Z"); + } + + public static Member cflowStackInit() { + return new MemberImpl(Member.CONSTRUCTOR, CFLOW_STACK_TYPE, 0, "", "()V"); + } + + public static Member aroundClosurePreInitializationField() { + return new MemberImpl(Member.FIELD, AROUND_CLOSURE_TYPE, 0, "preInitializationState", "[Ljava/lang/Object;"); + } + + public static Member aroundClosurePreInitializationGetter() { + return new MemberImpl(Member.METHOD, AROUND_CLOSURE_TYPE, 0, "getPreInitializationState", "()[Ljava/lang/Object;"); + } + + public static ResolvedMember preIntroducedConstructor(UnresolvedType aspectType, UnresolvedType targetType, + UnresolvedType[] paramTypes) { + return new ResolvedMemberImpl(Member.METHOD, aspectType, PUBLIC_STATIC_FINAL, UnresolvedType.OBJECTARRAY, NameMangler + .preIntroducedConstructor(aspectType, targetType), paramTypes); + } + + public static ResolvedMember postIntroducedConstructor(UnresolvedType aspectType, UnresolvedType targetType, + UnresolvedType[] paramTypes) { + return new ResolvedMemberImpl(Member.METHOD, aspectType, PUBLIC_STATIC_FINAL, ResolvedType.VOID, NameMangler + .postIntroducedConstructor(aspectType, targetType), UnresolvedType.insert(targetType, paramTypes)); + } + + public static ResolvedMember itdAtDeclareParentsField(ResolvedType targetType, UnresolvedType itdType, UnresolvedType aspectType) { + return new ResolvedMemberImpl(Member.FIELD, targetType, Modifier.PRIVATE, itdType, NameMangler.itdAtDeclareParentsField( + aspectType, itdType), null); + } + + public static ResolvedMember interConstructor(ResolvedType targetType, ResolvedMember constructor, UnresolvedType aspectType) { + // + // ResolvedType targetType, + // UnresolvedType[] argTypes, + // int modifiers) + // { + ResolvedMember ret = new ResolvedMemberImpl(Member.CONSTRUCTOR, targetType, Modifier.PUBLIC, ResolvedType.VOID, "", + constructor.getParameterTypes(), constructor.getExceptions()); + // System.out.println("ret: " + ret + " mods: " + Modifier.toString(modifiers)); + if (Modifier.isPublic(constructor.getModifiers())) + return ret; + while (true) { + ret = addCookieTo(ret, aspectType); + if (targetType.lookupMemberNoSupers(ret) == null) + return ret; + } + } + + public static ResolvedMember interFieldInitializer(ResolvedMember field, UnresolvedType aspectType) { + return new ResolvedMemberImpl(Member.METHOD, aspectType, PUBLIC_STATIC, NameMangler.interFieldInitializer(aspectType, field + .getDeclaringType(), field.getName()), field.isStatic() ? "()V" : "(" + field.getDeclaringType().getSignature() + + ")V"); + } + + /** + * Makes public and non-final + */ + private static int makePublicNonFinal(int modifiers) { + return (modifiers & ~VISIBILITY & ~Modifier.FINAL) | Modifier.PUBLIC; + } + + /** + * This static method goes on the aspect that declares the inter-type field + */ + public static ResolvedMember interFieldSetDispatcher(ResolvedMember field, UnresolvedType aspectType) { + ResolvedMember rm = new ResolvedMemberImpl(Member.METHOD, aspectType, PUBLIC_STATIC, ResolvedType.VOID, NameMangler + .interFieldSetDispatcher(aspectType, field.getDeclaringType(), field.getName()), + field.isStatic() ? new UnresolvedType[] { field.getReturnType() } : new UnresolvedType[] { + field.getDeclaringType(), field.getReturnType() }); + rm.setTypeVariables(field.getTypeVariables()); + return rm; + } + + /** + * This static method goes on the aspect that declares the inter-type field + */ + public static ResolvedMember interFieldGetDispatcher(ResolvedMember field, UnresolvedType aspectType) { + ResolvedMember rm = new ResolvedMemberImpl(Member.METHOD, aspectType, PUBLIC_STATIC, field.getReturnType(), NameMangler + .interFieldGetDispatcher(aspectType, field.getDeclaringType(), field.getName()), + field.isStatic() ? UnresolvedType.NONE : new UnresolvedType[] { field.getDeclaringType() }, UnresolvedType.NONE); + rm.setTypeVariables(field.getTypeVariables()); + return rm; + } + + // private static int makeFieldModifiers(int declaredModifiers) { + // int ret = Modifier.PUBLIC; + // if (Modifier.isTransient(declaredModifiers)) ret |= Modifier.TRANSIENT; + // if (Modifier.isVolatile(declaredModifiers)) ret |= Modifier.VOLATILE; + // return ret; + // } + + /** + * This field goes on the class the field is declared onto + */ + public static ResolvedMember interFieldClassField(ResolvedMember field, UnresolvedType aspectType) { + return new ResolvedMemberImpl(Member.FIELD, field.getDeclaringType(), makePublicNonFinal(field.getModifiers()), field + .getReturnType(), NameMangler.interFieldClassField(field.getModifiers(), aspectType, field.getDeclaringType(), + field.getName()), UnresolvedType.NONE, UnresolvedType.NONE); + } + + /** + * This field goes on top-most implementers of the interface the field is declared onto + */ + public static ResolvedMember interFieldInterfaceField(ResolvedMember field, UnresolvedType onClass, UnresolvedType aspectType) { + return new ResolvedMemberImpl(Member.FIELD, onClass, makePublicNonFinal(field.getModifiers()), field.getReturnType(), + NameMangler.interFieldInterfaceField(aspectType, field.getDeclaringType(), field.getName()), UnresolvedType.NONE, + UnresolvedType.NONE); + } + + /** + * This instance method goes on the interface the field is declared onto as well as its top-most implementors + */ + public static ResolvedMember interFieldInterfaceSetter(ResolvedMember field, ResolvedType onType, UnresolvedType aspectType) { + int modifiers = Modifier.PUBLIC; + if (onType.isInterface()) + modifiers |= Modifier.ABSTRACT; + ResolvedMember rm = new ResolvedMemberImpl(Member.METHOD, onType, modifiers, ResolvedType.VOID, NameMangler + .interFieldInterfaceSetter(aspectType, field.getDeclaringType(), field.getName()), new UnresolvedType[] { field + .getReturnType() }, UnresolvedType.NONE); + rm.setTypeVariables(field.getTypeVariables()); + return rm; + } + + /** + * This instance method goes on the interface the field is declared onto as well as its top-most implementors + */ + public static ResolvedMember interFieldInterfaceGetter(ResolvedMember field, ResolvedType onType, UnresolvedType aspectType) { + int modifiers = Modifier.PUBLIC; + if (onType.isInterface()) + modifiers |= Modifier.ABSTRACT; + ResolvedMember rm = new ResolvedMemberImpl(Member.METHOD, onType, modifiers, field.getReturnType(), NameMangler + .interFieldInterfaceGetter(aspectType, field.getDeclaringType(), field.getName()), UnresolvedType.NONE, + UnresolvedType.NONE); + rm.setTypeVariables(field.getTypeVariables()); + return rm; + } + + /** + * This method goes on the target type of the inter-type method. (and possibly the topmost-implementors, if the target type is + * an interface). The implementation will call the interMethodDispatch method on the aspect. + */ + public static ResolvedMember interMethod(ResolvedMember meth, UnresolvedType aspectType, boolean onInterface) { + if (Modifier.isPublic(meth.getModifiers()) && !onInterface) + return meth; + + int modifiers = makePublicNonFinal(meth.getModifiers()); + if (onInterface) + modifiers |= Modifier.ABSTRACT; + + ResolvedMemberImpl rmi = new ResolvedMemberImpl(Member.METHOD, meth.getDeclaringType(), modifiers, meth.getReturnType(), + NameMangler.interMethod(meth.getModifiers(), aspectType, meth.getDeclaringType(), meth.getName()), meth + .getParameterTypes(), meth.getExceptions()); + rmi.setTypeVariables(meth.getTypeVariables()); + return rmi; + } + + /** + * This method goes on the target type of the inter-type method. (and possibly the topmost-implementors, if the target type is + * an interface). The implementation will call the interMethodDispatch method on the aspect. + */ + public static ResolvedMember interMethodBridger(ResolvedMember meth, UnresolvedType aspectType, boolean onInterface) { + // if (Modifier.isPublic(meth.getModifiers()) && !onInterface) + // return meth; + + int modifiers = makePublicNonFinal(meth.getModifiers()) | BRIDGE; + if (onInterface) + modifiers |= Modifier.ABSTRACT; + + ResolvedMemberImpl rmi = new ResolvedMemberImpl(Member.METHOD, meth.getDeclaringType(), modifiers, meth.getReturnType(), + NameMangler.interMethod(meth.getModifiers(), aspectType, meth.getDeclaringType(), meth.getName()), meth + .getParameterTypes(), meth.getExceptions()); + rmi.setTypeVariables(meth.getTypeVariables()); + return rmi; + } + + /** + * Sometimes the intertyped method requires a bridge method alongside it. For example if the method 'N SomeI.m()' is put onto + * an interface 'interface I' and then a concrete implementation is 'class C implements I' then the ITD + * on the interface will be 'Number m()', whereas the ITD on the 'topmostimplementor' will be 'Float m()'. A bridge method needs + * to be created in the topmostimplementor 'Number m()' that delegates to 'Float m()' + */ + public static ResolvedMember bridgerToInterMethod(ResolvedMember meth, UnresolvedType aspectType) { + + int modifiers = makePublicNonFinal(meth.getModifiers()); + + ResolvedMemberImpl rmi = new ResolvedMemberImpl(Member.METHOD, aspectType, modifiers, meth.getReturnType(), NameMangler + .interMethod(meth.getModifiers(), aspectType, meth.getDeclaringType(), meth.getName()), meth.getParameterTypes(), + meth.getExceptions()); + rmi.setTypeVariables(meth.getTypeVariables()); + return rmi; + } + + /** + * This static method goes on the declaring aspect of the inter-type method. The implementation calls the interMethodBody() + * method on the aspect. + */ + public static ResolvedMember interMethodDispatcher(ResolvedMember meth, UnresolvedType aspectType) { + UnresolvedType[] paramTypes = meth.getParameterTypes(); + if (!meth.isStatic()) { + paramTypes = UnresolvedType.insert(meth.getDeclaringType(), paramTypes); + } + + ResolvedMemberImpl rmi = new ResolvedMemberImpl(Member.METHOD, aspectType, PUBLIC_STATIC, meth.getReturnType(), NameMangler + .interMethodDispatcher(aspectType, meth.getDeclaringType(), meth.getName()), paramTypes, meth.getExceptions()); + + rmi.setTypeVariables(meth.getTypeVariables()); + + return rmi; + } + + /** + * This method goes on the declaring aspect of the inter-type method. It contains the real body of the ITD method. + */ + public static ResolvedMember interMethodBody(ResolvedMember meth, UnresolvedType aspectType) { + UnresolvedType[] paramTypes = meth.getParameterTypes(); + if (!meth.isStatic()) { + paramTypes = UnresolvedType.insert(meth.getDeclaringType(), paramTypes); + } + + int modifiers = PUBLIC_STATIC; + if (Modifier.isStrict(meth.getModifiers())) { + modifiers |= Modifier.STRICT; + } + + ResolvedMemberImpl rmi = new ResolvedMemberImpl(Member.METHOD, aspectType, modifiers, meth.getReturnType(), NameMangler + .interMethodBody(aspectType, meth.getDeclaringType(), meth.getName()), paramTypes, meth.getExceptions()); + rmi.setTypeVariables(meth.getTypeVariables()); + return rmi; + } + + private static ResolvedMember addCookieTo(ResolvedMember ret, UnresolvedType aspectType) { + UnresolvedType[] params = ret.getParameterTypes(); + + UnresolvedType[] freshParams = UnresolvedType.add(params, aspectType); + return new ResolvedMemberImpl(ret.getKind(), ret.getDeclaringType(), ret.getModifiers(), ret.getReturnType(), + ret.getName(), freshParams, ret.getExceptions()); + } + + public static ResolvedMember toObjectConversionMethod(UnresolvedType fromType) { + if (fromType.isPrimitiveType()) { + String name = fromType.toString() + "Object"; + return new ResolvedMemberImpl(Member.METHOD, CONVERSIONS_TYPE, PUBLIC_STATIC, UnresolvedType.OBJECT, name, + new UnresolvedType[] { fromType }, UnresolvedType.NONE); + } else { + return null; + } + } + + public static Member interfaceConstructor(ResolvedType resolvedTypeX) { + // AMC next two lines should not be needed when sig for generic type is changed + ResolvedType declaringType = resolvedTypeX; + if (declaringType.isRawType()) + declaringType = declaringType.getGenericType(); + return new ResolvedMemberImpl(Member.CONSTRUCTOR, declaringType, Modifier.PUBLIC, "", "()V"); + } + + // -- common types we use. Note: Java 5 dependand types are refered to as String + public final static UnresolvedType ASPECT_ANNOTATION = UnresolvedType.forSignature("Lorg/aspectj/lang/annotation/Aspect;"); + + public final static UnresolvedType BEFORE_ANNOTATION = UnresolvedType.forSignature("Lorg/aspectj/lang/annotation/Before;"); + + public final static UnresolvedType AROUND_ANNOTATION = UnresolvedType.forSignature("Lorg/aspectj/lang/annotation/Around;"); + + public final static UnresolvedType AFTERRETURNING_ANNOTATION = UnresolvedType + .forSignature("Lorg/aspectj/lang/annotation/AfterReturning;"); + + public final static UnresolvedType AFTERTHROWING_ANNOTATION = UnresolvedType + .forSignature("Lorg/aspectj/lang/annotation/AfterThrowing;"); + + public final static UnresolvedType AFTER_ANNOTATION = UnresolvedType.forSignature("Lorg/aspectj/lang/annotation/After;"); + + public final static UnresolvedType POINTCUT_ANNOTATION = UnresolvedType.forSignature("Lorg/aspectj/lang/annotation/Pointcut;"); + + public final static UnresolvedType DECLAREERROR_ANNOTATION = UnresolvedType + .forSignature("Lorg/aspectj/lang/annotation/DeclareError;"); + + public final static UnresolvedType DECLAREWARNING_ANNOTATION = UnresolvedType + .forSignature("Lorg/aspectj/lang/annotation/DeclareWarning;"); + + public final static UnresolvedType DECLAREPRECEDENCE_ANNOTATION = UnresolvedType + .forSignature("Lorg/aspectj/lang/annotation/DeclarePrecedence;"); + + // public final static UnresolvedType DECLAREIMPLEMENTS_ANNOTATION = + // UnresolvedType.forSignature("Lorg/aspectj/lang/annotation/DeclareImplements;"); + + public final static UnresolvedType DECLAREPARENTS_ANNOTATION = UnresolvedType + .forSignature("Lorg/aspectj/lang/annotation/DeclareParents;"); + + public final static UnresolvedType TYPEX_JOINPOINT = UnresolvedType.forSignature("Lorg/aspectj/lang/JoinPoint;"); + + public final static UnresolvedType TYPEX_PROCEEDINGJOINPOINT = UnresolvedType + .forSignature("Lorg/aspectj/lang/ProceedingJoinPoint;"); + + public final static UnresolvedType TYPEX_STATICJOINPOINT = UnresolvedType + .forSignature("Lorg/aspectj/lang/JoinPoint$StaticPart;"); + + public final static UnresolvedType TYPEX_ENCLOSINGSTATICJOINPOINT = UnresolvedType + .forSignature("Lorg/aspectj/lang/JoinPoint$EnclosingStaticPart;"); + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/AnnotatedElement.java b/org.aspectj.matcher/src/org/aspectj/weaver/AnnotatedElement.java new file mode 100644 index 000000000..86297b85b --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/AnnotatedElement.java @@ -0,0 +1,21 @@ +/* ******************************************************************* + * 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; + +/** + * Represents any element that may have annotations + */ +public interface AnnotatedElement { + boolean hasAnnotation(UnresolvedType ofType); + + ResolvedType[] getAnnotationTypes(); + + AnnotationAJ getAnnotationOfType(UnresolvedType ofType); +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/AnnotationAJ.java b/org.aspectj.matcher/src/org/aspectj/weaver/AnnotationAJ.java new file mode 100644 index 000000000..c7605c1ee --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/AnnotationAJ.java @@ -0,0 +1,102 @@ +/* ******************************************************************* + * Copyright (c) 2006-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; + +import java.util.Set; + +/** + * Simple representation of an annotation that the weaver can work with. + * + * @author AndyClement + */ +public interface AnnotationAJ { + + public static final AnnotationAJ[] EMPTY_ARRAY = new AnnotationAJ[0]; + + /** + * @return the signature for the annotation type, eg. Lcom/foo/MyAnno; + */ + public String getTypeSignature(); + + /** + * @return the type name for the annotation, eg. com.foo.MyAnno + */ + public String getTypeName(); + + /** + * @return the type of the annotation + */ + public ResolvedType getType(); + + /** + * return true if this annotation can target an annotation type + */ + public boolean allowedOnAnnotationType(); + + /** + * @return true if this annotation can be put on a field + */ + public boolean allowedOnField(); + + /** + * @return true if this annotation can target a 'regular' type. A 'regular' type is enum/class/interface - it is *not* + * annotation. + */ + public boolean allowedOnRegularType(); + + /** + * @return for the @target annotation, this will return a set of the element-types it can be applied to. For other annotations , + * it returns the empty set. + */ + public Set getTargets(); + + /** + * @param name the name of the value + * @return true if there is a value with that name + */ + public boolean hasNamedValue(String name); + + /** + * @param name the name of the annotation field + * @param value the value of the annotation field + * @return true if there is a value with the specified name and value + */ + public boolean hasNameValuePair(String name, String value); + + /** + * @return String representation of the valid targets for this annotation, eg. "{TYPE,FIELD}" + */ + public String getValidTargets(); + + /** + * @return String form of the annotation and any values, eg. @Foo(a=b,c=d) + */ + public String stringify(); + + /** + * @return true if this annotation is marked with @target + */ + public boolean specifiesTarget(); + + /** + * @return true if the annotation is marked for runtime visibility + */ + public boolean isRuntimeVisible(); + + /** + * Determine the string representation of the value of a field. For example in @SuppressAjWarnings({"adviceDidNotMatch"}) + * the return value for getStringFormOfValue("value") would be "[adviceDidNotMatch]". + * + * @param name the name of the annotation field being looked up + * @return string representation of the value of that field, may be null if no such field set + */ + public String getStringFormOfValue(String name); + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/AnnotationAnnotationValue.java b/org.aspectj.matcher/src/org/aspectj/weaver/AnnotationAnnotationValue.java new file mode 100644 index 000000000..8002371ca --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/AnnotationAnnotationValue.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://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andy Clement IBM initial implementation + * ******************************************************************/ + package org.aspectj.weaver; + +public class AnnotationAnnotationValue extends AnnotationValue { + + private AnnotationAJ value; + + public AnnotationAnnotationValue(AnnotationAJ value) { + super(AnnotationValue.ANNOTATION); + this.value = value; + } + + public AnnotationAJ getAnnotation() { + return value; + } + + public String stringify() { + return value.stringify(); + } + + public String toString() { + return value.toString(); + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/AnnotationNameValuePair.java b/org.aspectj.matcher/src/org/aspectj/weaver/AnnotationNameValuePair.java new file mode 100644 index 000000000..3ab561b6a --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/AnnotationNameValuePair.java @@ -0,0 +1,47 @@ +/* ******************************************************************* + * 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 IBM initial implementation + * ******************************************************************/ + package org.aspectj.weaver; + +public class AnnotationNameValuePair { + + private String name; + + private AnnotationValue val; + + public AnnotationNameValuePair(String name,AnnotationValue val) { + this.name = name; + this.val = val; + } + + public String getName() { + return name; + } + + public AnnotationValue getValue() { + return val; + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append(name+"="+val.toString()); + return sb.toString(); + } + + public String stringify() { + StringBuffer sb = new StringBuffer(); + if (!name.equals("value")) { + sb.append(name+"="); + } + sb.append(val.stringify()); + return sb.toString(); + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/AnnotationOnTypeMunger.java b/org.aspectj.matcher/src/org/aspectj/weaver/AnnotationOnTypeMunger.java new file mode 100644 index 000000000..6286d336c --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/AnnotationOnTypeMunger.java @@ -0,0 +1,57 @@ +/* ******************************************************************* + * 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; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Represents adding an annotation to a type + */ +public class AnnotationOnTypeMunger extends ResolvedTypeMunger { + AnnotationAJ newAnnotation; + + public AnnotationOnTypeMunger(AnnotationAJ anno) { + super(AnnotationOnType, null); + newAnnotation = anno; + } + + public void write(DataOutputStream s) throws IOException { + throw new RuntimeException("unimplemented"); + } + + public AnnotationAJ getNewAnnotation() { + return newAnnotation; + } + + public boolean equals(Object other) { + if (!(other instanceof AnnotationOnTypeMunger)) + return false; + AnnotationOnTypeMunger o = (AnnotationOnTypeMunger) other; + // TODO does not check equality of annotation values + return newAnnotation.getTypeSignature().equals( + o.newAnnotation.getTypeSignature()); + } + + private volatile int hashCode = 0; + + public int hashCode() { + if (hashCode == 0) { + int result = 17; + result = 37 * result + newAnnotation.getTypeSignature().hashCode(); + hashCode = result; + } + return hashCode; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/AnnotationTargetKind.java b/org.aspectj.matcher/src/org/aspectj/weaver/AnnotationTargetKind.java new file mode 100644 index 000000000..e0370411a --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/AnnotationTargetKind.java @@ -0,0 +1,51 @@ +/******************************************************************** + * 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: + * Helen Hawkins - Initial implementation + *******************************************************************/ +package org.aspectj.weaver; + +import java.io.DataInputStream; +import java.io.IOException; + +import org.aspectj.util.TypeSafeEnum; + +/** + * A TypeSafeEnum similar to the Java5 ElementType Enum + */ +public class AnnotationTargetKind extends TypeSafeEnum { + + public AnnotationTargetKind(String name, int key) { + super(name, key); + } + + public static AnnotationTargetKind read(DataInputStream s) throws IOException { + int key = s.readByte(); + switch(key) { + case 1: return ANNOTATION_TYPE; + case 2: return CONSTRUCTOR; + case 3: return FIELD; + case 4: return LOCAL_VARIABLE; + case 5: return METHOD; + case 6: return PACKAGE; + case 7: return PARAMETER; + case 8: return TYPE; + } + throw new BCException("weird annotation target kind " + key); + } + + public static final AnnotationTargetKind ANNOTATION_TYPE = new AnnotationTargetKind("ANNOTATION_TYPE", 1); + public static final AnnotationTargetKind CONSTRUCTOR = new AnnotationTargetKind("CONSTRUCTOR", 2); + public static final AnnotationTargetKind FIELD = new AnnotationTargetKind("FIELD", 3); + public static final AnnotationTargetKind LOCAL_VARIABLE = new AnnotationTargetKind("LOCAL_VARIABLE", 4); + public static final AnnotationTargetKind METHOD = new AnnotationTargetKind("METHOD", 5); + public static final AnnotationTargetKind PACKAGE = new AnnotationTargetKind("PACKAGE", 6); + public static final AnnotationTargetKind PARAMETER = new AnnotationTargetKind("PARAMETER", 7); + public static final AnnotationTargetKind TYPE = new AnnotationTargetKind("TYPE", 8); + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/AnnotationValue.java b/org.aspectj.matcher/src/org/aspectj/weaver/AnnotationValue.java new file mode 100644 index 000000000..9c7a4b916 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/AnnotationValue.java @@ -0,0 +1,72 @@ +/* ******************************************************************* + * 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 IBM initial implementation + * ******************************************************************/ + package org.aspectj.weaver; + + +public abstract class AnnotationValue { + + protected int valueKind; + + public static final int STRING = 's'; + public static final int ENUM_CONSTANT = 'e'; + public static final int CLASS = 'c'; + public static final int ANNOTATION = '@'; + public static final int ARRAY = '['; + + public static final int PRIMITIVE_INT = 'I'; + public static final int PRIMITIVE_BYTE = 'B'; + public static final int PRIMITIVE_CHAR = 'C'; + public static final int PRIMITIVE_DOUBLE = 'D'; + public static final int PRIMITIVE_FLOAT = 'F'; + public static final int PRIMITIVE_LONG = 'J'; + public static final int PRIMITIVE_SHORT = 'S'; + public static final int PRIMITIVE_BOOLEAN= 'Z'; + + public abstract String stringify(); + + public AnnotationValue(int kind) { + valueKind = kind; + } + + public static String whatKindIsThis(int kind) { + switch (kind) { + case PRIMITIVE_BYTE: // byte + return "byte"; + case PRIMITIVE_CHAR: // char + return "char"; + case PRIMITIVE_DOUBLE: // double + return "double"; + case PRIMITIVE_FLOAT: // float + return "float"; + case PRIMITIVE_INT: // int + return "int"; + case PRIMITIVE_LONG: // long + return "long"; + case PRIMITIVE_SHORT: // short + return "short"; + case PRIMITIVE_BOOLEAN: // boolean + return "boolean"; + case 's': // String + return "string"; + case 'e': // Enum constant + return "enum"; + case 'c': // Class + return "class"; + case '@': // Annotation + return "annotation"; + case '[': // Array + return "array"; + default: + throw new RuntimeException("Dont know what this is : "+kind); + } + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/ArrayAnnotationValue.java b/org.aspectj.matcher/src/org/aspectj/weaver/ArrayAnnotationValue.java new file mode 100644 index 000000000..8b63eaa8f --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/ArrayAnnotationValue.java @@ -0,0 +1,57 @@ +/* ******************************************************************* + * 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 IBM initial implementation + * ******************************************************************/ + package org.aspectj.weaver; + +public class ArrayAnnotationValue extends AnnotationValue { + + private AnnotationValue[] values; + + public ArrayAnnotationValue() { + super(AnnotationValue.ARRAY); + } + + public void setValues(AnnotationValue[] values) { + this.values = values; + } + + public ArrayAnnotationValue(AnnotationValue[] values) { + super(AnnotationValue.ARRAY); + this.values = values; + } + + public AnnotationValue[] getValues() { + return values; + } + + public String stringify() { + StringBuffer sb = new StringBuffer(); + sb.append("["); + for (int i = 0; i < values.length; i++) { + sb.append(values[i].stringify()); + if (i+1 0) { + ResolvedType[] allInterfaces = new ResolvedType[interfaces.length + + additionalInterfaceBounds.length]; + System + .arraycopy(interfaces, 0, allInterfaces, 0, + interfaces.length); + System.arraycopy(additionalInterfaceBounds, 0, allInterfaces, + interfaces.length, additionalInterfaceBounds.length); + return allInterfaces; + } else { + return interfaces; + } + } + + public boolean isGenericWildcard() { + return true; + } + + protected static class ReferenceTypeReferenceTypeDelegate extends + AbstractReferenceTypeDelegate { + + public ReferenceTypeReferenceTypeDelegate(ReferenceType backing) { + super(backing, false); + } + + public void addAnnotation(AnnotationAJ annotationX) { + throw new UnsupportedOperationException( + "What on earth do you think you are doing???"); + } + + public boolean isAspect() { + return resolvedTypeX.isAspect(); + } + + public boolean isAnnotationStyleAspect() { + return resolvedTypeX.isAnnotationStyleAspect(); + } + + public boolean isInterface() { + return resolvedTypeX.isInterface(); + } + + public boolean isEnum() { + return resolvedTypeX.isEnum(); + } + + public boolean isAnnotation() { + return resolvedTypeX.isAnnotation(); + } + + public boolean isAnnotationWithRuntimeRetention() { + return resolvedTypeX.isAnnotationWithRuntimeRetention(); + } + + public boolean isAnonymous() { + return resolvedTypeX.isAnonymous(); + } + + public boolean isNested() { + return resolvedTypeX.isNested(); + } + + public ResolvedType getOuterClass() { + return resolvedTypeX.getOuterClass(); + } + + public String getRetentionPolicy() { + return resolvedTypeX.getRetentionPolicy(); + } + + public boolean canAnnotationTargetType() { + return resolvedTypeX.canAnnotationTargetType(); + } + + public AnnotationTargetKind[] getAnnotationTargetKinds() { + return resolvedTypeX.getAnnotationTargetKinds(); + } + + public boolean isGeneric() { + return resolvedTypeX.isGenericType(); + } + + public String getDeclaredGenericSignature() { + return resolvedTypeX.getDeclaredGenericSignature(); + } + + public boolean hasAnnotation(UnresolvedType ofType) { + return resolvedTypeX.hasAnnotation(ofType); + } + + public AnnotationAJ[] getAnnotations() { + return resolvedTypeX.getAnnotations(); + } + + public ResolvedType[] getAnnotationTypes() { + return resolvedTypeX.getAnnotationTypes(); + } + + public ResolvedMember[] getDeclaredFields() { + return resolvedTypeX.getDeclaredFields(); + } + + public ResolvedType[] getDeclaredInterfaces() { + return resolvedTypeX.getDeclaredInterfaces(); + } + + public ResolvedMember[] getDeclaredMethods() { + return resolvedTypeX.getDeclaredMethods(); + } + + public ResolvedMember[] getDeclaredPointcuts() { + return resolvedTypeX.getDeclaredPointcuts(); + } + + public PerClause getPerClause() { + return resolvedTypeX.getPerClause(); + } + + public Collection getDeclares() { + return resolvedTypeX.getDeclares(); + } + + public Collection getTypeMungers() { + return resolvedTypeX.getTypeMungers(); + } + + public Collection getPrivilegedAccesses() { + return Collections.EMPTY_LIST; + } + + public int getModifiers() { + return resolvedTypeX.getModifiers(); + } + + public ResolvedType getSuperclass() { + return resolvedTypeX.getSuperclass(); + } + + public WeaverStateInfo getWeaverState() { + return null; + } + + public TypeVariable[] getTypeVariables() { + return resolvedTypeX.getTypeVariables(); + } + + public void ensureDelegateConsistent() { + resolvedTypeX.getDelegate().ensureDelegateConsistent(); + } + + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/Checker.java b/org.aspectj.matcher/src/org/aspectj/weaver/Checker.java new file mode 100644 index 000000000..7505d12e5 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/Checker.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; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +import org.aspectj.weaver.patterns.DeclareErrorOrWarning; +import org.aspectj.weaver.patterns.PerClause; +import org.aspectj.weaver.patterns.Pointcut; + +/** + * Representation of a shadow munger for a declare error or warning declaration. + * + * @author Andy Clement + */ +public class Checker extends ShadowMunger { + + private final String message; + private final boolean isError; // if not error then it is a warning + private volatile int hashCode = -1; + + /** + * Create a Checker for a deow. + * + * @param deow the declare error or warning for which to create the checker munger + */ + public Checker(DeclareErrorOrWarning deow) { + super(deow.getPointcut(), deow.getStart(), deow.getEnd(), deow.getSourceContext()); + this.message = deow.getMessage(); + this.isError = deow.isError(); + } + + /** + * Only used when filling in a parameterized Checker. + * + * @param pc the pointcut + * @param start the start + * @param end the end + * @param context the source context + * @param message the message string + * @param isError whether it is an error or just a warning + */ + private Checker(Pointcut pc, int start, int end, ISourceContext context, String message, boolean isError) { + super(pc, start, end, context); + this.message = message; + this.isError = isError; + } + + public boolean isError() { + return isError; + } + + /** + * Not supported for a Checker + */ + public void specializeOn(Shadow shadow) { + throw new RuntimeException("illegal state"); + } + + /** + * Not supported for a Checker + */ + public void implementOn(Shadow shadow) { + throw new RuntimeException("illegal state"); + } + + /** + * Determine if the Checker matches at a shadow. If it does then we can immediately report the message. There (currently) can + * never be a non-statically determinable match. + * + * @param shadow the shadow which to match against + * @param world the world through which to access message handlers + */ + public boolean match(Shadow shadow, World world) { + if (super.match(shadow, world)) { + world.reportCheckerMatch(this, shadow); + } + return false; + } + + // FIXME what the hell? + public int compareTo(Object other) { + return 0; + } + + // FIXME Alex: ATAJ is that ok in all cases ? + /** + * Default to true + * + * @return + */ + public boolean mustCheckExceptions() { + return true; + } + + public Collection getThrownExceptions() { + return Collections.EMPTY_LIST; + } + + // FIXME this perhaps ought to take account of the other fields in advice ... + public boolean equals(Object other) { + if (!(other instanceof Checker)) { + return false; + } + Checker o = (Checker) other; + return o.isError == isError && ((o.pointcut == null) ? (pointcut == null) : o.pointcut.equals(pointcut)); + } + + public int hashCode() { + if (hashCode == -1) { + int result = 17; + result = 37 * result + (isError ? 1 : 0); + result = 37 * result + ((pointcut == null) ? 0 : pointcut.hashCode()); + hashCode = result; + } + return hashCode; + } + + public ShadowMunger parameterizeWith(ResolvedType declaringType, Map typeVariableMap) { + Checker ret = new Checker(this.pointcut.parameterizeWith(typeVariableMap, declaringType.getWorld()), this.start, this.end, + this.sourceContext, this.message, this.isError); + return ret; + } + + /** + * Concretize this Checker by concretizing the pointcut. + * + */ + public ShadowMunger concretize(ResolvedType theAspect, World world, PerClause clause) { + this.pointcut = this.pointcut.concretize(theAspect, getDeclaringType(), 0, this); + this.hashCode = -1; + return this; + } + + public String getMessage() { + return this.message; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/ClassAnnotationValue.java b/org.aspectj.matcher/src/org/aspectj/weaver/ClassAnnotationValue.java new file mode 100644 index 000000000..a85c10c42 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/ClassAnnotationValue.java @@ -0,0 +1,31 @@ +/* ******************************************************************* + * 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 IBM initial implementation + * ******************************************************************/ + package org.aspectj.weaver; + +public class ClassAnnotationValue extends AnnotationValue { + + private String signature; + + public ClassAnnotationValue(String sig) { + super(AnnotationValue.CLASS); + this.signature = sig; + } + + public String stringify() { + return signature; + } + + public String toString() { + return signature; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/ConcreteTypeMunger.java b/org.aspectj.matcher/src/org/aspectj/weaver/ConcreteTypeMunger.java new file mode 100644 index 000000000..524d091be --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/ConcreteTypeMunger.java @@ -0,0 +1,144 @@ +/* ******************************************************************* + * 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.util.Map; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.util.PartialOrder; + +public abstract class ConcreteTypeMunger implements PartialOrder.PartialComparable { + protected ResolvedTypeMunger munger; + protected ResolvedType aspectType; + + public ConcreteTypeMunger(ResolvedTypeMunger munger, ResolvedType aspectType) { + this.munger = munger; + this.aspectType = aspectType; + } + + // An EclipseTypeMunger and a BcelTypeMunger may say TRUE for equivalentTo()... + // public boolean equivalentTo(Object other) { + // if (! (other instanceof ConcreteTypeMunger)) return false; + // ConcreteTypeMunger o = (ConcreteTypeMunger) other; + // return ((o.getMunger() == null) ? (getMunger() == null) : o.getMunger().equals(getMunger())) + // && ((o.getAspectType() == null) ? (getAspectType() == null) : o.getAspectType().equals(getAspectType())); + // } + + // public abstract boolean munge(LazyClassGen gen); + + /** + * returns null for mungers that are used internally, but were not part of a declared thing in source code. + */ + public ResolvedTypeMunger getMunger() { + return munger; + } + + public ResolvedType getAspectType() { + return aspectType; + } + + public ResolvedMember getSignature() { + return munger.getSignature(); + } + + public World getWorld() { + return aspectType.getWorld(); + } + + public ISourceLocation getSourceLocation() { + if (munger == null) + return null; + return munger.getSourceLocation(); // XXX + } + + public boolean matches(ResolvedType onType) { + if (munger == null) + throw new RuntimeException("huh: " + this); + return munger.matches(onType, aspectType); + } + + public ResolvedMember getMatchingSyntheticMember(Member member) { + return munger.getMatchingSyntheticMember(member, aspectType); + } + + public int compareTo(Object other) { + ConcreteTypeMunger o = (ConcreteTypeMunger) other; + + ResolvedType otherAspect = o.aspectType; + + if (aspectType.equals(otherAspect)) { + return getSignature().getStart() < o.getSignature().getStart() ? -1 : +1; + } else if (aspectType.isAssignableFrom(o.aspectType)) { + return +1; + } else if (o.aspectType.isAssignableFrom(aspectType)) { + return -1; + } else { + return 0; + } + } + + public int fallbackCompareTo(Object other) { + // ConcreteTypeMunger o = (ConcreteTypeMunger) other; + return 0; + } + + /** + * returns true if the ITD target type used type variables, for example I. When they are specified like this, the ITDs + * 'share' type variables with the generic type. Usually this method is called because we need to know whether to tailor the + * munger for addition to a particular type. For example: + * interface I {} + * + * aspect X implements I { + * List I.foo { return null; } + * } + * In this case the munger matches X but it matches with the form + * List foo() { return null; } + * + */ + public boolean isTargetTypeParameterized() { + if (munger == null) + return false; + return munger.sharesTypeVariablesWithGenericType(); + } + + /** + * For an ITD made on a generic type that shares type variables with that target type, this method will tailor the ITD for a + * particular usage of the generic type - either in its raw or parameterized form. + */ + public abstract ConcreteTypeMunger parameterizedFor(ResolvedType targetType); + + public boolean isLateMunger() { + if (munger == null) + return false; + return munger.isLateMunger(); + } + + public abstract ConcreteTypeMunger parameterizeWith(Map parameterizationMap, World world); + + /** + * Some type mungers are created purely to help with the implementation of shadow mungers. For example to support the cflow() + * pointcut we create a new cflow field in the aspect, and that is added via a BcelCflowCounterFieldAdder. + * + * During compilation we need to compare sets of type mungers, and if some only come into existence after the 'shadowy' type + * things have been processed, we need to ignore them during the comparison. + * + * Returning true from this method indicates the type munger exists to support 'shadowy' stuff - and so can be ignored in some + * comparison. + */ + public boolean existsToSupportShadowMunging() { + if (munger != null) { + return munger.existsToSupportShadowMunging(); + } + return false; + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/Constants.java b/org.aspectj.matcher/src/org/aspectj/weaver/Constants.java new file mode 100644 index 000000000..7c55aab5f --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/Constants.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2004 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 API and implementation + *******************************************************************************/ +package org.aspectj.weaver; + +/** + * Some useful weaver constants. + * + * Current uses: + * 1. Holds values that are necessary for working with 1.5 code but + * which don't exist in a 1.4 world. + */ +public interface Constants { + + public final static int ACC_BRIDGE = 0x0040; + public final static int ACC_VARARGS = 0x0080; + + public final static String RUNTIME_LEVEL_12 = "1.2"; + public final static String RUNTIME_LEVEL_15 = "1.5"; + + // Default for 1.5.0 + public final static String RUNTIME_LEVEL_DEFAULT = RUNTIME_LEVEL_15; +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/CrosscuttingMembers.java b/org.aspectj.matcher/src/org/aspectj/weaver/CrosscuttingMembers.java new file mode 100644 index 000000000..9cdb78a64 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/CrosscuttingMembers.java @@ -0,0 +1,533 @@ +/* ******************************************************************* + * 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.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.aspectj.weaver.patterns.Declare; +import org.aspectj.weaver.patterns.DeclareAnnotation; +import org.aspectj.weaver.patterns.DeclareErrorOrWarning; +import org.aspectj.weaver.patterns.DeclareParents; +import org.aspectj.weaver.patterns.DeclarePrecedence; +import org.aspectj.weaver.patterns.DeclareSoft; +import org.aspectj.weaver.patterns.PerClause; +import org.aspectj.weaver.patterns.Pointcut; +import org.aspectj.weaver.patterns.PointcutRewriter; + +/** + * This holds on to all members that have an invasive effect outside of there + * own compilation unit. These members need to be all gathered up and in a world + * before any weaving can take place. + * + * They are also important in the compilation process and need to be gathered up + * before the inter-type declaration weaving stage (unsurprisingly). + * + * All members are concrete. + * + * @author Jim Hugunin + */ +public class CrosscuttingMembers { + private final ResolvedType inAspect; + private final World world; + + private PerClause perClause; + + private List shadowMungers = new ArrayList(4); + private List typeMungers = new ArrayList(4); + private List lateTypeMungers = new ArrayList(0); + + private List declareParents = new ArrayList(4); + private List declareSofts = new ArrayList(0); + private List declareDominates = new ArrayList(4); + + // These are like declare parents type mungers + private List declareAnnotationsOnType = new ArrayList(); + private List declareAnnotationsOnField = new ArrayList(); + private List declareAnnotationsOnMethods = new ArrayList(); // includes + // ctors + + private boolean shouldConcretizeIfNeeded = true; + + public CrosscuttingMembers(ResolvedType inAspect, boolean shouldConcretizeIfNeeded) { + this.inAspect = inAspect; + world = inAspect.getWorld(); + this.shouldConcretizeIfNeeded = shouldConcretizeIfNeeded; + } + + private final Hashtable cflowFields = new Hashtable(); + private final Hashtable cflowBelowFields = new Hashtable(); + + // public void addConcreteShadowMungers(Collection c) { + // shadowMungers.addAll(c); + // } + + public void addConcreteShadowMunger(ShadowMunger m) { + // assert m is concrete + shadowMungers.add(m); + } + + public void addShadowMungers(Collection c) { + for (Iterator i = c.iterator(); i.hasNext();) { + addShadowMunger((ShadowMunger) i.next()); + } + } + + private void addShadowMunger(ShadowMunger m) { + if (inAspect.isAbstract()) + return; // we don't do mungers for abstract aspects + addConcreteShadowMunger(m.concretize(inAspect, world, perClause)); + } + + public void addTypeMungers(Collection c) { + typeMungers.addAll(c); + } + + public void addTypeMunger(ConcreteTypeMunger m) { + if (m == null) + throw new Error("FIXME AV - should not happen or what ?");// return; + // //??? + typeMungers.add(m); + } + + public void addLateTypeMungers(Collection c) { + lateTypeMungers.addAll(c); + } + + public void addLateTypeMunger(ConcreteTypeMunger m) { + lateTypeMungers.add(m); + } + + public void addDeclares(Collection c) { + for (Iterator i = c.iterator(); i.hasNext();) { + addDeclare((Declare) i.next()); + } + } + + public void addDeclare(Declare declare) { + // this is not extensible, oh well + if (declare instanceof DeclareErrorOrWarning) { + ShadowMunger m = new Checker((DeclareErrorOrWarning) declare); + m.setDeclaringType(declare.getDeclaringType()); + addShadowMunger(m); + } else if (declare instanceof DeclarePrecedence) { + declareDominates.add(declare); + } else if (declare instanceof DeclareParents) { + DeclareParents dp = (DeclareParents) declare; + exposeTypes(dp.getParents().getExactTypes()); + declareParents.add(dp); + } else if (declare instanceof DeclareSoft) { + DeclareSoft d = (DeclareSoft) declare; + // Ordered so that during concretization we can check the related + // munger + ShadowMunger m = Advice.makeSoftener(world, d.getPointcut(), d.getException(), inAspect, d); + m.setDeclaringType(d.getDeclaringType()); + Pointcut concretePointcut = d.getPointcut().concretize(inAspect, d.getDeclaringType(), 0, m); + m.pointcut = concretePointcut; + declareSofts.add(new DeclareSoft(d.getException(), concretePointcut)); + addConcreteShadowMunger(m); + } else if (declare instanceof DeclareAnnotation) { + // FIXME asc perf Possible Improvement. Investigate why this is + // called twice in a weave ? + DeclareAnnotation da = (DeclareAnnotation) declare; + if (da.getAspect() == null) + da.setAspect(inAspect); + if (da.isDeclareAtType()) { + declareAnnotationsOnType.add(da); + } else if (da.isDeclareAtField()) { + declareAnnotationsOnField.add(da); + } else if (da.isDeclareAtMethod() || da.isDeclareAtConstuctor()) { + declareAnnotationsOnMethods.add(da); + } + } else { + throw new RuntimeException("unimplemented"); + } + } + + public void exposeTypes(Collection typesToExpose) { + for (Iterator i = typesToExpose.iterator(); i.hasNext();) { + exposeType((UnresolvedType) i.next()); + } + } + + public void exposeType(UnresolvedType typeToExpose) { + if (ResolvedType.isMissing(typeToExpose)) + return; + if (typeToExpose.isParameterizedType() || typeToExpose.isRawType()) { + if (typeToExpose instanceof ResolvedType) { + typeToExpose = ((ResolvedType) typeToExpose).getGenericType(); + } else { + typeToExpose = UnresolvedType.forSignature(typeToExpose.getErasureSignature()); + } + } + // Check we haven't already got a munger for this: + String signatureToLookFor = typeToExpose.getSignature(); + for (Iterator iterator = typeMungers.iterator(); iterator.hasNext();) { + ConcreteTypeMunger cTM = (ConcreteTypeMunger) iterator.next(); + ResolvedTypeMunger rTM = cTM.getMunger(); + if (rTM != null && rTM instanceof ExposeTypeMunger) { + String exposedType = ((ExposeTypeMunger) rTM).getExposedTypeSignature(); + if (exposedType.equals(signatureToLookFor)) + return; // dont need to bother + } + } + addTypeMunger(world.getWeavingSupport().concreteTypeMunger(new ExposeTypeMunger(typeToExpose), inAspect)); + // ResolvedMember member = new ResolvedMemberImpl( + // Member.STATIC_INITIALIZATION, typeToExpose, 0, ResolvedType.VOID, + // "", UnresolvedType.NONE); + // addTypeMunger(world.concreteTypeMunger( + // new PrivilegedAccessMunger(member), inAspect)); + } + + public void addPrivilegedAccesses(Collection accessedMembers) { + for (Iterator i = accessedMembers.iterator(); i.hasNext();) { + addPrivilegedAccess((ResolvedMember) i.next()); + } + } + + private void addPrivilegedAccess(ResolvedMember member) { + // System.err.println("add priv access: " + member); + addTypeMunger(world.getWeavingSupport().concreteTypeMunger(new PrivilegedAccessMunger(member), inAspect)); + } + + public Collection getCflowEntries() { + ArrayList ret = new ArrayList(); + for (Iterator i = shadowMungers.iterator(); i.hasNext();) { + ShadowMunger m = (ShadowMunger) i.next(); + if (m instanceof Advice) { + Advice a = (Advice) m; + if (a.getKind().isCflow()) { + ret.add(a); + } + } + } + return ret; + } + + /** + * Updates the records if something has changed. This is called at most + * twice, firstly whilst collecting ITDs and declares. At this point the + * CrosscuttingMembers we're comparing ourselves with doesn't know about + * shadowmungers. Therefore a straight comparison with the existing list of + * shadowmungers would return that something has changed even though it + * might not have, so in this first round we ignore the shadowMungers. The + * second time this is called is whilst we're preparing to weave. At this + * point we know everything in the system and so we're able to compare the + * shadowMunger list. (see bug 129163) + * + * @param other + * @param careAboutShadowMungers + * @return true if something has changed since the last time this method was + * called, false otherwise + */ + public boolean replaceWith(CrosscuttingMembers other, boolean careAboutShadowMungers) { + boolean changed = false; + + if (careAboutShadowMungers) { + if (perClause == null || !perClause.equals(other.perClause)) { + changed = true; + perClause = other.perClause; + } + } + + // XXX all of the below should be set equality rather than list equality + // System.err.println("old: " + shadowMungers + " new: " + + // other.shadowMungers); + + if (careAboutShadowMungers) { + // bug 129163: use set equality rather than list equality + Set theseShadowMungers = new HashSet(); + Set theseInlinedAroundMungers = new HashSet(); + for (Iterator iter = shadowMungers.iterator(); iter.hasNext();) { + ShadowMunger munger = (ShadowMunger) iter.next(); + if (munger instanceof Advice) { + Advice adviceMunger = (Advice) munger; + // bug 154054: if we're around advice that has been inlined + // then we need to do more checking than existing equals + // methods allow + if (!world.isXnoInline() && adviceMunger.getKind().equals(AdviceKind.Around)) { + theseInlinedAroundMungers.add(adviceMunger); + } else { + theseShadowMungers.add(adviceMunger); + } + } else { + theseShadowMungers.add(munger); + } + } + Set tempSet = new HashSet(); + tempSet.addAll(other.shadowMungers); + Set otherShadowMungers = new HashSet(); + Set otherInlinedAroundMungers = new HashSet(); + for (Iterator iter = tempSet.iterator(); iter.hasNext();) { + ShadowMunger munger = (ShadowMunger) iter.next(); + if (munger instanceof Advice) { + Advice adviceMunger = (Advice) munger; + // bug 154054: if we're around advice that has been inlined + // then we need to do more checking than existing equals + // methods allow + if (!world.isXnoInline() && adviceMunger.getKind().equals(AdviceKind.Around)) { + otherInlinedAroundMungers.add(rewritePointcutInMunger(adviceMunger)); + } else { + otherShadowMungers.add(rewritePointcutInMunger(adviceMunger)); + } + } else { + otherShadowMungers.add(rewritePointcutInMunger(munger)); + } + } + if (!theseShadowMungers.equals(otherShadowMungers)) { + changed = true; + } + if (!equivalent(theseInlinedAroundMungers, otherInlinedAroundMungers)) { + changed = true; + } + + // bug 158573 - if there are no changes then preserve whether + // or not a particular shadowMunger has matched something. + if (!changed) { + for (Iterator iter = shadowMungers.iterator(); iter.hasNext();) { + ShadowMunger munger = (ShadowMunger) iter.next(); + int i = other.shadowMungers.indexOf(munger); + ShadowMunger otherMunger = (ShadowMunger) other.shadowMungers.get(i); + if (munger instanceof Advice) { + ((Advice) otherMunger).setHasMatchedSomething(((Advice) munger).hasMatchedSomething()); + } + } + } + // replace the existing list of shadowmungers with the + // new ones in case anything like the sourcelocation has + // changed, however, don't want this flagged as a change + // which will force a full build - bug 134541 + shadowMungers = other.shadowMungers; + } + + // bug 129163: use set equality rather than list equality and + // if we dont care about shadow mungers then ignore those + // typeMungers which are created to help with the implementation + // of shadowMungers + Set theseTypeMungers = new HashSet(); + Set otherTypeMungers = new HashSet(); + if (!careAboutShadowMungers) { + for (Iterator iter = typeMungers.iterator(); iter.hasNext();) { + Object o = iter.next(); + if (o instanceof ConcreteTypeMunger) { + ConcreteTypeMunger typeMunger = (ConcreteTypeMunger) o; + if (!typeMunger.existsToSupportShadowMunging()) { + theseTypeMungers.add(typeMunger); + } + } else { + theseTypeMungers.add(o); + } + } + + for (Iterator iter = other.typeMungers.iterator(); iter.hasNext();) { + Object o = iter.next(); + if (o instanceof ConcreteTypeMunger) { + ConcreteTypeMunger typeMunger = (ConcreteTypeMunger) o; + if (!typeMunger.existsToSupportShadowMunging()) { + otherTypeMungers.add(typeMunger); + } + } else { + otherTypeMungers.add(o); + } + } + } else { + theseTypeMungers.addAll(typeMungers); + otherTypeMungers.addAll(other.typeMungers); + } + + // initial go at equivalence logic rather than set compare (see + // pr133532) + // if (theseTypeMungers.size()!=otherTypeMungers.size()) { + // changed = true; + // typeMungers = other.typeMungers; + // } else { + // boolean foundInequality=false; + // for (Iterator iter = theseTypeMungers.iterator(); iter.hasNext() && + // !foundInequality;) { + // Object thisOne = (Object) iter.next(); + // boolean foundInOtherSet = false; + // for (Iterator iterator = otherTypeMungers.iterator(); + // iterator.hasNext();) { + // Object otherOne = (Object) iterator.next(); + // if (thisOne instanceof ConcreteTypeMunger && otherOne instanceof + // ConcreteTypeMunger) { + // if (((ConcreteTypeMunger)thisOne).equivalentTo(otherOne)) { + // foundInOtherSet=true; + // } else if (thisOne.equals(otherOne)) { + // foundInOtherSet=true; + // } + // } else { + // if (thisOne.equals(otherOne)) { + // foundInOtherSet=true; + // } + // } + // } + // if (!foundInOtherSet) foundInequality=true; + // } + // if (foundInequality) { + // changed = true; + // typeMungers = other.typeMungers; + // // } else { + // // typeMungers = other.typeMungers; + // } + // } + if (!theseTypeMungers.equals(otherTypeMungers)) { + changed = true; + typeMungers = other.typeMungers; + } + + if (!lateTypeMungers.equals(other.lateTypeMungers)) { + changed = true; + lateTypeMungers = other.lateTypeMungers; + } + + if (!declareDominates.equals(other.declareDominates)) { + changed = true; + declareDominates = other.declareDominates; + } + + if (!declareParents.equals(other.declareParents)) { + changed = true; + declareParents = other.declareParents; + } + + if (!declareSofts.equals(other.declareSofts)) { + changed = true; + declareSofts = other.declareSofts; + } + + // DECAT for when attempting to replace an aspect + if (!declareAnnotationsOnType.equals(other.declareAnnotationsOnType)) { + changed = true; + declareAnnotationsOnType = other.declareAnnotationsOnType; + } + + if (!declareAnnotationsOnField.equals(other.declareAnnotationsOnField)) { + changed = true; + declareAnnotationsOnField = other.declareAnnotationsOnField; + } + + if (!declareAnnotationsOnMethods.equals(other.declareAnnotationsOnMethods)) { + changed = true; + declareAnnotationsOnMethods = other.declareAnnotationsOnMethods; + } + + return changed; + } + + private boolean equivalent(Set theseInlinedAroundMungers, Set otherInlinedAroundMungers) { + if (theseInlinedAroundMungers.size() != otherInlinedAroundMungers.size()) { + return false; + } + for (Iterator iter = theseInlinedAroundMungers.iterator(); iter.hasNext();) { + Advice thisAdvice = (Advice) iter.next(); + boolean foundIt = false; + for (Iterator iterator = otherInlinedAroundMungers.iterator(); iterator.hasNext();) { + Advice otherAdvice = (Advice) iterator.next(); + if (thisAdvice.equals(otherAdvice)) { + if (thisAdvice.getSignature() instanceof ResolvedMemberImpl) { + if (((ResolvedMemberImpl) thisAdvice.getSignature()).isEquivalentTo(otherAdvice.getSignature())) { + foundIt = true; + continue; + } + } + return false; + } + } + if (!foundIt) { + return false; + } + } + return true; + } + + private ShadowMunger rewritePointcutInMunger(ShadowMunger munger) { + PointcutRewriter pr = new PointcutRewriter(); + Pointcut p = munger.getPointcut(); + Pointcut newP = pr.rewrite(p); + if (p.m_ignoreUnboundBindingForNames.length != 0) {// *sigh* dirty fix + // for dirty hacky + // implementation + // pr149305 + newP.m_ignoreUnboundBindingForNames = p.m_ignoreUnboundBindingForNames; + } + munger.setPointcut(newP); + return munger; + } + + public void setPerClause(PerClause perClause) { + if (shouldConcretizeIfNeeded) { + this.perClause = perClause.concretize(inAspect); + } else { + this.perClause = perClause; + } + } + + public List getDeclareDominates() { + return declareDominates; + } + + public List getDeclareParents() { + return declareParents; + } + + public List getDeclareSofts() { + return declareSofts; + } + + public List getShadowMungers() { + return shadowMungers; + } + + public List getTypeMungers() { + return typeMungers; + } + + public List getLateTypeMungers() { + return lateTypeMungers; + } + + public List getDeclareAnnotationOnTypes() { + return declareAnnotationsOnType; + } + + public List getDeclareAnnotationOnFields() { + return declareAnnotationsOnField; + } + + /** + * includes declare @method and @constructor + */ + public List getDeclareAnnotationOnMethods() { + return declareAnnotationsOnMethods; + } + + public Map getCflowBelowFields() { + return cflowBelowFields; + } + + public Map getCflowFields() { + return cflowFields; + } + + public void clearCaches() { + cflowFields.clear(); + cflowBelowFields.clear(); + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/CrosscuttingMembersSet.java b/org.aspectj.matcher/src/org/aspectj/weaver/CrosscuttingMembersSet.java new file mode 100644 index 000000000..186d7ef6f --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/CrosscuttingMembersSet.java @@ -0,0 +1,305 @@ +/* ******************************************************************* + * 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.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.weaver.patterns.DeclareParents; +import org.aspectj.weaver.patterns.IVerificationRequired; +import org.aspectj.weaver.tools.Trace; +import org.aspectj.weaver.tools.TraceFactory; + +/** + * This holds on to all CrosscuttingMembers for a world. It handles management of change. + * + * @author Jim Hugunin + */ +public class CrosscuttingMembersSet { + // FIXME AV - ? we may need a sequencedHashMap there to ensure source based precedence for @AJ advice + private final Map /* ResolvedType (the aspect) > CrosscuttingMembers */members = new HashMap(); + + private List shadowMungers = null; + private List typeMungers = null; + private List lateTypeMungers = null; + private List declareSofts = null; + private List declareParents = null; + private List declareAnnotationOnTypes = null; + private List declareAnnotationOnFields = null; + private List declareAnnotationOnMethods = null; // includes ctors + private List declareDominates = null; + private boolean changedSinceLastReset = false; + + private List /* IVerificationRequired */verificationList = null; // List of things to be verified once the type system is + // 'complete' + + private static Trace trace = TraceFactory.getTraceFactory().getTrace(CrosscuttingMembersSet.class); + + public CrosscuttingMembersSet(World world) { + trace.enter("", this, world); + + trace.exit(""); + } + + public boolean addOrReplaceAspect(ResolvedType aspectType) { + return addOrReplaceAspect(aspectType, true); + } + + /** + * @return whether or not that was a change to the global signature XXX for efficiency we will need a richer representation than + * this + */ + public boolean addOrReplaceAspect(ResolvedType aspectType, boolean inWeavingPhase) { + trace.enter("addOrReplaceAspect", this, new Object[] { aspectType, new Boolean(inWeavingPhase) }); + + boolean change = false; + CrosscuttingMembers xcut = (CrosscuttingMembers) members.get(aspectType); + if (xcut == null) { + members.put(aspectType, aspectType.collectCrosscuttingMembers(inWeavingPhase)); + clearCaches(); + change = true; + } else { + if (xcut.replaceWith(aspectType.collectCrosscuttingMembers(inWeavingPhase), inWeavingPhase)) { + clearCaches(); + change = true; + } else { + if (inWeavingPhase) { + // bug 134541 - even though we haven't changed we may have updated the + // sourcelocation for the shadowMunger which we need to pick up + shadowMungers = null; + } + change = false; + } + } + if (aspectType.isAbstract()) { + // we might have sub-aspects that need to re-collect their crosscutting members from us + boolean ancestorChange = addOrReplaceDescendantsOf(aspectType, inWeavingPhase); + change = change || ancestorChange; + } + changedSinceLastReset = changedSinceLastReset || change; + + trace.exit("addOrReplaceAspect", change); + return change; + } + + private boolean addOrReplaceDescendantsOf(ResolvedType aspectType, boolean inWeavePhase) { + // System.err.println("Looking at descendants of "+aspectType.getName()); + Set knownAspects = members.keySet(); + Set toBeReplaced = new HashSet(); + for (Iterator it = knownAspects.iterator(); it.hasNext();) { + ResolvedType candidateDescendant = (ResolvedType) it.next(); + if ((candidateDescendant != aspectType) && (aspectType.isAssignableFrom(candidateDescendant))) { + toBeReplaced.add(candidateDescendant); + } + } + boolean change = false; + for (Iterator it = toBeReplaced.iterator(); it.hasNext();) { + ResolvedType next = (ResolvedType) it.next(); + boolean thisChange = addOrReplaceAspect(next, inWeavePhase); + change = change || thisChange; + } + return change; + } + + public void addAdviceLikeDeclares(ResolvedType aspectType) { + CrosscuttingMembers xcut = (CrosscuttingMembers) members.get(aspectType); + xcut.addDeclares(aspectType.collectDeclares(true)); + } + + public boolean deleteAspect(UnresolvedType aspectType) { + boolean isAspect = members.remove(aspectType) != null; + clearCaches(); + return isAspect; + } + + public boolean containsAspect(UnresolvedType aspectType) { + return members.containsKey(aspectType); + } + + // XXX only for testing + public void addFixedCrosscuttingMembers(ResolvedType aspectType) { + members.put(aspectType, aspectType.crosscuttingMembers); + clearCaches(); + } + + private void clearCaches() { + shadowMungers = null; + typeMungers = null; + lateTypeMungers = null; + declareSofts = null; + declareParents = null; + declareAnnotationOnFields = null; + declareAnnotationOnMethods = null; + declareAnnotationOnTypes = null; + declareDominates = null; + } + + public List getShadowMungers() { + if (shadowMungers == null) { + ArrayList ret = new ArrayList(); + for (Iterator i = members.values().iterator(); i.hasNext();) { + ret.addAll(((CrosscuttingMembers) i.next()).getShadowMungers()); + } + shadowMungers = ret; + } + return shadowMungers; + } + + public List getTypeMungers() { + if (typeMungers == null) { + ArrayList ret = new ArrayList(); + for (Iterator i = members.values().iterator(); i.hasNext();) { + ret.addAll(((CrosscuttingMembers) i.next()).getTypeMungers()); + } + typeMungers = ret; + } + return typeMungers; + } + + public List getLateTypeMungers() { + if (lateTypeMungers == null) { + ArrayList ret = new ArrayList(); + for (Iterator i = members.values().iterator(); i.hasNext();) { + ret.addAll(((CrosscuttingMembers) i.next()).getLateTypeMungers()); + } + lateTypeMungers = ret; + } + return lateTypeMungers; + } + + public List getDeclareSofts() { + if (declareSofts == null) { + Set ret = new HashSet(); + for (Iterator i = members.values().iterator(); i.hasNext();) { + ret.addAll(((CrosscuttingMembers) i.next()).getDeclareSofts()); + } + declareSofts = new ArrayList(); + declareSofts.addAll(ret); + } + return declareSofts; + } + + public List getDeclareParents() { + if (declareParents == null) { + Set ret = new HashSet(); + for (Iterator i = members.values().iterator(); i.hasNext();) { + ret.addAll(((CrosscuttingMembers) i.next()).getDeclareParents()); + } + declareParents = new ArrayList(); + declareParents.addAll(ret); + } + return declareParents; + } + + // DECAT Merge multiple together + public List getDeclareAnnotationOnTypes() { + if (declareAnnotationOnTypes == null) { + Set ret = new HashSet(); + for (Iterator i = members.values().iterator(); i.hasNext();) { + ret.addAll(((CrosscuttingMembers) i.next()).getDeclareAnnotationOnTypes()); + } + declareAnnotationOnTypes = new ArrayList(); + declareAnnotationOnTypes.addAll(ret); + } + return declareAnnotationOnTypes; + } + + public List getDeclareAnnotationOnFields() { + if (declareAnnotationOnFields == null) { + Set ret = new HashSet(); + for (Iterator i = members.values().iterator(); i.hasNext();) { + ret.addAll(((CrosscuttingMembers) i.next()).getDeclareAnnotationOnFields()); + } + declareAnnotationOnFields = new ArrayList(); + declareAnnotationOnFields.addAll(ret); + } + return declareAnnotationOnFields; + } + + /** + * Return an amalgamation of the declare @method/@constructor statements. + */ + public List getDeclareAnnotationOnMethods() { + if (declareAnnotationOnMethods == null) { + Set ret = new HashSet(); + for (Iterator i = members.values().iterator(); i.hasNext();) { + ret.addAll(((CrosscuttingMembers) i.next()).getDeclareAnnotationOnMethods()); + } + declareAnnotationOnMethods = new ArrayList(); + declareAnnotationOnMethods.addAll(ret); + } + return declareAnnotationOnMethods; + } + + public List getDeclareDominates() { + if (declareDominates == null) { + ArrayList ret = new ArrayList(); + for (Iterator i = members.values().iterator(); i.hasNext();) { + ret.addAll(((CrosscuttingMembers) i.next()).getDeclareDominates()); + } + declareDominates = ret; + } + return declareDominates; + } + + public ResolvedType findAspectDeclaringParents(DeclareParents p) { + Set keys = this.members.keySet(); + for (Iterator iter = keys.iterator(); iter.hasNext();) { + ResolvedType element = (ResolvedType) iter.next(); + for (Iterator i = ((CrosscuttingMembers) members.get(element)).getDeclareParents().iterator(); i.hasNext();) { + DeclareParents dp = (DeclareParents) i.next(); + if (dp.equals(p)) + return element; + } + } + return null; + } + + public void reset() { + verificationList = null; + changedSinceLastReset = false; + } + + public boolean hasChangedSinceLastReset() { + return changedSinceLastReset; + } + + /** + * Record something that needs verifying when we believe the type system is complete. Used for things that can't be verified as + * we go along - for example some recursive type variable references (pr133307) + */ + public void recordNecessaryCheck(IVerificationRequired verification) { + if (verificationList == null) + verificationList = new ArrayList(); + verificationList.add(verification); + } + + /** + * Called when type bindings are complete - calls all registered verification objects in turn. + */ + public void verify() { + if (verificationList == null) + return; + for (Iterator iter = verificationList.iterator(); iter.hasNext();) { + IVerificationRequired element = (IVerificationRequired) iter.next(); + element.verify(); + } + verificationList = null; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/CustomMungerFactory.java b/org.aspectj.matcher/src/org/aspectj/weaver/CustomMungerFactory.java new file mode 100644 index 000000000..3ff37a423 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/CustomMungerFactory.java @@ -0,0 +1,58 @@ +/* ******************************************************************* + * Copyright (c) 2007 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: + * Linton Ye https://bugs.eclipse.org/bugs/show_bug.cgi?id=193065 + * ******************************************************************/ + +package org.aspectj.weaver; + +import java.util.Collection; + +/** + *

+ * This interface is introduced to support tools like PointcutDoctor. + *

+ *

+ * A CustomMungerFactory is used to create ShadowMungers and/or + * ConcreteTypeMungers so that an extender can extract extra information during + * the weaving process. + *

+ *

+ * A CustomMungerFactory is assigned to a weaver through its AjCompiler in + * extenders' code, and gets invoked by the weaver right before the weaving + * starts. The custom shadow/type mungers being created will be added into the + * shadow/type munger list in the weaver and participate the weaving process. + * For example, the match method of each custom shadow munger will be called + * against each shadow. + *

+ * @author lintonye + * + */ +public interface CustomMungerFactory { + + /** + * @param aspectType + * @return a Collection<ShadowMunger> of custom shadow mungers for the + * given aspect + */ + public Collection/* ShadowMunger */createCustomShadowMungers( + ResolvedType aspectType); + + /** + * @param aspectType + * @return a Collection<ConcreteTypeMunger> of custom type mungers for the + * given aspect + */ + public Collection/* ConcreteTypeMunger */createCustomTypeMungers( + ResolvedType aspectType); + + public Collection/* ShadowMunger */getAllCreatedCustomShadowMungers(); + + public Collection/* ConcreteTypeMunger */getAllCreatedCustomTypeMungers(); +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/Dump.java b/org.aspectj.matcher/src/org/aspectj/weaver/Dump.java new file mode 100644 index 000000000..bdc6ec463 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/Dump.java @@ -0,0 +1,506 @@ +/* ******************************************************************* + * 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 + * ******************************************************************/ +package org.aspectj.weaver; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.PrintStream; +import java.lang.ref.WeakReference; +import java.net.URL; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.WeakHashMap; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.IMessageHolder; +import org.aspectj.bridge.Version; +import org.aspectj.weaver.tools.Trace; +import org.aspectj.weaver.tools.TraceFactory; +import org.aspectj.weaver.tools.Traceable; + +/** + * @author websterm + * + * To change the template for this generated type comment go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +public class Dump { + + public final static String DUMP_CONDITION_PROPERTY = "org.aspectj.weaver.Dump.condition"; + public final static String DUMP_DIRECTORY_PROPERTY = "org.aspectj.dump.directory"; + + /* Format for unique filename based on date & time */ + private static final String FILENAME_PREFIX = "ajcore"; +// private static final DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd"); +// private static final DateFormat timeFormat = new SimpleDateFormat("HHmmss.SSS"); + private static final String FILENAME_SUFFIX = "txt"; + + public static final String UNKNOWN_FILENAME = "Unknown"; + public static final String DUMP_EXCLUDED = "Excluded"; + public static final String NULL_OR_EMPTY = "Empty"; + + private static Class exceptionClass; + private static IMessage.Kind conditionKind = IMessage.ABORT; + private static File directory = new File("."); + + private String reason; + private String fileName; + private PrintStream print; + + private static String[] savedCommandLine; + private static List savedFullClasspath; + private static IMessageHolder savedMessageHolder; + + private static Map nodes = new WeakHashMap(); + private static String lastDumpFileName = UNKNOWN_FILENAME; + + private static boolean preserveOnNextReset = false; + + private static Trace trace = TraceFactory.getTraceFactory().getTrace(Dump.class); + + /** + * for testing only, so that we can verify dump contents + * after compilation has completely finished + */ + public static void preserveOnNextReset() { + preserveOnNextReset = true; + } + + public static void reset() { + if (preserveOnNextReset) { + preserveOnNextReset = false; + return; + } + else { + nodes.clear(); + savedMessageHolder = null; + } + } + + + /* + * Dump methods + */ + public static String dump (String reason) { + String fileName = UNKNOWN_FILENAME; + Dump dump = null; + try { + dump = new Dump(reason); + fileName = dump.getFileName(); + dump.dumpDefault(); + } + finally { + if (dump != null) dump.close(); + } + return fileName; + } + + public static String dumpWithException (Throwable th) { + return dumpWithException(savedMessageHolder,th); + } + + public static String dumpWithException (IMessageHolder messageHolder, Throwable th) { + if (trace.isTraceEnabled()) trace.enter("dumpWithException",null,new Object[] {messageHolder, th}); + + String fileName = UNKNOWN_FILENAME; + Dump dump = null; + try { + dump = new Dump(th.getClass().getName()); + fileName = dump.getFileName(); + dump.dumpException(messageHolder,th); + } + finally { + if (dump != null) dump.close(); + } + + if (trace.isTraceEnabled()) trace.exit("dumpWithException",fileName); + return fileName; + } + + public static String dumpOnExit () { + return dumpOnExit(savedMessageHolder, false); + } + + public static String dumpOnExit (IMessageHolder messageHolder, boolean reset) { + if (trace.isTraceEnabled()) trace.enter("dumpOnExit",null,messageHolder); + String fileName = UNKNOWN_FILENAME; + + if (!shouldDumpOnExit(messageHolder)) { + fileName = DUMP_EXCLUDED; + } + else { + Dump dump = null; + try { + dump = new Dump(conditionKind.toString()); + fileName = dump.getFileName(); + dump.dumpDefault(messageHolder); + } + finally { + if (dump != null) dump.close(); + } + } + + if (reset) messageHolder.clearMessages(); + + if (trace.isTraceEnabled()) trace.exit("dumpOnExit",fileName); + return fileName; + } + + private static boolean shouldDumpOnExit (IMessageHolder messageHolder) { + if (trace.isTraceEnabled()) trace.enter("shouldDumpOnExit",null,messageHolder); + if (trace.isTraceEnabled()) trace.event("shouldDumpOnExit",null,conditionKind); + boolean result = (messageHolder == null) || messageHolder.hasAnyMessage(conditionKind,true); + + if (trace.isTraceEnabled()) trace.exit("shouldDumpOnExit",result); + return result; + } + + /* + * Dump configuration + */ + public static void setDumpOnException (boolean b) { + if (b) { + exceptionClass = java.lang.Throwable.class; + } + else { + exceptionClass = null; + } + } + + public static boolean setDumpDirectory (String directoryName) { + if (trace.isTraceEnabled()) trace.enter("setDumpDirectory",null,directoryName); + boolean success = false; + + File newDirectory = new File(directoryName); + if (newDirectory.exists()) { + directory = newDirectory; + success = true; + } + + if (trace.isTraceEnabled()) trace.exit("setDumpDirectory",success); + return success; + + } + + public static boolean getDumpOnException () { + return (exceptionClass != null); + } + + public static boolean setDumpOnExit (IMessage.Kind condition) { + if (trace.isTraceEnabled()) trace.event("setDumpOnExit",null,condition); + + conditionKind = condition; + return true; + } + + public static boolean setDumpOnExit (String condition) { + for (Iterator i = IMessage.KINDS.iterator(); i.hasNext();) { + IMessage.Kind kind = (IMessage.Kind)i.next(); + if (kind.toString().equals(condition)) { + return setDumpOnExit(kind); + } + } + return false; + } + + public static IMessage.Kind getDumpOnExit () { + return conditionKind; + } + + public static String getLastDumpFileName () { + return lastDumpFileName; + } + + /* + * Dump registration + */ + public static void saveCommandLine (String[] args) { + savedCommandLine = new String[args.length]; + System.arraycopy(args,0,savedCommandLine,0,args.length); + } + + public static void saveFullClasspath (List list) { + savedFullClasspath = list; + } + + public static void saveMessageHolder (IMessageHolder holder) { + savedMessageHolder = holder; + } + + public static void registerNode (Class module, INode newNode) { + if (trace.isTraceEnabled()) trace.enter("registerNode",null,new Object[] { module, newNode} ); + + nodes.put(newNode,new WeakReference(newNode)); + + if (trace.isTraceEnabled()) trace.exit("registerNode",nodes.size()); + } + + /* + * Dump methods + */ + private Dump (String reason) { + if (trace.isTraceEnabled()) trace.enter("",this,reason); + + this.reason = reason; + + openDump(); + dumpAspectJProperties(); + dumpDumpConfiguration(); + + if (trace.isTraceEnabled()) trace.exit("",this); + } + + public String getFileName() { + return fileName; + } + + private void dumpDefault () { + dumpDefault(savedMessageHolder); + } + + private void dumpDefault (IMessageHolder holder) { + dumpSytemProperties(); + dumpCommandLine(); + dumpFullClasspath(); + dumpCompilerMessages(holder); + + dumpNodes(); + } + + private void dumpNodes() { + + /* + * Dump registered nodes + */ + IVisitor dumpVisitor = new IVisitor() { + + public void visitObject (Object obj) { + println(formatObj(obj)); + } + + public void visitList (List list) { + println(list); + } + }; + Set keys = nodes.keySet(); + for (Iterator i = keys.iterator(); i.hasNext();) { + Object module = i.next(); +// INode dumpNode = (INode)nodes.get(module); + INode dumpNode = (INode)module; + println("---- " + formatObj(dumpNode) + " ----"); + try { + dumpNode.accept(dumpVisitor); + } + catch (Exception ex) { + trace.error(formatObj(dumpNode).toString(),ex); + } + } + } + + private void dumpException (IMessageHolder messageHolder, Throwable th) { + println("---- Exception Information ---"); + println(th); + dumpDefault(messageHolder); + } + + private void dumpAspectJProperties () { + println("---- AspectJ Properties ---"); + println("AspectJ Compiler " + Version.text + " built on " + Version.time_text); + } + + private void dumpDumpConfiguration () { + println("---- Dump Properties ---"); + println("Dump file: " + fileName); + println("Dump reason: " + reason); + println("Dump on exception: " + (exceptionClass != null)); + println("Dump at exit condition: " + conditionKind); + } + + private void dumpFullClasspath () { + println("---- Full Classpath ---"); + if (savedFullClasspath != null && savedFullClasspath.size() > 0) { + for (Iterator iter = savedFullClasspath.iterator(); iter.hasNext(); ) { + String fileName = (String)iter.next(); + File file = new File(fileName); + println(file); + } + } + else { + println(NULL_OR_EMPTY); + } + } + + private void dumpSytemProperties () { + println("---- System Properties ---"); + Properties props = System.getProperties(); + println(props); + } + + private void dumpCommandLine () { + println("---- Command Line ---"); + println(savedCommandLine); + } + + private void dumpCompilerMessages (IMessageHolder messageHolder) { + println("---- Compiler Messages ---"); + if (messageHolder != null) for (Iterator i = messageHolder.getUnmodifiableListView().iterator(); i.hasNext(); ) { + IMessage message = (IMessage)i.next(); + println(message.toString()); + } + else { + println(NULL_OR_EMPTY); + } + } + + /* + * Dump output + */ + private void openDump () { + if (print != null) return; + + Date now = new Date(); + fileName = FILENAME_PREFIX + "." + + new SimpleDateFormat("yyyyMMdd").format(now) + "." + + new SimpleDateFormat("HHmmss.SSS").format(now) + "." + + FILENAME_SUFFIX; + try { + File file = new File(directory,fileName); + print = new PrintStream(new FileOutputStream(file),true); + trace.info("Dumping to " + file.getAbsolutePath()); + } + catch (Exception ex) { + print = System.err; + trace.info("Dumping to stderr"); + fileName = UNKNOWN_FILENAME; + } + + lastDumpFileName = fileName; + } + + public void close () { + print.close(); + } + + private void println (Object obj) { + print.println(obj); + } + + private void println (Object[] array) { + if (array == null) { + println(NULL_OR_EMPTY); + return; + } + + for (int i = 0; i < array.length; i++) { + print.println(array[i]); + } + } + + private void println (Properties props) { + Iterator iter = props.keySet().iterator(); + while (iter.hasNext()) { + String key = (String)iter.next(); + String value = props.getProperty(key); + print.println(key + "=" + value); + } + } + + private void println (Throwable th) { + th.printStackTrace(print); + } + + private void println (File file) { + print.print(file.getAbsolutePath()); + if (!file.exists()) { + println("(missing)"); + } + else if (file.isDirectory()) { + int count = file.listFiles().length; + println("(" + count + " entries)"); + } + else { + println("(" + file.length() + " bytes)"); + } + } + + private void println (List list) { + if (list == null || list.isEmpty()) println(NULL_OR_EMPTY); + else for (Iterator i = list.iterator(); i.hasNext();) { + Object o = i.next(); + if (o instanceof Exception) { + println((Exception)o); + } else { + println(o.toString()); + } + } + } + + private static Object formatObj(Object obj) { + + /* These classes have a safe implementation of toString() */ + if (obj == null + || obj instanceof String + || obj instanceof Number + || obj instanceof Boolean + || obj instanceof Exception + || obj instanceof Character + || obj instanceof Class + || obj instanceof File + || obj instanceof StringBuffer + || obj instanceof URL + ) return obj; + else try { + + /* Classes can provide an alternative implementation of toString() */ + if (obj instanceof Traceable) { + Traceable t = (Traceable)obj; + return t.toTraceString(); + } + + /* Use classname@hashcode */ + else return obj.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(obj)); + + /* Object.hashCode() can be override and may thow an exception */ + } catch (Exception ex) { + return obj.getClass().getName() + "@FFFFFFFF"; + } + } + + static { + String exceptionName = System.getProperty("org.aspectj.weaver.Dump.exception","true"); + if (!exceptionName.equals("false")) setDumpOnException(true); + + String conditionName = System.getProperty(DUMP_CONDITION_PROPERTY); + if (conditionName != null) setDumpOnExit(conditionName); + + String directoryName = System.getProperty(DUMP_DIRECTORY_PROPERTY); + if (directoryName != null) setDumpDirectory(directoryName); + } + + public interface INode { + + public void accept (IVisitor visior); + + } + + public interface IVisitor { + + public void visitObject (Object s); + public void visitList (List list); + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/EnumAnnotationValue.java b/org.aspectj.matcher/src/org/aspectj/weaver/EnumAnnotationValue.java new file mode 100644 index 000000000..c29709c18 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/EnumAnnotationValue.java @@ -0,0 +1,37 @@ +/* ******************************************************************* + * 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 IBM initial implementation + * ******************************************************************/ + package org.aspectj.weaver; + +public class EnumAnnotationValue extends AnnotationValue { + + private String type; + private String value; + + public EnumAnnotationValue(String type,String value) { + super(AnnotationValue.ENUM_CONSTANT); + this.type = type; + this.value = value; + } + + public String getType() { + return type; + } + + public String stringify() { + return value; + } + + public String toString() { + return value; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/ExposeTypeMunger.java b/org.aspectj.matcher/src/org/aspectj/weaver/ExposeTypeMunger.java new file mode 100644 index 000000000..486ddbeab --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/ExposeTypeMunger.java @@ -0,0 +1,30 @@ +/* ******************************************************************* + * Copyright (c) 2007 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, IBM initial implementation + * ******************************************************************/ +package org.aspectj.weaver; + +/** + * Special kind of privileged access munger which exposes a type to be public. + */ +public class ExposeTypeMunger extends PrivilegedAccessMunger { + + public ExposeTypeMunger(UnresolvedType typeToExpose) { + super(new ResolvedMemberImpl(Member.STATIC_INITIALIZATION, typeToExpose, 0, ResolvedType.VOID, "", UnresolvedType.NONE)); + } + + public String toString() { + return "ExposeTypeMunger("+getSignature().getDeclaringType().getName()+")"; + } + + public String getExposedTypeSignature() { + return getSignature().getDeclaringType().getSignature(); + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/GeneratedReferenceTypeDelegate.java b/org.aspectj.matcher/src/org/aspectj/weaver/GeneratedReferenceTypeDelegate.java new file mode 100644 index 000000000..88e8c8759 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/GeneratedReferenceTypeDelegate.java @@ -0,0 +1,158 @@ +/* ******************************************************************* + * 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 + * + * ******************************************************************/ +package org.aspectj.weaver; + +import java.util.Collection; + +import org.aspectj.weaver.patterns.PerClause; + +/** + * A delegate that can sit in the ReferenceType instance created for an aspect generated from aop.xml. Only answers the minimal set + * of information required as the type is processed. + * + * @author Andy Clement + */ +public class GeneratedReferenceTypeDelegate extends AbstractReferenceTypeDelegate { + + private ResolvedType superclass; + + public GeneratedReferenceTypeDelegate(ReferenceType backing) { + super(backing, false); + } + + public void addAnnotation(AnnotationAJ annotationX) { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public boolean isAspect() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public boolean isAnnotationStyleAspect() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public boolean isInterface() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public boolean isEnum() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public boolean isAnnotation() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public boolean isAnnotationWithRuntimeRetention() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public boolean isAnonymous() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public boolean isNested() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public ResolvedType getOuterClass() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public String getRetentionPolicy() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public boolean canAnnotationTargetType() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public AnnotationTargetKind[] getAnnotationTargetKinds() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public boolean isGeneric() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public String getDeclaredGenericSignature() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public boolean hasAnnotation(UnresolvedType ofType) { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public AnnotationAJ[] getAnnotations() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public ResolvedType[] getAnnotationTypes() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public ResolvedMember[] getDeclaredFields() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public ResolvedType[] getDeclaredInterfaces() { + return ResolvedType.NONE; + } + + public ResolvedMember[] getDeclaredMethods() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public ResolvedMember[] getDeclaredPointcuts() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public PerClause getPerClause() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public Collection getDeclares() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public Collection getTypeMungers() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public Collection getPrivilegedAccesses() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public int getModifiers() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public void setSuperclass(ResolvedType superclass) { + this.superclass = superclass; + } + + public ResolvedType getSuperclass() { + return this.superclass; + } + + public WeaverStateInfo getWeaverState() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public TypeVariable[] getTypeVariables() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + + public void ensureDelegateConsistent() { + throw new UnsupportedOperationException("Not supported for GeneratedReferenceTypeDelegate"); + } + +} \ No newline at end of file diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/IClassFileProvider.java b/org.aspectj.matcher/src/org/aspectj/weaver/IClassFileProvider.java new file mode 100644 index 000000000..ead81da96 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/IClassFileProvider.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * 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; + +/** + * @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/org.aspectj.matcher/src/org/aspectj/weaver/IClassWeaver.java b/org.aspectj.matcher/src/org/aspectj/weaver/IClassWeaver.java new file mode 100644 index 000000000..679d0fafb --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/IClassWeaver.java @@ -0,0 +1,29 @@ +/* ******************************************************************* + * 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; + +/** + * An IClassWeaver is initialized with a class (a type, really, but let's ignore that for now) + * and a world, and has one method that actually weaves the contents of the world into the class + * implementation. + */ + +public interface IClassWeaver { + + /** perform the weaving. + * + * @return true if the class is changed by the weaving, false otherwise. + */ + boolean weave(); +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/ICrossReferenceHandler.java b/org.aspectj.matcher/src/org/aspectj/weaver/ICrossReferenceHandler.java new file mode 100644 index 000000000..e38338ae1 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/ICrossReferenceHandler.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2000, 2003 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 org.aspectj.bridge.ISourceLocation; + +/** + * Clients can pass a single cross-reference handler to the weaver on construction of a BcelWorld. Any cross-references detected + * during munging will be notified to the handler. + */ +public interface ICrossReferenceHandler { + + void addCrossReference(ISourceLocation from, ISourceLocation to, String kind, boolean runtimeTest); + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/IEclipseSourceContext.java b/org.aspectj.matcher/src/org/aspectj/weaver/IEclipseSourceContext.java new file mode 100644 index 000000000..10d02582d --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/IEclipseSourceContext.java @@ -0,0 +1,16 @@ +/******************************************************************** + * 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 - iniital version + *******************************************************************/ +package org.aspectj.weaver; + + +public interface IEclipseSourceContext extends ISourceContext { + public void removeUnnecessaryProblems(Member method, int problemLineNumber); +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/IHasPosition.java b/org.aspectj.matcher/src/org/aspectj/weaver/IHasPosition.java new file mode 100644 index 000000000..e47e09b04 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/IHasPosition.java @@ -0,0 +1,33 @@ +/* ******************************************************************* + * 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; + +public interface IHasPosition { + /** + * The starting index of this location in the character stream. + */ + int getStart(); + + /** + * The ending index of this location in the character stream + * + * This points to the last character in this token. + * + * If a location truly had no contents, then start == end + 1. We don't recommend this. + */ + int getEnd(); +// +// String getFileName(); + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/IHasSourceLocation.java b/org.aspectj.matcher/src/org/aspectj/weaver/IHasSourceLocation.java new file mode 100644 index 000000000..b070d908b --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/IHasSourceLocation.java @@ -0,0 +1,21 @@ +/* ******************************************************************* + * 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; + +public interface IHasSourceLocation extends IHasPosition { + ISourceContext getSourceContext(); + ISourceLocation getSourceLocation(); +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/ISourceContext.java b/org.aspectj.matcher/src/org/aspectj/weaver/ISourceContext.java new file mode 100644 index 000000000..dfaab8da2 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/ISourceContext.java @@ -0,0 +1,23 @@ +/* ******************************************************************* + * 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; + +public interface ISourceContext { + public ISourceLocation makeSourceLocation(IHasPosition position); + public ISourceLocation makeSourceLocation(int line, int offset); + public int getOffset(); + public void tidy(); +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/IUnwovenClassFile.java b/org.aspectj.matcher/src/org/aspectj/weaver/IUnwovenClassFile.java new file mode 100644 index 000000000..289c20c66 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/IUnwovenClassFile.java @@ -0,0 +1,26 @@ +/* ******************************************************************* + * 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; + +/** + * History: 246125 + * @author Andy Clement + */ +public interface IUnwovenClassFile { + + String getFilename(); + + String getClassName(); + + byte[] getBytes(); + + char[] getClassNameAsChars(); + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/IWeaveRequestor.java b/org.aspectj.matcher/src/org/aspectj/weaver/IWeaveRequestor.java new file mode 100644 index 000000000..13d8b957a --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/IWeaveRequestor.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * 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; + + +/** + * @author colyer + * + * This interface is implemented by clients driving weaving through the IClassFileProvider interface. It is used by the + * weaver to return woven class file results back to the client. The client can correlate weave results with inputs since it + * knows the last UnwovenClassFile returned by its iterator. + */ +public interface IWeaveRequestor { + + /* + * A class file resulting from a weave (yes, even though the type name says "unwoven"...). + */ + void acceptResult(IUnwovenClassFile result); + + // various notifications to the requestor about our progress... + void processingReweavableState(); + + void addingTypeMungers(); + + void weavingAspects(); + + void weavingClasses(); + + void weaveCompleted(); +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/IWeavingSupport.java b/org.aspectj.matcher/src/org/aspectj/weaver/IWeavingSupport.java new file mode 100644 index 000000000..57bf82b7b --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/IWeavingSupport.java @@ -0,0 +1,44 @@ +/* ******************************************************************* + * 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; + +import org.aspectj.weaver.ast.Var; +import org.aspectj.weaver.patterns.PerClause; +import org.aspectj.weaver.patterns.Pointcut; + +/** + * Encapsulates operations that a world will need to support if it is actually going to modify bytecode rather than just match + * against it. {@see BcelWeavingSupport} + * + * @author Andy Clement + */ +public interface IWeavingSupport { + + public Advice createAdviceMunger(AjAttribute.AdviceAttribute attribute, Pointcut pointcut, Member signature); + + public abstract ConcreteTypeMunger makeCflowStackFieldAdder(ResolvedMember cflowField); + + public abstract ConcreteTypeMunger makeCflowCounterFieldAdder(ResolvedMember cflowField); + + /** + * Register a munger for perclause @AJ aspect so that we add aspectOf(..) to them as needed + * + * @see org.aspectj.weaver.bcel.BcelWorld#makePerClauseAspect(ResolvedType, org.aspectj.weaver.patterns.PerClause.Kind) + */ + public abstract ConcreteTypeMunger makePerClauseAspect(ResolvedType aspect, PerClause.Kind kind); + + public abstract ConcreteTypeMunger concreteTypeMunger(ResolvedTypeMunger munger, ResolvedType aspectType); + + public ConcreteTypeMunger createAccessForInlineMunger(ResolvedType inAspect); + + public Var makeCflowAccessVar(ResolvedType formalType, Member cflowField, int arrayIndex); +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/IntMap.java b/org.aspectj.matcher/src/org/aspectj/weaver/IntMap.java new file mode 100644 index 000000000..6f37f20f2 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/IntMap.java @@ -0,0 +1,147 @@ +/* ******************************************************************* + * 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.util.ArrayList; +import java.util.List; + +public class IntMap { + // public static final IntMap EMPTY = new IntMap(0) { + // public boolean directlyInAdvice() { return true; } + // public ShadowMunger getEnclosingAdvice() { return null; } //XXX possible + // }; + + // XXX begin hack to avoid a signature refactoring in Pointcut + private ResolvedType concreteAspect; + private ShadowMunger enclosingAdvice; + private List/* ResolvedPointcutDefinition */enclosingDefinition = new ArrayList(); + + public void pushEnclosingDefinition(ResolvedPointcutDefinition def) { + enclosingDefinition.add(def); + } + + public void popEnclosingDefinitition() { + enclosingDefinition.remove(enclosingDefinition.size() - 1); + } + + public ResolvedPointcutDefinition peekEnclosingDefinition() { + if (enclosingDefinition.size() == 0) + return null; + return (ResolvedPointcutDefinition) enclosingDefinition.get(enclosingDefinition.size() - 1); + } + + public boolean directlyInAdvice() { + return enclosingDefinition.isEmpty(); + } + + public ShadowMunger getEnclosingAdvice() { + return enclosingAdvice; + } + + public void setEnclosingAdvice(ShadowMunger advice) { + this.enclosingAdvice = advice; + } + + public Member getAdviceSignature() { + if (enclosingAdvice instanceof Advice) + return ((Advice) enclosingAdvice).getSignature(); + else + return null; + } + + public ResolvedType getConcreteAspect() { + return concreteAspect; + } + + public void setConcreteAspect(ResolvedType concreteAspect) { + this.concreteAspect = concreteAspect; + } + + public void copyContext(IntMap bindings) { + this.enclosingAdvice = bindings.enclosingAdvice; + this.enclosingDefinition = bindings.enclosingDefinition; + this.concreteAspect = bindings.concreteAspect; + } + + // XXX end hack to avoid a signature refactoring in Pointcut + + private static final int MISSING = -1; + + private int[] map; + + private IntMap(int[] map) { + this.map = map; + } + + public IntMap() { + map = new int[0]; + } + + public IntMap(int initialCapacity) { + map = new int[initialCapacity]; + for (int i = 0; i < initialCapacity; i++) { + map[i] = MISSING; + } + } + + public void put(int key, int val) { + /* assert (val >= 0 && key >= 0) */ + if (key >= map.length) { + int[] tmp = new int[key * 2 + 1]; // ??? better expansion function + System.arraycopy(map, 0, tmp, 0, map.length); + for (int i = map.length, len = tmp.length; i < len; i++) + tmp[i] = MISSING; + map = tmp; + } + map[key] = val; + } + + public int get(int key) { + return map[key]; + } + + public boolean hasKey(int key) { + return (key < map.length && map[key] != MISSING); + } + + // ---- factory methods + + public static IntMap idMap(int size) { + int[] map = new int[size]; + for (int i = 0; i < size; i++) { + map[i] = i; + } + return new IntMap(map); + } + + // ---- from object + + public String toString() { + StringBuffer buf = new StringBuffer("["); + boolean seenFirst = false; + for (int i = 0, len = map.length; i < len; i++) { + if (map[i] != MISSING) { + if (seenFirst) { + buf.append(", "); + } + seenFirst = true; + buf.append(i); + buf.append(" -> "); + buf.append(map[i]); + } + } + buf.append("]"); + return buf.toString(); + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/Iterators.java b/org.aspectj.matcher/src/org/aspectj/weaver/Iterators.java new file mode 100644 index 000000000..1e5e6e6f5 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/Iterators.java @@ -0,0 +1,264 @@ +/* ******************************************************************* + * 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.util.HashSet; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Set; + +public final class Iterators { + + /** + * Private constructor, nobody should ever make one of these + */ + private Iterators() { + } + + /** + * A getter represents a mapping function from Object to Iterator + */ + public interface Getter { + Iterator get(Object target); + } + + /** + * A filter represents a mapping function from Iterator to Iterator + */ + public interface Filter { + Iterator filter(Iterator in); + } + + /** + * Create a new filter F that, when wrapped around another iterator I, creates a new iterator I' that will return only those + * values of I that have not yet been returned by I', discarding duplicates. + */ + public static Filter dupFilter() { + return new Filter() { + final Set seen = new HashSet(); // should have weak ptrs? + + public Iterator filter(final Iterator in) { + return new Iterator() { + boolean fresh = false; + Object peek; + + public boolean hasNext() { + if (fresh) + return true; + while (true) { + if (!in.hasNext()) + return false; + peek = in.next(); + if (!seen.contains(peek)) { + return fresh = true; + } else { + peek = null; // garbage collection + } + } + } + + public Object next() { + if (!hasNext()) + throw new NoSuchElementException(); + Object ret = peek; + peek = null; + fresh = false; + return ret; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }; + } + + /** + * Creates an iterator that will return the elements of a specified array, in order. Like Arrays.asList(o).iterator(), without + * all that pesky safety. + */ + + public static Iterator array(final Object[] o) { + return new Iterator() { + int i = 0; + int len = (o == null) ? 0 : o.length; + + public boolean hasNext() { + return i < len; + } + + public Object next() { + if (i < len) { + return o[i++]; + } else { + throw new NoSuchElementException(); + } + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + /** + * creates an iterator I based on a base iterator A and a getter G. I returns, in order, forall (i in I), G(i). + */ + public static Iterator mapOver(final Iterator a, final Getter g) { + return new Iterator() { + Iterator delegate = new Iterator() { + public boolean hasNext() { + if (!a.hasNext()) + return false; + Object o = a.next(); + delegate = append1(g.get(o), this); + return delegate.hasNext(); + } + + public Object next() { + if (!hasNext()) + throw new UnsupportedOperationException(); + return delegate.next(); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + + public boolean hasNext() { + return delegate.hasNext(); + } + + public Object next() { + return delegate.next(); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + /** + * creates an iterator I based on a base iterator A and a getter G. I returns, in order, forall (i in I) i :: forall (i' in + * g(i)) recur(i', g) + */ + public static Iterator recur(final Object a, final Getter g) { + return new Iterator() { + Iterator delegate = one(a); + + public boolean hasNext() { + return delegate.hasNext(); + } + + public Object next() { + Object next = delegate.next(); + delegate = append(g.get(next), delegate); + return next; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + /** + * creates an iterator I based on base iterators A and B. Returns the elements returned by A followed by those returned by B. If + * B is empty, simply returns A, and if A is empty, simply returns B. Do NOT USE if b.hasNext() is not idempotent. + */ + public static Iterator append(final Iterator a, final Iterator b) { + if (!b.hasNext()) + return a; + return append1(a, b); + } + + /** + * creates an iterator I based on base iterators A and B. Returns the elements returned by A followed by those returned by B. If + * A is empty, simply returns B. Guaranteed not to call B.hasNext() until A is empty. + */ + public static Iterator append1(final Iterator a, final Iterator b) { + if (!a.hasNext()) + return b; + return new Iterator() { + public boolean hasNext() { + return a.hasNext() || b.hasNext(); + } + + public Object next() { + if (a.hasNext()) + return a.next(); + if (b.hasNext()) + return b.next(); + throw new NoSuchElementException(); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + /** + * creates an iterator I based on a base iterator A and an object O. Returns the elements returned by A, followed by O. + */ + public static Iterator snoc(final Iterator first, final Object last) { + return new Iterator() { + Object last1 = last; + + public boolean hasNext() { + return first.hasNext() || last1 != null; + } + + public Object next() { + if (first.hasNext()) + return first.next(); + else if (last1 == null) + throw new NoSuchElementException(); + Object ret = last1; + last1 = null; + return ret; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + /** + * creates an iterator I based on an object O. Returns O, once. + */ + public static Iterator one(final Object it) { + return new Iterator() { + boolean avail = true; + + public boolean hasNext() { + return avail; + } + + public Object next() { + if (!avail) + throw new NoSuchElementException(); + avail = false; + return it; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/JoinPointSignature.java b/org.aspectj.matcher/src/org/aspectj/weaver/JoinPointSignature.java new file mode 100644 index 000000000..399c73115 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/JoinPointSignature.java @@ -0,0 +1,409 @@ +/* ******************************************************************* + * 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.io.DataOutputStream; +import java.io.IOException; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.weaver.AjAttribute.EffectiveSignatureAttribute; + +/** + * @author colyer Instances of this class are created by + * ResolvedMember.getSignatures() when collating all of the signatures + * for a member. We need to create entries in the set for the "gaps" in + * the hierarchy. For example: + * + * class A { void foo(); } + * + * class B extends A {} + * + * Join Point : call(* B.foo()) + * + * has signatures: + * + * B.foo() AND A.foo() B.foo() will be created as a + * ResolvedMemberWithSubstituteDeclaringType + * + * Oh for a JDK 1.4 dynamic proxy.... we have to run on 1.3 :( + */ +public class JoinPointSignature implements ResolvedMember { + + private ResolvedMember realMember; + private ResolvedType substituteDeclaringType; + + public JoinPointSignature(ResolvedMember backing, ResolvedType aType) { + this.realMember = backing; + this.substituteDeclaringType = aType; + } + + public UnresolvedType getDeclaringType() { + return substituteDeclaringType; + } + + public int getModifiers(World world) { + return realMember.getModifiers(world); + } + + public int getModifiers() { + return realMember.getModifiers(); + } + + public UnresolvedType[] getExceptions(World world) { + return realMember.getExceptions(world); + } + + public UnresolvedType[] getExceptions() { + return realMember.getExceptions(); + } + + public ShadowMunger getAssociatedShadowMunger() { + return realMember.getAssociatedShadowMunger(); + } + + public boolean isAjSynthetic() { + return realMember.isAjSynthetic(); + } + + public boolean hasAnnotations() { + return realMember.hasAnnotations(); + } + + public boolean hasAnnotation(UnresolvedType ofType) { + return realMember.hasAnnotation(ofType); + } + + public ResolvedType[] getAnnotationTypes() { + return realMember.getAnnotationTypes(); + } + + public AnnotationAJ getAnnotationOfType(UnresolvedType ofType) { + return realMember.getAnnotationOfType(ofType); + } + + public void setAnnotationTypes(UnresolvedType[] annotationtypes) { + realMember.setAnnotationTypes(annotationtypes); + } + + public void addAnnotation(AnnotationAJ annotation) { + realMember.addAnnotation(annotation); + } + + public boolean isBridgeMethod() { + return realMember.isBridgeMethod(); + } + + public boolean isVarargsMethod() { + return realMember.isVarargsMethod(); + } + + public boolean isSynthetic() { + return realMember.isSynthetic(); + } + + public void write(DataOutputStream s) throws IOException { + realMember.write(s); + } + + public ISourceContext getSourceContext(World world) { + return realMember.getSourceContext(world); + } + + public String[] getParameterNames() { + return realMember.getParameterNames(); + } + + public void setParameterNames(String[] names) { + realMember.setParameterNames(names); + } + + public String[] getParameterNames(World world) { + return realMember.getParameterNames(world); + } + + public EffectiveSignatureAttribute getEffectiveSignature() { + return realMember.getEffectiveSignature(); + } + + public ISourceLocation getSourceLocation() { + return realMember.getSourceLocation(); + } + + public int getEnd() { + return realMember.getEnd(); + } + + public ISourceContext getSourceContext() { + return realMember.getSourceContext(); + } + + public int getStart() { + return realMember.getStart(); + } + + public void setPosition(int sourceStart, int sourceEnd) { + realMember.setPosition(sourceStart, sourceEnd); + } + + public void setSourceContext(ISourceContext sourceContext) { + realMember.setSourceContext(sourceContext); + } + + public boolean isAbstract() { + return realMember.isAbstract(); + } + + public boolean isPublic() { + return realMember.isPublic(); + } + + public boolean isProtected() { + return realMember.isProtected(); + } + + public boolean isNative() { + return realMember.isNative(); + } + + public boolean isDefault() { + return realMember.isDefault(); + } + + public boolean isVisible(ResolvedType fromType) { + return realMember.isVisible(fromType); + } + + public void setCheckedExceptions(UnresolvedType[] checkedExceptions) { + realMember.setCheckedExceptions(checkedExceptions); + } + + public void setAnnotatedElsewhere(boolean b) { + realMember.setAnnotatedElsewhere(b); + } + + public boolean isAnnotatedElsewhere() { + return realMember.isAnnotatedElsewhere(); + } + + public UnresolvedType getGenericReturnType() { + return realMember.getGenericReturnType(); + } + + public UnresolvedType[] getGenericParameterTypes() { + return realMember.getGenericParameterTypes(); + } + + public ResolvedMemberImpl parameterizedWith( + UnresolvedType[] typeParameters, ResolvedType newDeclaringType, + boolean isParameterized) { + return realMember.parameterizedWith(typeParameters, newDeclaringType, + isParameterized); + } + + public ResolvedMemberImpl parameterizedWith( + UnresolvedType[] typeParameters, ResolvedType newDeclaringType, + boolean isParameterized, List aliases) { + return realMember.parameterizedWith(typeParameters, newDeclaringType, + isParameterized, aliases); + } + + public void setTypeVariables(TypeVariable[] types) { + realMember.setTypeVariables(types); + } + + public TypeVariable[] getTypeVariables() { + return realMember.getTypeVariables(); + } + + public TypeVariable getTypeVariableNamed(String name) { + return realMember.getTypeVariableNamed(name); + } + + public boolean matches(ResolvedMember aCandidateMatch) { + return realMember.matches(aCandidateMatch); + } + + public ResolvedMember resolve(World world) { + return realMember.resolve(world); + } + + public int compareTo(Object other) { + return realMember.compareTo(other); + } + + public MemberKind getKind() { + return realMember.getKind(); + } + + public UnresolvedType getReturnType() { + return realMember.getReturnType(); + } + + public UnresolvedType getType() { + return realMember.getType(); + } + + public String getName() { + return realMember.getName(); + } + + public UnresolvedType[] getParameterTypes() { + return realMember.getParameterTypes(); + } + + public AnnotationAJ[][] getParameterAnnotations() { + return realMember.getParameterAnnotations(); + } + + public ResolvedType[][] getParameterAnnotationTypes() { + return realMember.getParameterAnnotationTypes(); + } + + public String getSignature() { + return realMember.getSignature(); + } + + public int getArity() { + return realMember.getArity(); + } + + public String getParameterSignature() { + return realMember.getParameterSignature(); + } + + public boolean isCompatibleWith(Member am) { + return realMember.isCompatibleWith(am); + } + + public boolean isStatic() { + return realMember.isStatic(); + } + + public boolean isInterface() { + return realMember.isInterface(); + } + + public boolean isPrivate() { + return realMember.isPrivate(); + } + + public boolean canBeParameterized() { + return realMember.canBeParameterized(); + } + + public AnnotationAJ[] getAnnotations() { + return realMember.getAnnotations(); + } + + public Collection getDeclaringTypes(World world) { + throw new UnsupportedOperationException( + "Adrian doesn't think you should be calling this..."); + } + + public Iterator getJoinPointSignatures(World world) { + return realMember.getJoinPointSignatures(world); + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append(getReturnType().getName()); + buf.append(' '); + buf.append(getDeclaringType().getName()); + buf.append('.'); + buf.append(getName()); + if (getKind() != FIELD) { + buf.append("("); + UnresolvedType[] parameterTypes = getParameterTypes(); + if (parameterTypes.length != 0) { + buf.append(parameterTypes[0]); + for (int i = 1, len = parameterTypes.length; i < len; i++) { + buf.append(", "); + buf.append(parameterTypes[i].getName()); + } + } + buf.append(")"); + } + return buf.toString(); + } + + public String toGenericString() { + return realMember.toGenericString(); + } + + public String toDebugString() { + return realMember.toDebugString(); + } + + public void resetName(String newName) { + realMember.resetName(newName); + } + + public void resetKind(MemberKind newKind) { + realMember.resetKind(newKind); + } + + public void resetModifiers(int newModifiers) { + realMember.resetModifiers(newModifiers); + } + + public void resetReturnTypeToObjectArray() { + realMember.resetReturnTypeToObjectArray(); + } + + public boolean equals(Object obj) { + if (!(obj instanceof JoinPointSignature)) + return false; + JoinPointSignature other = (JoinPointSignature) obj; + if (!realMember.equals(other.realMember)) + return false; + if (!substituteDeclaringType.equals(other.substituteDeclaringType)) + return false; + return true; + } + + public int hashCode() { + return 17 + (37 * realMember.hashCode()) + + (37 * substituteDeclaringType.hashCode()); + } + + public boolean hasBackingGenericMember() { + return realMember.hasBackingGenericMember(); + } + + public ResolvedMember getBackingGenericMember() { + return realMember.getBackingGenericMember(); + } + + public void evictWeavingState() { + realMember.evictWeavingState(); + } + + public ResolvedMember parameterizedWith(Map m, World w) { + return realMember.parameterizedWith(m, w); + } + + public String getAnnotationDefaultValue() { + return realMember.getAnnotationDefaultValue(); + } + + public String getParameterSignatureErased() { + return realMember.getParameterSignatureErased(); + } + + public String getSignatureErased() { + return realMember.getSignatureErased(); + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/JoinPointSignatureIterator.java b/org.aspectj.matcher/src/org/aspectj/weaver/JoinPointSignatureIterator.java new file mode 100644 index 000000000..cc754d18b --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/JoinPointSignatureIterator.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; + +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Set; + +/** + * Iterates over the signatures of a join point, calculating new signatures + * lazily to minimize processing and to avoid unneccessary "can't find type" + * errors. Iterator can be cached and reused by calling the "reset" method + * between iterations. + */ +public class JoinPointSignatureIterator implements Iterator { + + private Member signaturesOfMember; + private ResolvedMember firstDefiningMember; + ResolvedType firstDefiningType; + private World world; + private List /*JoinPointSignature*/ discoveredSignatures = new ArrayList(); + private List additionalSignatures = Collections.EMPTY_LIST; + private Iterator discoveredSignaturesIterator = null; + private Iterator superTypeIterator = null; + private Set visitedSuperTypes = new HashSet(); + private List /*SearchPair*/ yetToBeProcessedSuperMembers = null;//new ArrayList(); + + private boolean iteratingOverDiscoveredSignatures = true; + private boolean couldBeFurtherAsYetUndiscoveredSignatures = true; + + /** + * + */ + public JoinPointSignatureIterator(Member joinPointSignature, World inAWorld) { + this.signaturesOfMember = joinPointSignature; + this.world = inAWorld; + addSignaturesUpToFirstDefiningMember(); + if (!shouldWalkUpHierarchy()) couldBeFurtherAsYetUndiscoveredSignatures = false; + } + + public void reset() { + discoveredSignaturesIterator = discoveredSignatures.iterator(); + additionalSignatures.clear(); + iteratingOverDiscoveredSignatures = true; + } + + /* (non-Javadoc) + * @see java.util.Iterator#hasNext() + */ + public boolean hasNext() { + if (iteratingOverDiscoveredSignatures && discoveredSignaturesIterator.hasNext()) { + return true; + } else if (couldBeFurtherAsYetUndiscoveredSignatures) { + if (additionalSignatures.size() > 0) return true; + else return findSignaturesFromSupertypes(); + } else { + return false; + } + } + + /* (non-Javadoc) + * @see java.util.Iterator#next() + */ + public Object next() { + if (iteratingOverDiscoveredSignatures && discoveredSignaturesIterator.hasNext()) { + return discoveredSignaturesIterator.next(); + } else { + if (additionalSignatures.size() > 0) { + return additionalSignatures.remove(0); + } + } + throw new NoSuchElementException(); + } + + /* (non-Javadoc) + * @see java.util.Iterator#remove() + */ + public void remove() { + throw new UnsupportedOperationException("can't remove from JoinPointSignatureIterator"); + } + + private void addSignaturesUpToFirstDefiningMember() { + // Walk up hierarchy creating one member for each type up to and including the + // first defining type + ResolvedType originalDeclaringType = signaturesOfMember.getDeclaringType().resolve(world); + + if (world.isJoinpointArrayConstructionEnabled() && originalDeclaringType.isArray()) { // Aha, this must be the array constructor call join point - a 'special'... + Member m = signaturesOfMember; + ResolvedMember rm = new ResolvedMemberImpl(m.getKind(),m.getDeclaringType(),m.getModifiers(),m.getReturnType(),m.getName(),m.getParameterTypes()); + discoveredSignatures.add(new JoinPointSignature(rm,originalDeclaringType)); + couldBeFurtherAsYetUndiscoveredSignatures = false; + return; + } + + firstDefiningMember = signaturesOfMember.resolve(world); + + if (firstDefiningMember == null) { + couldBeFurtherAsYetUndiscoveredSignatures = false; + return; + } + + // declaringType can be unresolved if we matched a synthetic member generated by Aj... + // should be fixed elsewhere but add this resolve call on the end for now so that we can + // focus on one problem at a time... + firstDefiningType = firstDefiningMember.getDeclaringType().resolve(world); + if (firstDefiningType != originalDeclaringType) { + if (signaturesOfMember.getKind() == Member.CONSTRUCTOR) { + return; + } + } + + List declaringTypes = new ArrayList(); + accumulateTypesInBetween(originalDeclaringType, + firstDefiningType, + declaringTypes); + for (Iterator iter = declaringTypes.iterator(); iter.hasNext();) { + ResolvedType declaringType = (ResolvedType) iter.next(); + ResolvedMember member = ((ResolvedMemberImpl)firstDefiningMember).withSubstituteDeclaringType(declaringType); + discoveredSignatures.add(member); + } + } + + /** + * Build a list containing every type between subtype and supertype, inclusively. + */ + private void accumulateTypesInBetween(ResolvedType subType, ResolvedType superType, List types) { + types.add(subType); + if (subType == superType) { + return; + } else { + for (Iterator iter = subType.getDirectSupertypes(); iter.hasNext();) { + ResolvedType parent = (ResolvedType) iter.next(); + if (superType.isAssignableFrom(parent,true)) { + accumulateTypesInBetween(parent, superType,types); + } + } + } + } + + private boolean shouldWalkUpHierarchy() { + if (signaturesOfMember.getKind() == Member.CONSTRUCTOR) return false; + if (signaturesOfMember.getKind() == Member.FIELD) return false; + if (signaturesOfMember.isStatic()) return false; + return true; + } + + private boolean findSignaturesFromSupertypes() { + iteratingOverDiscoveredSignatures = false; + if (superTypeIterator == null) { + superTypeIterator = firstDefiningType.getDirectSupertypes(); + } + if (superTypeIterator.hasNext()) { + ResolvedType superType = (ResolvedType) superTypeIterator.next(); + if (visitedSuperTypes.contains(superType)) { + return findSignaturesFromSupertypes(); + } else { + // we haven't looked in this type yet + visitedSuperTypes.add(superType); + if (superType.isMissing()) { + // issue a warning, stop looking for join point signatures in this line + warnOnMissingType(superType); + return findSignaturesFromSupertypes(); + } + ResolvedMemberImpl foundMember = (ResolvedMemberImpl) superType.lookupResolvedMember(firstDefiningMember,true); + if (foundMember != null && isVisibleTo(firstDefiningMember,foundMember)) { + List declaringTypes = new ArrayList(); + // declaring type can be unresolved if the member can from an ITD... + ResolvedType resolvedDeclaringType = foundMember.getDeclaringType().resolve(world); + accumulateTypesInBetween(superType, resolvedDeclaringType, declaringTypes); + for (Iterator iter = declaringTypes.iterator(); iter.hasNext();) { + ResolvedType declaringType = (ResolvedType) iter.next(); + ResolvedMember member = foundMember.withSubstituteDeclaringType(declaringType); + discoveredSignatures.add(member); // for next time we are reset + if (additionalSignatures==Collections.EMPTY_LIST) additionalSignatures=new ArrayList(); + additionalSignatures.add(member); // for this time + } + // if this was a parameterized type, look in the generic type that backs it too + if (superType.isParameterizedType() && (foundMember.backingGenericMember != null)) { + ResolvedMember member =new JoinPointSignature(foundMember.backingGenericMember,foundMember.declaringType.resolve(world)); + discoveredSignatures.add(member); // for next time we are reset + if (additionalSignatures==Collections.EMPTY_LIST) additionalSignatures=new ArrayList(); + additionalSignatures.add(member); // for this time + } + if (yetToBeProcessedSuperMembers==null) yetToBeProcessedSuperMembers=new ArrayList(); + yetToBeProcessedSuperMembers.add(new SearchPair(foundMember,superType)); + return true; + } else { + return findSignaturesFromSupertypes(); + } + } + } + if (yetToBeProcessedSuperMembers!=null && !yetToBeProcessedSuperMembers.isEmpty()) { + SearchPair nextUp = (SearchPair) yetToBeProcessedSuperMembers.remove(0); + firstDefiningType = nextUp.type; + firstDefiningMember = nextUp.member; + superTypeIterator = null; + return findSignaturesFromSupertypes(); + } + couldBeFurtherAsYetUndiscoveredSignatures = false; + return false; + } + + /** + * Returns true if the parent member is visible to the child member + * In the same declaring type this is always true, otherwise if parent is private + * it is false. + * @param childMember + * @param parentMember + * @return + */ + private boolean isVisibleTo(ResolvedMember childMember, ResolvedMember parentMember) { + if (childMember.getDeclaringType().equals(parentMember.getDeclaringType())) return true; + if (Modifier.isPrivate(parentMember.getModifiers())) { + return false; + } else { + return true; + } + } + + private void warnOnMissingType(ResolvedType missing) { + if (missing instanceof MissingResolvedTypeWithKnownSignature) { + // which it should be... + MissingResolvedTypeWithKnownSignature mrt = (MissingResolvedTypeWithKnownSignature) missing; + mrt.raiseWarningOnJoinPointSignature(signaturesOfMember.toString()); + } + } + + private static class SearchPair { + public ResolvedMember member; + public ResolvedType type; + public SearchPair(ResolvedMember member, ResolvedType type) { + this.member = member; + this.type = type; + } + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/Lint.java b/org.aspectj.matcher/src/org/aspectj/weaver/Lint.java new file mode 100644 index 000000000..162c03e7e --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/Lint.java @@ -0,0 +1,315 @@ +/* ******************************************************************* + * 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 java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.text.MessageFormat; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Properties; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.weaver.tools.Trace; +import org.aspectj.weaver.tools.TraceFactory; + +public class Lint { + /* private */Map kinds = new HashMap(); + /* private */World world; + + public final Kind invalidAbsoluteTypeName = new Kind("invalidAbsoluteTypeName", "no match for this type name: {0}"); + + public final Kind invalidWildcardTypeName = new Kind("invalidWildcardTypeName", "no match for this type pattern: {0}"); + + public final Kind unresolvableMember = new Kind("unresolvableMember", "can not resolve this member: {0}"); + + public final Kind typeNotExposedToWeaver = new Kind("typeNotExposedToWeaver", + "this affected type is not exposed to the weaver: {0}"); + + public final Kind shadowNotInStructure = new Kind("shadowNotInStructure", + "the shadow for this join point is not exposed in the structure model: {0}"); + + public final Kind unmatchedSuperTypeInCall = new Kind("unmatchedSuperTypeInCall", + "does not match because declaring type is {0}, if match desired use target({1})"); + + public final Kind unmatchedTargetKind = new Kind("unmatchedTargetKind", "does not match because annotation {0} has @Target{1}"); + + public final Kind canNotImplementLazyTjp = new Kind("canNotImplementLazyTjp", + "can not implement lazyTjp on this joinpoint {0} because around advice is used"); + + public final Kind multipleAdviceStoppingLazyTjp = new Kind("multipleAdviceStoppingLazyTjp", + "can not implement lazyTjp at joinpoint {0} because of advice conflicts, see secondary locations to find conflicting advice"); + + public final Kind needsSerialVersionUIDField = new Kind("needsSerialVersionUIDField", + "serialVersionUID of type {0} needs to be set because of {1}"); + + public final Kind serialVersionUIDBroken = new Kind("brokeSerialVersionCompatibility", + "serialVersionUID of type {0} is broken because of added field {1}"); + + public final Kind noInterfaceCtorJoinpoint = new Kind("noInterfaceCtorJoinpoint", + "no interface constructor-execution join point - use {0}+ for implementing classes"); + + public final Kind noJoinpointsForBridgeMethods = new Kind( + "noJoinpointsForBridgeMethods", + "pointcut did not match on the method call to a bridge method. Bridge methods are generated by the compiler and have no join points"); + + public final Kind enumAsTargetForDecpIgnored = new Kind("enumAsTargetForDecpIgnored", + "enum type {0} matches a declare parents type pattern but is being ignored"); + + public final Kind annotationAsTargetForDecpIgnored = new Kind("annotationAsTargetForDecpIgnored", + "annotation type {0} matches a declare parents type pattern but is being ignored"); + + public final Kind cantMatchArrayTypeOnVarargs = new Kind("cantMatchArrayTypeOnVarargs", + "an array type as the last parameter in a signature does not match on the varargs declared method: {0}"); + + public final Kind adviceDidNotMatch = new Kind("adviceDidNotMatch", "advice defined in {0} has not been applied"); + + public final Kind invalidTargetForAnnotation = new Kind("invalidTargetForAnnotation", + "{0} is not a valid target for annotation {1}, this annotation can only be applied to {2}"); + + public final Kind elementAlreadyAnnotated = new Kind("elementAlreadyAnnotated", + "{0} - already has an annotation of type {1}, cannot add a second instance"); + + public final Kind runtimeExceptionNotSoftened = new Kind("runtimeExceptionNotSoftened", + "{0} will not be softened as it is already a RuntimeException"); + + public final Kind uncheckedArgument = new Kind("uncheckedArgument", + "unchecked match of {0} with {1} when argument is an instance of {2} at join point {3}"); + + public final Kind uncheckedAdviceConversion = new Kind("uncheckedAdviceConversion", + "unchecked conversion when advice applied at shadow {0}, expected {1} but advice uses {2}"); + + public final Kind noGuardForLazyTjp = new Kind("noGuardForLazyTjp", + "can not build thisJoinPoint lazily for this advice since it has no suitable guard"); + + public final Kind noExplicitConstructorCall = new Kind("noExplicitConstructorCall", + "inter-type constructor does not contain explicit constructor call: field initializers in the target type will not be executed"); + + public final Kind aspectExcludedByConfiguration = new Kind("aspectExcludedByConfiguration", + "aspect {0} exluded for class loader {1}"); + + public final Kind unorderedAdviceAtShadow = new Kind("unorderedAdviceAtShadow", + "at this shadow {0} no precedence is specified between advice applying from aspect {1} and aspect {2}"); + + public final Kind swallowedExceptionInCatchBlock = new Kind("swallowedExceptionInCatchBlock", + "exception swallowed in catch block"); + + public final Kind calculatingSerialVersionUID = new Kind("calculatingSerialVersionUID", + "calculated SerialVersionUID for type {0} to be {1}"); + + // there are a lot of messages in the cant find type family - I'm defining an umbrella lint warning that + // allows a user to control their severity (for e.g. ltw or binary weaving) + public final Kind cantFindType = new Kind("cantFindType", "{0}"); + + public final Kind cantFindTypeAffectingJoinPointMatch = new Kind("cantFindTypeAffectingJPMatch", "{0}"); + + public final Kind advisingSynchronizedMethods = new Kind("advisingSynchronizedMethods", + "advice matching the synchronized method shadow ''{0}'' will be executed outside the lock rather than inside (compiler limitation)"); + + public final Kind mustWeaveXmlDefinedAspects = new Kind( + "mustWeaveXmlDefinedAspects", + "XML Defined aspects must be woven in cases where cflow pointcuts are involved. Currently the include/exclude patterns exclude ''{0}''"); + + /** + * Indicates an aspect could not be found when attempting reweaving. + */ + public final Kind missingAspectForReweaving = new Kind("missingAspectForReweaving", + "aspect {0} cannot be found when reweaving {1}"); + + private static Trace trace = TraceFactory.getTraceFactory().getTrace(Lint.class); + + public Lint(World world) { + if (trace.isTraceEnabled()) + trace.enter("", this, world); + this.world = world; + if (trace.isTraceEnabled()) + trace.exit(""); + } + + public void setAll(String messageKind) { + if (trace.isTraceEnabled()) + trace.enter("setAll", this, messageKind); + setAll(getMessageKind(messageKind)); + if (trace.isTraceEnabled()) + trace.exit("setAll"); + } + + private void setAll(IMessage.Kind messageKind) { + for (Iterator i = kinds.values().iterator(); i.hasNext();) { + Kind kind = (Kind) i.next(); + kind.setKind(messageKind); + } + } + + public void setFromProperties(File file) { + if (trace.isTraceEnabled()) + trace.enter("setFromProperties", this, file); + try { + InputStream s = new FileInputStream(file); + setFromProperties(s); + } catch (IOException ioe) { + MessageUtil.error(world.getMessageHandler(), WeaverMessages.format(WeaverMessages.XLINT_LOAD_ERROR, file.getPath(), ioe + .getMessage())); + } + if (trace.isTraceEnabled()) + trace.exit("setFromProperties"); + } + + public void loadDefaultProperties() { + InputStream s = getClass().getResourceAsStream("XlintDefault.properties"); + if (s == null) { + MessageUtil.warn(world.getMessageHandler(), WeaverMessages.format(WeaverMessages.XLINTDEFAULT_LOAD_ERROR)); + return; + } + try { + setFromProperties(s); + } catch (IOException ioe) { + MessageUtil.error(world.getMessageHandler(), WeaverMessages.format(WeaverMessages.XLINTDEFAULT_LOAD_PROBLEM, ioe + .getMessage())); + } + + } + + private void setFromProperties(InputStream s) throws IOException { + Properties p = new Properties(); + p.load(s); + setFromProperties(p); + } + + public void setFromProperties(Properties properties) { + for (Iterator i = properties.entrySet().iterator(); i.hasNext();) { + Map.Entry entry = (Map.Entry) i.next(); + Kind kind = (Kind) kinds.get(entry.getKey()); + if (kind == null) { + MessageUtil.error(world.getMessageHandler(), WeaverMessages.format(WeaverMessages.XLINT_KEY_ERROR, entry.getKey())); + } else { + kind.setKind(getMessageKind((String) entry.getValue())); + } + } + } + + public Collection allKinds() { + return kinds.values(); + } + + public Kind getLintKind(String name) { + return (Kind) kinds.get(name); + } + + // temporarily suppress the given lint messages + public void suppressKinds(Collection lintKind) { + if (lintKind.isEmpty()) + return; + for (Iterator iter = lintKind.iterator(); iter.hasNext();) { + Kind k = (Kind) iter.next(); + k.setSuppressed(true); + } + } + + // remove any suppression of lint warnings in place + public void clearAllSuppressions() { + for (Iterator iter = kinds.values().iterator(); iter.hasNext();) { + Kind k = (Kind) iter.next(); + k.setSuppressed(false); + } + } + + public void clearSuppressions(Collection lintKind) { + if (lintKind.isEmpty()) + return; + for (Iterator iter = lintKind.iterator(); iter.hasNext();) { + Kind k = (Kind) iter.next(); + k.setSuppressed(false); + } + } + + private IMessage.Kind getMessageKind(String v) { + if (v.equals("ignore")) + return null; + else if (v.equals("warning")) + return IMessage.WARNING; + else if (v.equals("error")) + return IMessage.ERROR; + + MessageUtil.error(world.getMessageHandler(), WeaverMessages.format(WeaverMessages.XLINT_VALUE_ERROR, v)); + return null; + } + + public Kind fromKey(String lintkey) { + return (Lint.Kind) kinds.get(lintkey); + } + + public class Kind { + private final String name; + private final String message; + private IMessage.Kind kind = IMessage.WARNING; + private boolean isSupressed = false; // by SuppressAjWarnings + + public Kind(String name, String message) { + this.name = name; + this.message = message; + kinds.put(this.name, this); + } + + public void setSuppressed(boolean shouldBeSuppressed) { + this.isSupressed = shouldBeSuppressed; + } + + public boolean isEnabled() { + return (kind != null) && !isSupressed(); + } + + private boolean isSupressed() { + // can't suppress errors! + return isSupressed && (kind != IMessage.ERROR); + } + + public String getName() { + return name; + } + + public IMessage.Kind getKind() { + return kind; + } + + public void setKind(IMessage.Kind kind) { + this.kind = kind; + } + + public void signal(String info, ISourceLocation location) { + if (kind == null) + return; + + String text = MessageFormat.format(message, new Object[] { info }); + text += " [Xlint:" + name + "]"; + world.getMessageHandler().handleMessage(new LintMessage(text, kind, location, null, getLintKind(name))); + } + + public void signal(String[] infos, ISourceLocation location, ISourceLocation[] extraLocations) { + if (kind == null) + return; + + String text = MessageFormat.format(message, (Object[]) infos); + text += " [Xlint:" + name + "]"; + world.getMessageHandler().handleMessage(new LintMessage(text, kind, location, extraLocations, getLintKind(name))); + } + + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/LintMessage.java b/org.aspectj.matcher/src/org/aspectj/weaver/LintMessage.java new file mode 100644 index 000000000..3a93af94e --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/LintMessage.java @@ -0,0 +1,47 @@ +/* ******************************************************************* + * Copyright (c) 2002-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: + * PARC initial implementation + * AndyClement extracted as self contained type from Lint type (4-Aug-06) + * ******************************************************************/ +package org.aspectj.weaver; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.Message; + +public class LintMessage extends Message { + + // private Lint.Kind lintKind; + private String lintKind; + + public LintMessage( + String message, + IMessage.Kind messageKind, + ISourceLocation location, + ISourceLocation[] extraLocations, + Lint.Kind lintKind) { + super(message,"",messageKind,location,null,extraLocations); + this.lintKind = lintKind.getName(); + } + + public LintMessage(String message, String extraDetails, org.aspectj.weaver.Lint.Kind kind2, Kind kind, ISourceLocation sourceLocation, Throwable object, + ISourceLocation[] seeAlsoLocations, boolean declared, int id, int sourceStart, int sourceEnd) { + super(message,extraDetails,kind,sourceLocation,object,seeAlsoLocations,declared,id,sourceStart,sourceEnd); + this.lintKind = kind2.getName(); + } + + /** + * @return Returns the Lint kind of this message + */ + public String getLintKind() { + return lintKind; + } + +} \ No newline at end of file diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/Member.java b/org.aspectj.matcher/src/org/aspectj/weaver/Member.java new file mode 100644 index 000000000..04c67e178 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/Member.java @@ -0,0 +1,102 @@ +/* ******************************************************************* + * 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 + * AMC extracted as interface + * ******************************************************************/ +package org.aspectj.weaver; + +import java.util.Collection; +import java.util.Iterator; + +/** + * Abstract representation of a member within a type. + */ +public interface Member extends Comparable { + + public static final Member[] NONE = new Member[0]; + public static final MemberKind METHOD = new MemberKind("METHOD", 1); + public static final MemberKind FIELD = new MemberKind("FIELD", 2); + public static final MemberKind CONSTRUCTOR = new MemberKind("CONSTRUCTOR", + 3); + public static final MemberKind STATIC_INITIALIZATION = new MemberKind( + "STATIC_INITIALIZATION", 4); + public static final MemberKind POINTCUT = new MemberKind("POINTCUT", 5); + public static final MemberKind ADVICE = new MemberKind("ADVICE", 6); + public static final MemberKind HANDLER = new MemberKind("HANDLER", 7); + public static final MemberKind MONITORENTER = new MemberKind( + "MONITORENTER", 8); + public static final MemberKind MONITOREXIT = new MemberKind("MONITOREXIT", + 9); + + public static final AnnotationAJ[][] NO_PARAMETER_ANNOTATIONXS = new AnnotationAJ[][] {}; + public static final ResolvedType[][] NO_PARAMETER_ANNOTATION_TYPES = new ResolvedType[][] {}; + + public MemberKind getKind(); + + public ResolvedMember resolve(World world); + + public int compareTo(Object other); + + public UnresolvedType getDeclaringType(); + + public UnresolvedType getReturnType(); + + public UnresolvedType getGenericReturnType(); + + public UnresolvedType[] getGenericParameterTypes(); + + public UnresolvedType getType(); + + public String getName(); + + public UnresolvedType[] getParameterTypes(); + + /** + * Return full signature, including return type, e.g. "()LFastCar;". For a + * signature without the return type, use getParameterSignature() - it is + * important to choose the right one in the face of covariance. + */ + public String getSignature(); + + public Iterator getJoinPointSignatures(World world); + + public int getArity(); + + /** + * Return signature without return type, e.g. "()" for a signature *with* + * the return type, use getSignature() - it is important to choose the right + * one in the face of covariance. + */ + public String getParameterSignature(); + + public int getModifiers(World world); + + public int getModifiers(); + + public boolean isStatic(); + + public boolean isInterface(); + + public boolean isPrivate(); + + /** + * Returns true iff the member is generic (NOT parameterized) + */ + public boolean canBeParameterized(); + + public AnnotationAJ[] getAnnotations(); + + public Collection/* ResolvedType */getDeclaringTypes(World world); + + public String[] getParameterNames(World world); + + public UnresolvedType[] getExceptions(World world); +} \ No newline at end of file diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/MemberImpl.java b/org.aspectj.matcher/src/org/aspectj/weaver/MemberImpl.java new file mode 100644 index 000000000..f51146f63 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/MemberImpl.java @@ -0,0 +1,565 @@ +/* ******************************************************************* + * 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.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; + +public class MemberImpl implements Member { + + protected MemberKind kind; + protected int modifiers; + protected String name; + protected UnresolvedType returnType; + protected UnresolvedType declaringType; + protected UnresolvedType[] parameterTypes; + private final String signature; + private String paramSignature; + + // OPTIMIZE move out of the member! + private boolean reportedCantFindDeclaringType = false; + private boolean reportedUnresolvableMember = false; + + /** + * All the signatures that a join point with this member as its signature + * has. + */ + private JoinPointSignatureIterator joinPointSignatures = null; + + public MemberImpl(MemberKind kind, UnresolvedType declaringType, + int modifiers, String name, String signature) { + this.kind = kind; + this.declaringType = declaringType; + this.modifiers = modifiers; + this.name = name; + this.signature = signature; + if (kind == FIELD) { + this.returnType = UnresolvedType.forSignature(signature); + this.parameterTypes = UnresolvedType.NONE; + } else { + Object[] returnAndParams = signatureToTypes(signature, false); + this.returnType = (UnresolvedType) returnAndParams[0]; + this.parameterTypes = (UnresolvedType[]) returnAndParams[1]; + // always safe not to do this ?!? + // String oldsig=new String(signature); + // signature = typesToSignature(returnType,parameterTypes,true); + } + } + + public MemberImpl(MemberKind kind, UnresolvedType declaringType, + int modifiers, UnresolvedType returnType, String name, + UnresolvedType[] parameterTypes) { + this.kind = kind; + this.declaringType = declaringType; + this.modifiers = modifiers; + this.returnType = returnType; + this.name = name; + this.parameterTypes = parameterTypes; + if (kind == FIELD) { + this.signature = returnType.getErasureSignature(); + } else { + this.signature = typesToSignature(returnType, parameterTypes, true); + } + } + + public ResolvedMember resolve(World world) { + return world.resolve(this); + } + + // ---- utility methods + + /** + * returns an Object[] pair of UnresolvedType, UnresolvedType[] representing + * return type, argument types parsed from the JVM bytecode signature of a + * method. Yes, this should actually return a nice statically-typed pair + * object, but we don't have one of those. + * + *
+ * + *
+	 *   UnresolvedType.signatureToTypes("()[Z")[0].equals(Type.forSignature("[Z"))
+	 *   UnresolvedType.signatureToTypes("(JJ)I")[1]
+	 *      .equals(UnresolvedType.forSignatures(new String[] {"J", "J"}))
+	 * 
+ * + *
+ * + * @param signature + * the JVM bytecode method signature string we want to break + * apart + * @return a pair of UnresolvedType, UnresolvedType[] representing the + * return types and parameter types. + */ + // OPTIMIZE move static util methods out into a memberutils class + public static String typesToSignature(UnresolvedType returnType, + UnresolvedType[] paramTypes, boolean useRawTypes) { + StringBuffer buf = new StringBuffer(); + buf.append("("); + for (int i = 0, len = paramTypes.length; i < len; i++) { + if (paramTypes[i].isParameterizedType() && useRawTypes) + buf.append(paramTypes[i].getErasureSignature()); + else if (paramTypes[i].isTypeVariableReference() && useRawTypes) + buf.append(paramTypes[i].getErasureSignature()); + else + buf.append(paramTypes[i].getSignature()); + } + buf.append(")"); + if (returnType.isParameterizedType() && useRawTypes) + buf.append(returnType.getErasureSignature()); + else if (returnType.isTypeVariableReference() && useRawTypes) + buf.append(returnType.getErasureSignature()); + else + buf.append(returnType.getSignature()); + return buf.toString(); + } + + /** + * Returns "(,...)" - unlike the other + * typesToSignature that also includes the return type, this one just deals + * with the parameter types. + */ + public static String typesToSignature(UnresolvedType[] paramTypes) { + StringBuffer buf = new StringBuffer(); + buf.append("("); + for (int i = 0; i < paramTypes.length; i++) { + buf.append(paramTypes[i].getSignature()); + } + buf.append(")"); + return buf.toString(); + } + + /** + * returns an Object[] pair of UnresolvedType, UnresolvedType[] representing + * return type, argument types parsed from the JVM bytecode signature of a + * method. Yes, this should actually return a nice statically-typed pair + * object, but we don't have one of those. + * + *
+ * + *
+	 *   UnresolvedType.signatureToTypes("()[Z")[0].equals(Type.forSignature("[Z"))
+	 *   UnresolvedType.signatureToTypes("(JJ)I")[1]
+	 *      .equals(UnresolvedType.forSignatures(new String[] {"J", "J"}))
+	 * 
+ * + *
+ * + * @param signature + * the JVM bytecode method signature string we want to break + * apart + * @return a pair of UnresolvedType, UnresolvedType[] representing the + * return types and parameter types. + */ + private static Object[] signatureToTypes(String sig, + boolean keepParameterizationInfo) { + boolean hasParameters = sig.charAt(1) != ')'; + if (hasParameters) { + List l = new ArrayList(); + int i = 1; + boolean hasAnyAnglies = sig.indexOf('<') != -1; + while (true) { + char c = sig.charAt(i); + if (c == ')') + break; // break out when the hit the ')' + int start = i; + while (c == '[') + c = sig.charAt(++i); + if (c == 'L' || c == 'P') { + int nextSemicolon = sig.indexOf(';', start); + int firstAngly = (hasAnyAnglies ? sig.indexOf('<', start) + : -1); + if (!hasAnyAnglies || firstAngly == -1 + || firstAngly > nextSemicolon) { + i = nextSemicolon + 1; + l.add(UnresolvedType.forSignature(sig.substring(start, + i))); + } else { + // generics generics generics + // Have to skip to the *correct* ';' + boolean endOfSigReached = false; + int posn = firstAngly; + int genericDepth = 0; + while (!endOfSigReached) { + switch (sig.charAt(posn)) { + case '<': + genericDepth++; + break; + case '>': + genericDepth--; + break; + case ';': + if (genericDepth == 0) + endOfSigReached = true; + break; + default: + } + posn++; + } + // posn now points to the correct nextSemicolon :) + i = posn; + l.add(UnresolvedType.forSignature(sig.substring(start, + i))); + } + } else if (c == 'T') { // assumed 'reference' to a type + // variable, so just "Tname;" + int nextSemicolon = sig.indexOf(';', start); + String nextbit = sig.substring(start, nextSemicolon); + l.add(UnresolvedType.forSignature(nextbit)); + i = nextSemicolon + 1; + } else { + i++; + l.add(UnresolvedType.forSignature(sig.substring(start, i))); + } + } + UnresolvedType[] paramTypes = (UnresolvedType[]) l + .toArray(new UnresolvedType[l.size()]); + UnresolvedType returnType = UnresolvedType.forSignature(sig + .substring(i + 1, sig.length())); + return new Object[] { returnType, paramTypes }; + } else { + UnresolvedType returnType = UnresolvedType.forSignature(sig + .substring(2)); + return new Object[] { returnType, UnresolvedType.NONE }; + } + } + + // ---- factory methods + public static MemberImpl field(String declaring, int mods, String name, + String signature) { + return field(declaring, mods, UnresolvedType.forSignature(signature), + name); + } + + // public static Member field(UnresolvedType declaring, int mods, String + // name, UnresolvedType type) { + // return new MemberImpl(FIELD, declaring, mods, type, name, + // UnresolvedType.NONE); + // } + // OPTIMIZE do we need to call this? unless necessary the signatureToTypes() + // call smacks of laziness on the behalf of the caller + // of this method + public static MemberImpl method(UnresolvedType declaring, int mods, + String name, String signature) { + Object[] pair = signatureToTypes(signature, false); + return method(declaring, mods, (UnresolvedType) pair[0], name, + (UnresolvedType[]) pair[1]); + } + + public static MemberImpl monitorEnter() { + return new MemberImpl(MONITORENTER, UnresolvedType.OBJECT, + Modifier.STATIC, ResolvedType.VOID, "", + UnresolvedType.ARRAY_WITH_JUST_OBJECT); + } + + public static MemberImpl monitorExit() { + return new MemberImpl(MONITOREXIT, UnresolvedType.OBJECT, + Modifier.STATIC, ResolvedType.VOID, "", + UnresolvedType.ARRAY_WITH_JUST_OBJECT); + } + + public static Member pointcut(UnresolvedType declaring, String name, + String signature) { + Object[] pair = signatureToTypes(signature, false); + return pointcut(declaring, 0, (UnresolvedType) pair[0], name, + (UnresolvedType[]) pair[1]); + } + + private static MemberImpl field(String declaring, int mods, + UnresolvedType ty, String name) { + return new MemberImpl(FIELD, UnresolvedType.forName(declaring), mods, + ty, name, UnresolvedType.NONE); + } + + public static MemberImpl method(UnresolvedType declTy, int mods, + UnresolvedType rTy, String name, UnresolvedType[] paramTys) { + return new MemberImpl( + // ??? this calls a method + name.equals("") ? CONSTRUCTOR : METHOD, declTy, mods, + rTy, name, paramTys); + } + + private static Member pointcut(UnresolvedType declTy, int mods, + UnresolvedType rTy, String name, UnresolvedType[] paramTys) { + return new MemberImpl(POINTCUT, declTy, mods, rTy, name, paramTys); + } + + public static ResolvedMemberImpl makeExceptionHandlerSignature( + UnresolvedType inType, UnresolvedType catchType) { + return new ResolvedMemberImpl(HANDLER, inType, Modifier.STATIC, + "", "(" + catchType.getSignature() + ")V"); + } + + public boolean equals(Object other) { + if (!(other instanceof Member)) + return false; + Member o = (Member) other; + return (getKind() == o.getKind() && getName().equals(o.getName()) + && getSignature().equals(o.getSignature()) && getDeclaringType() + .equals(o.getDeclaringType())); + } + + /** + * Equality is checked based on the underlying signature, so the hash code + * of a member is based on its kind, name, signature, and declaring type. + * The algorithm for this was taken from page 38 of effective java. + */ + private volatile int hashCode = 0; + + public int hashCode() { + if (hashCode == 0) { + int result = 17; + result = 37 * result + getKind().hashCode(); + result = 37 * result + getName().hashCode(); + result = 37 * result + getSignature().hashCode(); + result = 37 * result + getDeclaringType().hashCode(); + hashCode = result; + } + return hashCode; + } + + public int compareTo(Object other) { + Member o = (Member) other; + int i = getName().compareTo(o.getName()); + if (i != 0) + return i; + return getSignature().compareTo(o.getSignature()); + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append(returnType.getName()); + buf.append(' '); + buf.append(declaringType.getName()); + buf.append('.'); + buf.append(name); + if (kind != FIELD) { + buf.append("("); + if (parameterTypes.length != 0) { + buf.append(parameterTypes[0]); + for (int i = 1, len = parameterTypes.length; i < len; i++) { + buf.append(", "); + buf.append(parameterTypes[i].getName()); + } + } + buf.append(")"); + } + return buf.toString(); + } + + public MemberKind getKind() { + return kind; + } + + public UnresolvedType getDeclaringType() { + return declaringType; + } + + public UnresolvedType getReturnType() { + return returnType; + } + + public UnresolvedType getGenericReturnType() { + return getReturnType(); + } + + public UnresolvedType[] getGenericParameterTypes() { + return getParameterTypes(); + } + + public final UnresolvedType getType() { + return returnType; + } + + public String getName() { + return name; + } + + public UnresolvedType[] getParameterTypes() { + return parameterTypes; + } + + public String getSignature() { + return signature; + } + + public int getArity() { + return parameterTypes.length; + } + + public String getParameterSignature() { + if (paramSignature != null) + return paramSignature; + StringBuffer sb = new StringBuffer(); + sb.append("("); + for (int i = 0; i < parameterTypes.length; i++) { + UnresolvedType tx = parameterTypes[i]; + sb.append(tx.getSignature()); + } + sb.append(")"); + paramSignature = sb.toString(); + return paramSignature; + } + + // OPTIMIZE see next line. Why the hell are they in here if we only know it + // once resolution has occurred... + // ---- things we know only with resolution + + public int getModifiers(World world) { + ResolvedMember resolved = resolve(world); + if (resolved == null) { + reportDidntFindMember(world); + return 0; + } + return resolved.getModifiers(); + } + + public UnresolvedType[] getExceptions(World world) { + ResolvedMember resolved = resolve(world); + if (resolved == null) { + reportDidntFindMember(world); + return UnresolvedType.NONE; + } + return resolved.getExceptions(); + } + + public final boolean isStatic() { + return Modifier.isStatic(modifiers); + } + + public final boolean isInterface() { + return Modifier.isInterface(modifiers); + } + + public final boolean isPrivate() { + return Modifier.isPrivate(modifiers); + } + + public boolean canBeParameterized() { + return false; + } + + public int getModifiers() { + return modifiers; + } + + public AnnotationAJ[] getAnnotations() { + throw new UnsupportedOperationException( + "You should resolve this member '" + this + + "' and call getAnnotations() on the result..."); + } + + // ---- fields 'n' stuff + + public Collection/* ResolvedType */getDeclaringTypes(World world) { + ResolvedType myType = getDeclaringType().resolve(world); + Collection ret = new HashSet(); + if (kind == CONSTRUCTOR) { + // this is wrong if the member doesn't exist, but that doesn't + // matter + ret.add(myType); + } else if (isStatic() || kind == FIELD) { + walkUpStatic(ret, myType); + } else { + walkUp(ret, myType); + } + + return ret; + } + + private boolean walkUp(Collection acc, ResolvedType curr) { + if (acc.contains(curr)) + return true; + + boolean b = false; + for (Iterator i = curr.getDirectSupertypes(); i.hasNext();) { + b |= walkUp(acc, (ResolvedType) i.next()); + } + + if (!b && curr.isParameterizedType()) { + b = walkUp(acc, curr.getGenericType()); + } + + if (!b) { + b = curr.lookupMemberNoSupers(this) != null; + } + if (b) + acc.add(curr); + return b; + } + + private boolean walkUpStatic(Collection acc, ResolvedType curr) { + if (curr.lookupMemberNoSupers(this) != null) { + acc.add(curr); + return true; + } else { + boolean b = false; + for (Iterator i = curr.getDirectSupertypes(); i.hasNext();) { + b |= walkUpStatic(acc, (ResolvedType) i.next()); + } + if (!b && curr.isParameterizedType()) { + b = walkUpStatic(acc, curr.getGenericType()); + } + if (b) + acc.add(curr); + return b; + } + } + + public String[] getParameterNames(World world) { + ResolvedMember resolved = resolve(world); + if (resolved == null) { + reportDidntFindMember(world); + return null; + } + return resolved.getParameterNames(); + } + + /** + * All the signatures that a join point with this member as its signature + * has. + */ + public Iterator getJoinPointSignatures(World inAWorld) { + if (joinPointSignatures == null) { + joinPointSignatures = new JoinPointSignatureIterator(this, inAWorld); + } + joinPointSignatures.reset(); + return joinPointSignatures; + } + + /** + * Raises an [Xlint:cantFindType] message if the declaring type cannot be + * found or an [Xlint:unresolvableMember] message if the type can be found + * (bug 149908) + */ + private void reportDidntFindMember(World world) { + if (reportedCantFindDeclaringType || reportedUnresolvableMember) + return; + ResolvedType rType = getDeclaringType().resolve(world); + if (rType.isMissing()) { + world.getLint().cantFindType.signal(WeaverMessages.format( + WeaverMessages.CANT_FIND_TYPE, rType.getName()), null); + reportedCantFindDeclaringType = true; + } else { + world.getLint().unresolvableMember.signal(getName(), null); + reportedUnresolvableMember = true; + } + } + + public void wipeJoinpointSignatures() { + joinPointSignatures = null; + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/MemberKind.java b/org.aspectj.matcher/src/org/aspectj/weaver/MemberKind.java new file mode 100644 index 000000000..7a5e009bd --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/MemberKind.java @@ -0,0 +1,37 @@ +/* ******************************************************************* + * 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.DataInputStream; +import java.io.IOException; + +import org.aspectj.util.TypeSafeEnum; + +public class MemberKind extends TypeSafeEnum { + public MemberKind(String name, int key) { super(name, key); } + + public static MemberKind read(DataInputStream s) throws IOException { + int key = s.readByte(); + switch(key) { + case 1: return Member.METHOD; + case 2: return Member.FIELD; + case 3: return Member.CONSTRUCTOR; + case 4: return Member.STATIC_INITIALIZATION; + case 5: return Member.POINTCUT; + case 6: return Member.ADVICE; + case 7: return Member.HANDLER; + case 8: return Member.MONITORENTER; + case 9: return Member.MONITOREXIT; + } + throw new BCException("Unexpected memberkind, should be (1-9) but was " + key); + } +} \ No newline at end of file diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/MethodDelegateTypeMunger.java b/org.aspectj.matcher/src/org/aspectj/weaver/MethodDelegateTypeMunger.java new file mode 100644 index 000000000..3ecac9882 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/MethodDelegateTypeMunger.java @@ -0,0 +1,202 @@ +/* ******************************************************************* + * 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; + +import java.io.DataOutputStream; +import java.io.IOException; + +import org.aspectj.weaver.patterns.TypePattern; + +/** + * Type munger for @AspectJ ITD declare parents ie with an interface AND an implementation. + * Given the aspect that has a field public static Interface fieldI = ... // impl. + * we will weave in the Interface' methods and delegate to the aspect public static field fieldI + * + * Note: this munger DOES NOT handles the interface addition to the target classes - a regular Parent kinded munger + * must be added in coordination. + */ +public class MethodDelegateTypeMunger extends ResolvedTypeMunger { + + private final UnresolvedType aspect; + + /** + * The mixin impl (no arg ctor) + */ + private final String implClassName; + + /** + * Type pattern this munger applies to + */ + private final TypePattern typePattern; + + /** + * Construct a new type munger for @AspectJ ITD + * + * @param signature + * @param aspect + * @param implClassName + * @param typePattern + */ + public MethodDelegateTypeMunger(ResolvedMember signature, UnresolvedType aspect, String implClassName, TypePattern typePattern) { + super(MethodDelegate, signature); + this.aspect = aspect; + this.typePattern = typePattern; + this.implClassName = implClassName; + } + + public boolean equals(Object other) { + if (!(other instanceof MethodDelegateTypeMunger)) return false; + MethodDelegateTypeMunger o = (MethodDelegateTypeMunger)other; + return ((o.aspect == null) ? (aspect == null ) : aspect.equals(o.aspect)) + && ((o.typePattern == null) ? (typePattern == null ) : typePattern.equals(o.typePattern)) + && ((o.implClassName == null) ? (implClassName == null) : implClassName.equals(o.implClassName)); + } + + private volatile int hashCode = 0; + public int hashCode() { + if (hashCode == 0) { + int result = 17; + result = 37*result + ((aspect == null) ? 0 : aspect.hashCode()); + result = 37*result + ((typePattern == null) ? 0 : typePattern.hashCode()); + result = 37*result + ((implClassName == null) ? 0 : implClassName.hashCode()); + hashCode = result; + } + return hashCode; + } + + public ResolvedMember getDelegate(ResolvedType targetType) { + return AjcMemberMaker.itdAtDeclareParentsField( + targetType, + signature.getDeclaringType(), + aspect + ); + } + + public String getImplClassName() { + return implClassName; + } + + public void write(DataOutputStream s) throws IOException { + kind.write(s); + signature.write(s); + aspect.write(s); + s.writeUTF(implClassName); + typePattern.write(s); + } + + public static ResolvedTypeMunger readMethod(VersionedDataInputStream s, ISourceContext context) throws IOException { + ResolvedMemberImpl signature = ResolvedMemberImpl.readResolvedMember(s, context); + UnresolvedType aspect = UnresolvedType.read(s); + String implClassName = s.readUTF(); + TypePattern tp = TypePattern.read(s, context); + return new MethodDelegateTypeMunger(signature, aspect, implClassName, tp); + } + + /** + * Match based on given type pattern, only classes can be matched + * + * @param matchType + * @param aspectType + * @return true if match + */ + public boolean matches(ResolvedType matchType, ResolvedType aspectType) { + // match only on class + if (matchType.isEnum() || matchType.isInterface() || matchType.isAnnotation()) { + return false; + } + + return typePattern.matchesStatically(matchType); + } + + /** + * Needed for reweavable + * + * @return true + */ + public boolean changesPublicSignature() { + return true; + } + + public static class FieldHostTypeMunger extends ResolvedTypeMunger { + + private UnresolvedType aspect; + + /** + * Type pattern this munger applies to + */ + private final TypePattern typePattern; + + /** + * Construct a new type munger for @AspectJ ITD + * + * @param field + * @param aspect + * @param typePattern + */ + public FieldHostTypeMunger(ResolvedMember field, UnresolvedType aspect, TypePattern typePattern) { + super(FieldHost, field); + this.aspect = aspect; + this.typePattern = typePattern; + } + + public boolean equals(Object other) { + if (!(other instanceof FieldHostTypeMunger)) return false; + FieldHostTypeMunger o = (FieldHostTypeMunger)other; + return ((o.aspect == null) ? (aspect == null ) : aspect.equals(o.aspect)) + && ((o.typePattern == null) ? (typePattern == null ) : typePattern.equals(o.typePattern)); + } + + public int hashCode() { + int result = 17; + result = 37*result + ((aspect == null) ? 0 : aspect.hashCode()); + result = 37*result + ((typePattern == null) ? 0 : typePattern.hashCode()); + return result; + } + + public void write(DataOutputStream s) throws IOException { + kind.write(s); + signature.write(s); + aspect.write(s); + typePattern.write(s); + } + + public static ResolvedTypeMunger readFieldHost(VersionedDataInputStream s, ISourceContext context) throws IOException { + ResolvedMemberImpl signature = ResolvedMemberImpl.readResolvedMember(s, context); + UnresolvedType aspect = UnresolvedType.read(s); + TypePattern tp = TypePattern.read(s, context); + return new FieldHostTypeMunger(signature, aspect, tp); + } + + /** + * Match based on given type pattern, only classes can be matched + * + * @param matchType + * @param aspectType + * @return true if match + */ + public boolean matches(ResolvedType matchType, ResolvedType aspectType) { + // match only on class + if (matchType.isEnum() || matchType.isInterface() || matchType.isAnnotation()) { + return false; + } + + return typePattern.matchesStatically(matchType); + } + + public boolean changesPublicSignature() { + return false; + } + + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/MissingResolvedTypeWithKnownSignature.java b/org.aspectj.matcher/src/org/aspectj/weaver/MissingResolvedTypeWithKnownSignature.java new file mode 100644 index 000000000..96cda71af --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/MissingResolvedTypeWithKnownSignature.java @@ -0,0 +1,212 @@ +/* ******************************************************************* + * 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.util.Collections; +import java.util.List; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.context.CompilationAndWeavingContext; + +/** + * When we try to resolve a type in the world that we require to be present, + * and then fail to find it, we return an instance of this class. This class + * defers the production of the "can't find type error" until the first time + * that someone asks a question that can't be answered solely from the signature. + * This enables the weaver to be more tolerant of missing types. + * + */ +public class MissingResolvedTypeWithKnownSignature extends ResolvedType { + + private static ResolvedMember[] NO_MEMBERS = new ResolvedMember[0]; + private static ResolvedType[] NO_TYPES = new ResolvedType[0]; + private boolean issuedCantFindTypeError = false; + private boolean issuedJoinPointWarning = false; + private boolean issuedMissingInterfaceWarning = false; + + /** + * @param signature + * @param world + */ + public MissingResolvedTypeWithKnownSignature(String signature, World world) { + super(signature, world); + } + + public boolean isMissing() { return true; } + + /** + * @param signature + * @param signatureErasure + * @param world + */ + public MissingResolvedTypeWithKnownSignature(String signature, + String signatureErasure, World world) { + super(signature, signatureErasure, world); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.ResolvedType#getDeclaredFields() + */ + public ResolvedMember[] getDeclaredFields() { + raiseCantFindType(WeaverMessages.CANT_FIND_TYPE_FIELDS); + return NO_MEMBERS; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.ResolvedType#getDeclaredMethods() + */ + public ResolvedMember[] getDeclaredMethods() { + raiseCantFindType(WeaverMessages.CANT_FIND_TYPE_METHODS); + return NO_MEMBERS; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.ResolvedType#getDeclaredInterfaces() + */ + public ResolvedType[] getDeclaredInterfaces() { + raiseCantFindType(WeaverMessages.CANT_FIND_TYPE_INTERFACES); + return NO_TYPES; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.ResolvedType#getDeclaredPointcuts() + */ + public ResolvedMember[] getDeclaredPointcuts() { + raiseCantFindType(WeaverMessages.CANT_FIND_TYPE_POINTCUTS); + return NO_MEMBERS; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.ResolvedType#getSuperclass() + */ + public ResolvedType getSuperclass() { + raiseCantFindType(WeaverMessages.CANT_FIND_TYPE_SUPERCLASS); + return ResolvedType.MISSING; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.ResolvedType#getModifiers() + */ + public int getModifiers() { + raiseCantFindType(WeaverMessages.CANT_FIND_TYPE_MODIFIERS); + return 0; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.ResolvedType#getSourceContext() + */ + public ISourceContext getSourceContext() { + return new ISourceContext() { + + public ISourceLocation makeSourceLocation(IHasPosition position) { + return null; + } + + public ISourceLocation makeSourceLocation(int line, int offset) { + return null; + } + + public int getOffset() { + return 0; + } + + public void tidy() {} + + }; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.ResolvedType#isAssignableFrom(org.aspectj.weaver.ResolvedType) + */ + public boolean isAssignableFrom(ResolvedType other) { + raiseCantFindType(WeaverMessages.CANT_FIND_TYPE_ASSIGNABLE,other.getName()); + return false; + } + + public boolean isAssignableFrom(ResolvedType other, boolean allowMissing) { + if (allowMissing) return false; + else return isAssignableFrom(other); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.ResolvedType#isCoerceableFrom(org.aspectj.weaver.ResolvedType) + */ + public boolean isCoerceableFrom(ResolvedType other) { + raiseCantFindType(WeaverMessages.CANT_FIND_TYPE_COERCEABLE,other.getName()); + return false; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.AnnotatedElement#hasAnnotation(org.aspectj.weaver.UnresolvedType) + */ + public boolean hasAnnotation(UnresolvedType ofType) { + raiseCantFindType(WeaverMessages.CANT_FIND_TYPE_ANNOTATION); + return false; + } + + public List getInterTypeMungers() { + return Collections.EMPTY_LIST; + } + + public List getInterTypeMungersIncludingSupers() { + return Collections.EMPTY_LIST; + } + + public List getInterTypeParentMungers() { + return Collections.EMPTY_LIST; + } + + public List getInterTypeParentMungersIncludingSupers() { + return Collections.EMPTY_LIST; + } + + protected void collectInterTypeMungers(List collector) { + return; + } + + public void raiseWarningOnJoinPointSignature(String signature) { + if (issuedJoinPointWarning) return; + String message = WeaverMessages.format(WeaverMessages.CANT_FIND_TYPE_JOINPOINT,getName(),signature); + message += "\n" + CompilationAndWeavingContext.getCurrentContext(); + world.getLint().cantFindTypeAffectingJoinPointMatch.signal(message,null); +// MessageUtil.warn(world.getMessageHandler(),message); + issuedJoinPointWarning = true; + } + + public void raiseWarningOnMissingInterfaceWhilstFindingMethods() { + if (issuedMissingInterfaceWarning) return; + String message = WeaverMessages.format(WeaverMessages.CANT_FIND_TYPE_INTERFACE_METHODS,getName(),signature); + message += "\n" + CompilationAndWeavingContext.getCurrentContext(); + world.getLint().cantFindTypeAffectingJoinPointMatch.signal(message,null); + //MessageUtil.warn(world.getMessageHandler(),message); + issuedMissingInterfaceWarning = true; + } + + private void raiseCantFindType(String key) { + if (issuedCantFindTypeError) return; + String message = WeaverMessages.format(key,getName()); + message += "\n" + CompilationAndWeavingContext.getCurrentContext(); + world.getLint().cantFindType.signal(message,null); +// MessageUtil.error(world.getMessageHandler(),message); + issuedCantFindTypeError = true; + } + + private void raiseCantFindType(String key,String insert) { + if (issuedCantFindTypeError) return; + String message = WeaverMessages.format(key,getName(),insert); + message += "\n" + CompilationAndWeavingContext.getCurrentContext(); + world.getLint().cantFindType.signal(message,null); +// MessageUtil.error(world.getMessageHandler(),message); + issuedCantFindTypeError = true; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/NameMangler.java b/org.aspectj.matcher/src/org/aspectj/weaver/NameMangler.java new file mode 100644 index 000000000..5da734a62 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/NameMangler.java @@ -0,0 +1,334 @@ +/* ******************************************************************* + * 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.lang.reflect.Modifier; + +public class NameMangler { + + // public static final char[] AJC_DOLLAR_PREFIX = { 'a', 'j', 'c', '$' }; + // public static final char[] CLINIT = { '<', 'c', 'l', 'i', 'n', 'i', 't', '>' }; + public static final String PREFIX = "ajc$"; + // public static final char[] INIT = { '<', 'i', 'n', 'i', 't', '>' }; + public static final String ITD_PREFIX = PREFIX + "interType$"; + // public static final char[] METHOD_ASPECTOF = {'a', 's', 'p','e','c','t','O','f'}; + // public static final char[] METHOD_HASASPECT = { 'h', 'a', 's', 'A', 's', 'p', 'e', 'c', 't' }; + // public static final char[] STATIC_INITIALIZER = { '<', 'c', 'l', 'i', 'n', 'i', 't', '>' }; + + public static final String CFLOW_STACK_TYPE = "org.aspectj.runtime.internal.CFlowStack"; + public static final String CFLOW_COUNTER_TYPE = "org.aspectj.runtime.internal.CFlowCounter"; + + public static final UnresolvedType CFLOW_STACK_UNRESOLVEDTYPE = UnresolvedType + .forSignature("Lorg/aspectj/runtime/internal/CFlowStack;"); + + public static final UnresolvedType CFLOW_COUNTER_UNRESOLVEDTYPE = UnresolvedType + .forSignature("Lorg/aspectj/runtime/internal/CFlowCounter;"); + + public static final String SOFT_EXCEPTION_TYPE = "org.aspectj.lang.SoftException"; + + public static final String PERSINGLETON_FIELD_NAME = PREFIX + "perSingletonInstance"; + public static final String PERCFLOW_FIELD_NAME = PREFIX + "perCflowStack"; + // public static final String PERTHIS_FIELD_NAME = PREFIX + "perSingletonInstance"; + + // ----- + public static final String PERCFLOW_PUSH_METHOD = PREFIX + "perCflowPush"; + + public static final String PEROBJECT_BIND_METHOD = PREFIX + "perObjectBind"; + + // PTWIMPL Method and field names + public static final String PERTYPEWITHIN_GETINSTANCE_METHOD = PREFIX + "getInstance"; + public static final String PERTYPEWITHIN_CREATEASPECTINSTANCE_METHOD = PREFIX + "createAspectInstance"; + public static final String PERTYPEWITHIN_WITHINTYPEFIELD = PREFIX + "withinType"; + public static final String PERTYPEWITHIN_GETWITHINTYPENAME_METHOD = "getWithinTypeName"; + + public static final String AJC_PRE_CLINIT_NAME = PREFIX + "preClinit"; + + public static final String AJC_POST_CLINIT_NAME = PREFIX + "postClinit"; + + public static final String INITFAILURECAUSE_FIELD_NAME = PREFIX + "initFailureCause"; + + public static boolean isSyntheticMethod(String methodName, boolean declaredInAspect) { + if (methodName.startsWith(PREFIX)) { + // it's synthetic unless it is an advice method + if (methodName.startsWith("ajc$before") || methodName.startsWith("ajc$after")) { + return false; + } else if (methodName.startsWith("ajc$around")) { + // around advice method is not synthetic, but generated proceed is... + return (methodName.endsWith("proceed")); + } else if (methodName.startsWith("ajc$interMethod$")) { + return false; // body of an itd-m + } + return true; + } else if (methodName.indexOf("_aroundBody") != -1) { + return true; + } + // these aren't the droids you're looking for...move along...... pr148727 + // else if (declaredInAspect) { + // if (methodName.equals("aspectOf") || methodName.equals("hasAspect")) { + // return true; + // } + // } + return false; + } + + public static String perObjectInterfaceGet(UnresolvedType aspectType) { + return makeName(aspectType.getNameAsIdentifier(), "perObjectGet"); + } + + public static String perObjectInterfaceSet(UnresolvedType aspectType) { + return makeName(aspectType.getNameAsIdentifier(), "perObjectSet"); + } + + public static String perObjectInterfaceField(UnresolvedType aspectType) { + return makeName(aspectType.getNameAsIdentifier(), "perObjectField"); + } + + // PTWIMPL method names that must include aspect type + public static String perTypeWithinFieldForTarget(UnresolvedType aspectType) { + return makeName(aspectType.getNameAsIdentifier(), "ptwAspectInstance"); + } + + public static String perTypeWithinLocalAspectOf(UnresolvedType aspectType) { + return makeName(aspectType.getNameAsIdentifier(), "localAspectOf"); + } + + public static String itdAtDeclareParentsField(UnresolvedType aspectType, UnresolvedType itdType) { + return makeName("instance", aspectType.getNameAsIdentifier(), itdType.getNameAsIdentifier()); + } + + public static String privilegedAccessMethodForMethod(String name, UnresolvedType objectType, UnresolvedType aspectType) { + return makeName("privMethod", aspectType.getNameAsIdentifier(), objectType.getNameAsIdentifier(), name); + } + + public static String privilegedAccessMethodForFieldGet(String name, UnresolvedType objectType, UnresolvedType aspectType) { + return makeName("privFieldGet", aspectType.getNameAsIdentifier(), objectType.getNameAsIdentifier(), name); + } + + public static String privilegedAccessMethodForFieldSet(String name, UnresolvedType objectType, UnresolvedType aspectType) { + return makeName("privFieldSet", aspectType.getNameAsIdentifier(), objectType.getNameAsIdentifier(), name); + } + + public static String inlineAccessMethodForMethod(String name, UnresolvedType objectType, UnresolvedType aspectType) { + return makeName("inlineAccessMethod", aspectType.getNameAsIdentifier(), objectType.getNameAsIdentifier(), name); + } + + public static String inlineAccessMethodForFieldGet(String name, UnresolvedType objectType, UnresolvedType aspectType) { + return makeName("inlineAccessFieldGet", aspectType.getNameAsIdentifier(), objectType.getNameAsIdentifier(), name); + } + + public static String inlineAccessMethodForFieldSet(String name, UnresolvedType objectType, UnresolvedType aspectType) { + return makeName("inlineAccessFieldSet", aspectType.getNameAsIdentifier(), objectType.getNameAsIdentifier(), name); + } + + /** + * The name of methods corresponding to advice declarations Of the form: + * "ajc$[AdviceKind]$[AspectName]$[NumberOfAdviceInAspect]$[PointcutHash]" + */ + public static String adviceName(String nameAsIdentifier, AdviceKind kind, int adviceSeqNumber, int pcdHash) { + String newname = makeName(kind.getName(), nameAsIdentifier, Integer.toString(adviceSeqNumber), Integer.toHexString(pcdHash)); + return newname; + } + + /** + * This field goes on top-most implementers of the interface the field is declared onto + */ + public static String interFieldInterfaceField(UnresolvedType aspectType, UnresolvedType interfaceType, String name) { + return makeName("interField", aspectType.getNameAsIdentifier(), interfaceType.getNameAsIdentifier(), name); + } + + /** + * This instance method goes on the interface the field is declared onto as well as its top-most implementors + */ + public static String interFieldInterfaceSetter(UnresolvedType aspectType, UnresolvedType interfaceType, String name) { + return makeName("interFieldSet", aspectType.getNameAsIdentifier(), interfaceType.getNameAsIdentifier(), name); + } + + /** + * This instance method goes on the interface the field is declared onto as well as its top-most implementors + */ + public static String interFieldInterfaceGetter(UnresolvedType aspectType, UnresolvedType interfaceType, String name) { + return makeName("interFieldGet", aspectType.getNameAsIdentifier(), interfaceType.getNameAsIdentifier(), name); + } + + /** + * This static method goes on the aspect that declares the inter-type field + */ + public static String interFieldSetDispatcher(UnresolvedType aspectType, UnresolvedType onType, String name) { + return makeName("interFieldSetDispatch", aspectType.getNameAsIdentifier(), onType.getNameAsIdentifier(), name); + } + + /** + * This static method goes on the aspect that declares the inter-type field + */ + public static String interFieldGetDispatcher(UnresolvedType aspectType, UnresolvedType onType, String name) { + return makeName("interFieldGetDispatch", aspectType.getNameAsIdentifier(), onType.getNameAsIdentifier(), name); + } + + /** + * This field goes on the class the field is declared onto + */ + public static String interFieldClassField(int modifiers, UnresolvedType aspectType, UnresolvedType classType, String name) { + if (Modifier.isPublic(modifiers)) + return name; + // ??? might want to handle case where aspect and class are in same package similar to public + return makeName("interField", makeVisibilityName(modifiers, aspectType), name); + } + + // /** + // * This static method goes on the aspect that declares the inter-type field + // */ + // public static String classFieldSetDispatcher(UnresolvedType aspectType, UnresolvedType classType, String name) { + // return makeName("interFieldSetDispatch", aspectType.getNameAsIdentifier(), + // classType.getNameAsIdentifier(), name); + // } + // + // /** + // * This static method goes on the aspect that declares the inter-type field + // */ + // public static String classFieldGetDispatcher(UnresolvedType aspectType, UnresolvedType classType, String name) + // { + // return makeName( + // "interFieldGetDispatch", + // aspectType.getNameAsIdentifier(), + // classType.getNameAsIdentifier(), + // name); + // } + + /** + * This static void method goes on the aspect that declares the inter-type field and is called from the appropriate place + * (target's initializer, or clinit, or topmost implementer's inits), to initialize the field; + */ + + public static String interFieldInitializer(UnresolvedType aspectType, UnresolvedType classType, String name) { + return makeName("interFieldInit", aspectType.getNameAsIdentifier(), classType.getNameAsIdentifier(), name); + } + + // ---- + + /** + * This method goes on the target type of the inter-type method. (and possibly the topmost-implemeters, if the target type is an + * interface) + */ + public static String interMethod(int modifiers, UnresolvedType aspectType, UnresolvedType classType, String name) { + if (Modifier.isPublic(modifiers)) + return name; + // ??? might want to handle case where aspect and class are in same package similar to public + return makeName("interMethodDispatch2", makeVisibilityName(modifiers, aspectType), name); + } + + /** + * This static method goes on the declaring aspect of the inter-type method. + */ + public static String interMethodDispatcher(UnresolvedType aspectType, UnresolvedType classType, String name) { + return makeName("interMethodDispatch1", aspectType.getNameAsIdentifier(), classType.getNameAsIdentifier(), name); + } + + /** + * This static method goes on the declaring aspect of the inter-type method. + */ + public static String interMethodBody(UnresolvedType aspectType, UnresolvedType classType, String name) { + return makeName("interMethod", aspectType.getNameAsIdentifier(), classType.getNameAsIdentifier(), name); + } + + // ---- + + /** + * This static method goes on the declaring aspect of the inter-type constructor. + */ + public static String preIntroducedConstructor(UnresolvedType aspectType, UnresolvedType targetType) { + return makeName("preInterConstructor", aspectType.getNameAsIdentifier(), targetType.getNameAsIdentifier()); + } + + /** + * This static method goes on the declaring aspect of the inter-type constructor. + */ + public static String postIntroducedConstructor(UnresolvedType aspectType, UnresolvedType targetType) { + return makeName("postInterConstructor", aspectType.getNameAsIdentifier(), targetType.getNameAsIdentifier()); + } + + // ---- + + /** + * This static method goes on the target class of the inter-type method. + */ + public static String superDispatchMethod(UnresolvedType classType, String name) { + return makeName("superDispatch", classType.getNameAsIdentifier(), name); + } + + /** + * This static method goes on the target class of the inter-type method. + */ + public static String protectedDispatchMethod(UnresolvedType classType, String name) { + return makeName("protectedDispatch", classType.getNameAsIdentifier(), name); + } + + // ---- + + private static String makeVisibilityName(int modifiers, UnresolvedType aspectType) { + if (Modifier.isPrivate(modifiers)) { + return aspectType.getOutermostType().getNameAsIdentifier(); + } else if (Modifier.isProtected(modifiers)) { + throw new RuntimeException("protected inter-types not allowed"); + } else if (Modifier.isPublic(modifiers)) { + return ""; + } else { + return aspectType.getPackageNameAsIdentifier(); + } + } + + private static String makeName(String s1, String s2) { + return "ajc$" + s1 + "$" + s2; + } + + public static String makeName(String s1, String s2, String s3) { + return "ajc$" + s1 + "$" + s2 + "$" + s3; + } + + public static String makeName(String s1, String s2, String s3, String s4) { + return "ajc$" + s1 + "$" + s2 + "$" + s3 + "$" + s4; + } + + public static String cflowStack(CrosscuttingMembers xcut) { + return makeName("cflowStack", Integer.toHexString(xcut.getCflowEntries().size())); + } + + public static String cflowCounter(CrosscuttingMembers xcut) { + return makeName("cflowCounter", Integer.toHexString(xcut.getCflowEntries().size())); + } + + public static String makeClosureClassName(UnresolvedType enclosingType, int index) { + return enclosingType.getName() + "$AjcClosure" + index; + } + + public static String aroundCallbackMethodName(Member shadowSig, String suffixTag) { + StringBuffer ret = new StringBuffer(); + ret.append(getExtractableName(shadowSig)).append("_aroundBody").append(suffixTag); + return ret.toString(); + } + + public static String getExtractableName(Member shadowSignature) { + String name = shadowSignature.getName(); + MemberKind kind = shadowSignature.getKind(); + if (kind == Member.CONSTRUCTOR) + return "init$"; + else if (kind == Member.STATIC_INITIALIZATION) + return "clinit$"; + else + return name; + } + + public static String proceedMethodName(String adviceMethodName) { + return adviceMethodName + "proceed"; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/NewConstructorTypeMunger.java b/org.aspectj.matcher/src/org/aspectj/weaver/NewConstructorTypeMunger.java new file mode 100644 index 000000000..21a055c3c --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/NewConstructorTypeMunger.java @@ -0,0 +1,144 @@ +/* ******************************************************************* + * 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.DataOutputStream; +import java.io.IOException; +import java.util.List; +import java.util.Set; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.ISourceLocation; + +public class NewConstructorTypeMunger extends ResolvedTypeMunger { + private ResolvedMember syntheticConstructor; + private ResolvedMember explicitConstructor; + + + public NewConstructorTypeMunger( + ResolvedMember signature, + ResolvedMember syntheticConstructor, + ResolvedMember explicitConstructor, + Set superMethodsCalled, + List typeVariableAliases) { + super(Constructor, signature); + this.syntheticConstructor = syntheticConstructor; + this.typeVariableAliases = typeVariableAliases; + this.explicitConstructor = explicitConstructor; + this.setSuperMethodsCalled(superMethodsCalled); + + } + + public boolean equals(Object other) { + if (!(other instanceof NewConstructorTypeMunger)) return false; + NewConstructorTypeMunger o = (NewConstructorTypeMunger)other; + return ((o.syntheticConstructor == null) ? (syntheticConstructor == null ) + : syntheticConstructor.equals(o.syntheticConstructor)) + & ((o.explicitConstructor == null) ? (explicitConstructor == null ) + : explicitConstructor.equals(o.explicitConstructor)); + } + + private volatile int hashCode = 0; + public int hashCode() { + if (hashCode == 0) { + int result = 17; + result = 37*result + ((syntheticConstructor == null) ? 0 : syntheticConstructor.hashCode()); + result = 37*result + ((explicitConstructor == null) ? 0 : explicitConstructor.hashCode()); + hashCode = result; + } + return hashCode; + } + + // doesnt seem required.... +// public ResolvedMember getDispatchMethod(UnresolvedType aspectType) { +// return AjcMemberMaker.interMethodBody(signature, aspectType); +// } + + public void write(DataOutputStream s) throws IOException { + kind.write(s); + signature.write(s); + syntheticConstructor.write(s); + explicitConstructor.write(s); + writeSuperMethodsCalled(s); + writeSourceLocation(s); + writeOutTypeAliases(s); + } + + public static ResolvedTypeMunger readConstructor(VersionedDataInputStream s, ISourceContext context) throws IOException { + ISourceLocation sloc = null; + ResolvedMember sig = ResolvedMemberImpl.readResolvedMember(s, context); + ResolvedMember syntheticCtor = ResolvedMemberImpl.readResolvedMember(s, context); + ResolvedMember explicitCtor = ResolvedMemberImpl.readResolvedMember(s, context); + Set superMethodsCalled = readSuperMethodsCalled(s); + sloc = readSourceLocation(s); + List typeVarAliases = readInTypeAliases(s); + ResolvedTypeMunger munger = new NewConstructorTypeMunger(sig,syntheticCtor,explicitCtor,superMethodsCalled,typeVarAliases); + if (sloc!=null) munger.setSourceLocation(sloc); + return munger; + } + + public ResolvedMember getExplicitConstructor() { + return explicitConstructor; + } + + public ResolvedMember getSyntheticConstructor() { + return syntheticConstructor; + } + + public void setExplicitConstructor(ResolvedMember explicitConstructor) { + this.explicitConstructor = explicitConstructor; + // reset hashCode so that its recalculated with new value + hashCode = 0; + } + + public ResolvedMember getMatchingSyntheticMember(Member member, ResolvedType aspectType) { + ResolvedMember ret = getSyntheticConstructor(); + if (ResolvedType.matches(ret, member)) return getSignature(); + return super.getMatchingSyntheticMember(member, aspectType); + } + + public void check(World world) { + if (getSignature().getDeclaringType().resolve(world).isAspect()) { + world.showMessage(IMessage.ERROR, + WeaverMessages.format(WeaverMessages.ITD_CONS_ON_ASPECT), + getSignature().getSourceLocation(), null); + } + } + + /** + * see ResolvedTypeMunger.parameterizedFor(ResolvedType) + */ + public ResolvedTypeMunger parameterizedFor(ResolvedType target) { + ResolvedType genericType = target; + if (target.isRawType() || target.isParameterizedType()) genericType = genericType.getGenericType(); + ResolvedMember parameterizedSignature = null; + // If we are parameterizing it for a generic type, we just need to 'swap the letters' from the ones used + // in the original ITD declaration to the ones used in the actual target type declaration. + if (target.isGenericType()) { + TypeVariable vars[] = target.getTypeVariables(); + UnresolvedTypeVariableReferenceType[] varRefs = new UnresolvedTypeVariableReferenceType[vars.length]; + for (int i = 0; i < vars.length; i++) { + varRefs[i] = new UnresolvedTypeVariableReferenceType(vars[i]); + } + parameterizedSignature = getSignature().parameterizedWith(varRefs,genericType,true,typeVariableAliases); + } else { + // For raw and 'normal' parameterized targets (e.g. Interface, Interface) + parameterizedSignature = getSignature().parameterizedWith(target.getTypeParameters(),genericType,target.isParameterizedType(),typeVariableAliases); + } + NewConstructorTypeMunger nctm = new NewConstructorTypeMunger(parameterizedSignature,syntheticConstructor,explicitConstructor,getSuperMethodsCalled(),typeVariableAliases); + nctm.setSourceLocation(getSourceLocation()); + return nctm; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/NewFieldTypeMunger.java b/org.aspectj.matcher/src/org/aspectj/weaver/NewFieldTypeMunger.java new file mode 100644 index 000000000..19ed670ab --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/NewFieldTypeMunger.java @@ -0,0 +1,124 @@ +/* ******************************************************************* + * 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.DataOutputStream; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.aspectj.bridge.ISourceLocation; + +public class NewFieldTypeMunger extends ResolvedTypeMunger { + + public NewFieldTypeMunger(ResolvedMember signature, Set superMethodsCalled, List typeVariableAliases) { + super(Field, signature); + this.typeVariableAliases = typeVariableAliases; + signature.setAnnotatedElsewhere(true); + this.setSuperMethodsCalled(superMethodsCalled); + } + + public ResolvedMember getInitMethod(UnresolvedType aspectType) { + return AjcMemberMaker.interFieldInitializer(signature, aspectType); + } + + public void write(DataOutputStream s) throws IOException { + kind.write(s); + signature.write(s); + writeSuperMethodsCalled(s); + writeSourceLocation(s); + writeOutTypeAliases(s); + } + + public static ResolvedTypeMunger readField(VersionedDataInputStream s, ISourceContext context) throws IOException { + ISourceLocation sloc = null; + ResolvedMember fieldSignature = ResolvedMemberImpl.readResolvedMember(s, context); + Set superMethodsCalled = readSuperMethodsCalled(s); + sloc = readSourceLocation(s); + List aliases = readInTypeAliases(s); + ResolvedTypeMunger munger = new NewFieldTypeMunger(fieldSignature,superMethodsCalled,aliases); + if (sloc!=null) munger.setSourceLocation(sloc); + return munger; + } + + public ResolvedMember getMatchingSyntheticMember(Member member, ResolvedType aspectType) { + //??? might give a field where a method is expected + ResolvedType onType = aspectType.getWorld().resolve(getSignature().getDeclaringType()); + if (onType.isRawType()) onType = onType.getGenericType(); + + ResolvedMember ret = AjcMemberMaker.interFieldGetDispatcher(getSignature(), aspectType); + if (ResolvedType.matches(ret, member)) return getSignature(); + ret = AjcMemberMaker.interFieldSetDispatcher(getSignature(), aspectType); + if (ResolvedType.matches(ret, member)) return getSignature(); + ret = AjcMemberMaker.interFieldInterfaceGetter(getSignature(), onType, aspectType); + if (ResolvedType.matches(ret, member)) return getSignature(); + ret = AjcMemberMaker.interFieldInterfaceSetter(getSignature(), onType, aspectType); + if (ResolvedType.matches(ret, member)) return getSignature(); + return super.getMatchingSyntheticMember(member, aspectType); + } + + /** + * see ResolvedTypeMunger.parameterizedFor(ResolvedType) + */ + public ResolvedTypeMunger parameterizedFor(ResolvedType target) { + ResolvedType genericType = target; + if (target.isRawType() || target.isParameterizedType()) genericType = genericType.getGenericType(); + ResolvedMember parameterizedSignature = null; + // If we are parameterizing it for a generic type, we just need to 'swap the letters' from the ones used + // in the original ITD declaration to the ones used in the actual target type declaration. + if (target.isGenericType()) { + TypeVariable vars[] = target.getTypeVariables(); + UnresolvedTypeVariableReferenceType[] varRefs = new UnresolvedTypeVariableReferenceType[vars.length]; + for (int i = 0; i < vars.length; i++) { + varRefs[i] = new UnresolvedTypeVariableReferenceType(vars[i]); + } + parameterizedSignature = getSignature().parameterizedWith(varRefs,genericType,true,typeVariableAliases); + } else { + // For raw and 'normal' parameterized targets (e.g. Interface, Interface) + parameterizedSignature = getSignature().parameterizedWith(target.getTypeParameters(),genericType,target.isParameterizedType(),typeVariableAliases); + } + NewFieldTypeMunger nftm = new NewFieldTypeMunger(parameterizedSignature,getSuperMethodsCalled(),typeVariableAliases); + nftm.setDeclaredSignature(getSignature()); + nftm.setSourceLocation(getSourceLocation()); + return nftm; + } + + public ResolvedTypeMunger parameterizeWith(Map m, World w) { + ResolvedMember parameterizedSignature = getSignature().parameterizedWith(m,w); + NewFieldTypeMunger nftm = new NewFieldTypeMunger(parameterizedSignature,getSuperMethodsCalled(),typeVariableAliases); + nftm.setDeclaredSignature(getSignature()); + nftm.setSourceLocation(getSourceLocation()); + return nftm; + } + + public boolean equals(Object other) { + if (! (other instanceof NewFieldTypeMunger)) return false; + NewFieldTypeMunger o = (NewFieldTypeMunger) other; + return kind.equals(o.kind) + && ((o.signature == null) ? (signature == null ) : signature.equals(o.signature)) + && ((o.declaredSignature == null) ? (declaredSignature == null ) : declaredSignature.equals(o.declaredSignature)) + && ((o.typeVariableAliases == null) ? (typeVariableAliases == null ) : typeVariableAliases.equals(o.typeVariableAliases)); + } + + public int hashCode() { + int result = 17; + result = 37*result + kind.hashCode(); + result = 37*result + ((signature == null) ? 0 : signature.hashCode()); + result = 37*result + ((declaredSignature == null) ? 0 : declaredSignature.hashCode()); + result = 37*result + ((typeVariableAliases == null) ? 0 : typeVariableAliases.hashCode()); + return result; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/NewMethodTypeMunger.java b/org.aspectj.matcher/src/org/aspectj/weaver/NewMethodTypeMunger.java new file mode 100644 index 000000000..2fa3a57e2 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/NewMethodTypeMunger.java @@ -0,0 +1,146 @@ +/* ******************************************************************* + * 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.DataOutputStream; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.aspectj.bridge.ISourceLocation; + +public class NewMethodTypeMunger extends ResolvedTypeMunger { + + public NewMethodTypeMunger(ResolvedMember signature, Set superMethodsCalled, List typeVariableAliases) { + super(Method, signature); + this.typeVariableAliases = typeVariableAliases; + this.setSuperMethodsCalled(superMethodsCalled); + } + + public ResolvedMember getInterMethodBody(UnresolvedType aspectType) { + return AjcMemberMaker.interMethodBody(signature, aspectType); + } + + /** + * If the munger has a declared signature + */ + public ResolvedMember getDeclaredInterMethodBody(UnresolvedType aspectType, World w) { + if (declaredSignature != null) { + ResolvedMember rm = declaredSignature.parameterizedWith(null, signature.getDeclaringType().resolve(w), false, + getTypeVariableAliases()); + return AjcMemberMaker.interMethodBody(rm, aspectType); + } else { + return AjcMemberMaker.interMethodBody(signature, aspectType); + } + } + + // public ResolvedMember getInterMethodDispatcher(UnresolvedType aspectType) { + // return AjcMemberMaker.interMethodDispatcher(signature, aspectType); + // } + + public ResolvedMember getDeclaredInterMethodDispatcher(UnresolvedType aspectType, World w) { + if (declaredSignature != null) { + ResolvedMember rm = declaredSignature.parameterizedWith(null, signature.getDeclaringType().resolve(w), false, + getTypeVariableAliases()); + return AjcMemberMaker.interMethodDispatcher(rm, aspectType); + } else { + return AjcMemberMaker.interMethodDispatcher(signature, aspectType); + } + } + + public void write(DataOutputStream s) throws IOException { + kind.write(s); + signature.write(s); + writeSuperMethodsCalled(s); + writeSourceLocation(s); + writeOutTypeAliases(s); + } + + public static ResolvedTypeMunger readMethod(VersionedDataInputStream s, ISourceContext context) throws IOException { + ISourceLocation sloc = null; + ResolvedMemberImpl rmImpl = ResolvedMemberImpl.readResolvedMember(s, context); + Set superMethodsCalled = readSuperMethodsCalled(s); + sloc = readSourceLocation(s); + List typeVarAliases = readInTypeAliases(s); + + ResolvedTypeMunger munger = new NewMethodTypeMunger(rmImpl, superMethodsCalled, typeVarAliases); + if (sloc != null) + munger.setSourceLocation(sloc); + return munger; + } + + public ResolvedMember getMatchingSyntheticMember(Member member, ResolvedType aspectType) { + ResolvedMember ret = AjcMemberMaker.interMethodDispatcher(getSignature(), aspectType); + if (ResolvedType.matches(ret, member)) + return getSignature(); + return super.getMatchingSyntheticMember(member, aspectType); + } + + /** + * see ResolvedTypeMunger.parameterizedFor(ResolvedType) + */ + public ResolvedTypeMunger parameterizedFor(ResolvedType target) { + ResolvedType genericType = target; + if (target.isRawType() || target.isParameterizedType()) + genericType = genericType.getGenericType(); + ResolvedMember parameterizedSignature = null; + // If we are parameterizing it for a generic type, we just need to 'swap the letters' from the ones used + // in the original ITD declaration to the ones used in the actual target type declaration. + if (target.isGenericType()) { + TypeVariable vars[] = target.getTypeVariables(); + UnresolvedTypeVariableReferenceType[] varRefs = new UnresolvedTypeVariableReferenceType[vars.length]; + for (int i = 0; i < vars.length; i++) { + varRefs[i] = new UnresolvedTypeVariableReferenceType(vars[i]); + } + parameterizedSignature = getSignature().parameterizedWith(varRefs, genericType, true, typeVariableAliases); + } else { + // For raw and 'normal' parameterized targets (e.g. Interface, Interface) + parameterizedSignature = getSignature().parameterizedWith(target.getTypeParameters(), genericType, + target.isParameterizedType(), typeVariableAliases); + } + NewMethodTypeMunger nmtm = new NewMethodTypeMunger(parameterizedSignature, getSuperMethodsCalled(), typeVariableAliases); + nmtm.setDeclaredSignature(getSignature()); + nmtm.setSourceLocation(getSourceLocation()); + return nmtm; + } + + public boolean equals(Object other) { + if (!(other instanceof NewMethodTypeMunger)) + return false; + NewMethodTypeMunger o = (NewMethodTypeMunger) other; + return kind.equals(o.kind) + && ((o.signature == null) ? (signature == null) : signature.equals(o.signature)) + && ((o.declaredSignature == null) ? (declaredSignature == null) : declaredSignature.equals(o.declaredSignature)) + && ((o.typeVariableAliases == null) ? (typeVariableAliases == null) : typeVariableAliases + .equals(o.typeVariableAliases)); + } + + public int hashCode() { + int result = 17; + result = 37 * result + kind.hashCode(); + result = 37 * result + ((signature == null) ? 0 : signature.hashCode()); + result = 37 * result + ((declaredSignature == null) ? 0 : declaredSignature.hashCode()); + result = 37 * result + ((typeVariableAliases == null) ? 0 : typeVariableAliases.hashCode()); + return result; + } + + public ResolvedTypeMunger parameterizeWith(Map m, World w) { + ResolvedMember parameterizedSignature = getSignature().parameterizedWith(m, w); + NewMethodTypeMunger nmtm = new NewMethodTypeMunger(parameterizedSignature, getSuperMethodsCalled(), typeVariableAliases); + nmtm.setDeclaredSignature(getSignature()); + nmtm.setSourceLocation(getSourceLocation()); + return nmtm; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/NewParentTypeMunger.java b/org.aspectj.matcher/src/org/aspectj/weaver/NewParentTypeMunger.java new file mode 100644 index 000000000..cc8ac44aa --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/NewParentTypeMunger.java @@ -0,0 +1,51 @@ +/* ******************************************************************* + * 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.DataOutputStream; +import java.io.IOException; + +public class NewParentTypeMunger extends ResolvedTypeMunger { + ResolvedType newParent; + + public NewParentTypeMunger(ResolvedType newParent) { + super(Parent, null); + this.newParent = newParent; + } + + public void write(DataOutputStream s) throws IOException { + throw new RuntimeException("unimplemented"); + } + + + public ResolvedType getNewParent() { + return newParent; + } + + public boolean equals(Object other) { + if (! (other instanceof NewParentTypeMunger)) return false; + NewParentTypeMunger o = (NewParentTypeMunger) other; + return newParent.equals(o.newParent); + } + + private volatile int hashCode = 0; + public int hashCode() { + if (hashCode == 0) { + int result = 17; + result = 37*result + newParent.hashCode(); + hashCode = result; + } + return hashCode; + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/PerObjectInterfaceTypeMunger.java b/org.aspectj.matcher/src/org/aspectj/weaver/PerObjectInterfaceTypeMunger.java new file mode 100644 index 000000000..05e007751 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/PerObjectInterfaceTypeMunger.java @@ -0,0 +1,91 @@ +/* ******************************************************************* + * 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 rearchitected for #75442 finer grained matching + * ******************************************************************/ +package org.aspectj.weaver; + +import org.aspectj.weaver.patterns.PerFromSuper; +import org.aspectj.weaver.patterns.PerObject; +import org.aspectj.weaver.patterns.PerThisOrTargetPointcutVisitor; +import org.aspectj.weaver.patterns.Pointcut; +import org.aspectj.weaver.patterns.TypePattern; + +import java.io.DataOutputStream; +import java.io.IOException; + +public class PerObjectInterfaceTypeMunger extends ResolvedTypeMunger { + + private final UnresolvedType interfaceType; + private final Pointcut testPointcut; + private TypePattern lazyTestTypePattern; + + public boolean equals(Object other) { + if (other==null || !(other instanceof PerObjectInterfaceTypeMunger)) return false; + PerObjectInterfaceTypeMunger o = (PerObjectInterfaceTypeMunger)other; + return ((testPointcut == null) ? (o.testPointcut == null ) : testPointcut.equals(o.testPointcut)) + && ((lazyTestTypePattern == null) ? (o.lazyTestTypePattern == null ) : lazyTestTypePattern.equals(o.lazyTestTypePattern)); + } + + private volatile int hashCode = 0; + public int hashCode() { + if (hashCode == 0) { + int result = 17; + result = 37*result + ((testPointcut == null) ? 0 : testPointcut.hashCode()); + result = 37*result + ((lazyTestTypePattern == null) ? 0 : lazyTestTypePattern.hashCode()); + hashCode = result; + } + return hashCode; + } + + public PerObjectInterfaceTypeMunger(UnresolvedType aspectType, Pointcut testPointcut) { + super(PerObjectInterface, null); + this.testPointcut = testPointcut; + this.interfaceType = AjcMemberMaker.perObjectInterfaceType(aspectType); + } + + private TypePattern getTestTypePattern(ResolvedType aspectType) { + if (lazyTestTypePattern == null) { + final boolean isPerThis; + if (aspectType.getPerClause() instanceof PerFromSuper) { + PerFromSuper ps = (PerFromSuper) aspectType.getPerClause(); + isPerThis = ((PerObject) ps.lookupConcretePerClause(aspectType)).isThis(); + } else { + isPerThis = ((PerObject) aspectType.getPerClause()).isThis(); + } + PerThisOrTargetPointcutVisitor v = new PerThisOrTargetPointcutVisitor(!isPerThis, aspectType); + lazyTestTypePattern = v.getPerTypePointcut(testPointcut); + // reset hashCode so that its recalculated with the new lazyTestTypePattern + hashCode = 0; + } + return lazyTestTypePattern; + } + + public void write(DataOutputStream s) throws IOException { + throw new RuntimeException("shouldn't be serialized"); + } + + public UnresolvedType getInterfaceType() { + return interfaceType; + } + + public Pointcut getTestPointcut() { + return testPointcut; + } + + public boolean matches(ResolvedType matchType, ResolvedType aspectType) { + if (matchType.isInterface()) return false; + return getTestTypePattern(aspectType).matchesStatically(matchType); + } + + public boolean isLateMunger() { + return true; + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/PerTypeWithinTargetTypeMunger.java b/org.aspectj.matcher/src/org/aspectj/weaver/PerTypeWithinTargetTypeMunger.java new file mode 100644 index 000000000..029354b5a --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/PerTypeWithinTargetTypeMunger.java @@ -0,0 +1,81 @@ +/* ******************************************************************* + * Copyright (c) 2005 IBM, 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; + +import java.io.DataOutputStream; +import java.io.IOException; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.patterns.PerTypeWithin; +import org.aspectj.weaver.patterns.Pointcut; + +// PTWIMPL Target type munger adds the localAspectOf() method +public class PerTypeWithinTargetTypeMunger extends ResolvedTypeMunger { + private UnresolvedType aspectType; + private PerTypeWithin testPointcut; + + public PerTypeWithinTargetTypeMunger(UnresolvedType aspectType, PerTypeWithin testPointcut) { + super(PerTypeWithinInterface, null); + this.aspectType = aspectType; + this.testPointcut = testPointcut; + } + + public boolean equals(Object other) { + if (!(other instanceof PerTypeWithinTargetTypeMunger)) return false; + PerTypeWithinTargetTypeMunger o = (PerTypeWithinTargetTypeMunger)other; + return ((o.testPointcut == null) ? (testPointcut == null ) : testPointcut.equals(o.testPointcut)) + && ((o.aspectType == null) ? (aspectType == null ) : aspectType.equals(o.aspectType)); + } + + private volatile int hashCode = 0; + public int hashCode() { + if (hashCode == 0) { + int result = 17; + result = 37*result + ((testPointcut == null) ? 0 : testPointcut.hashCode()); + result = 37*result + ((aspectType == null) ? 0 : aspectType.hashCode()); + hashCode = result; + } + return hashCode; + } + + public void write(DataOutputStream s) throws IOException { + throw new RuntimeException("shouldn't be serialized"); + } + + public UnresolvedType getAspectType() { + return aspectType; + } + + public Pointcut getTestPointcut() { + return testPointcut; + } + + // This is a lexical within() so if you say PerTypeWithin(Test) and matchType is an + // inner type (e.g. Test$NestedType) then it should match successfully + // Does not match if the target is an interface + public boolean matches(ResolvedType matchType, ResolvedType aspectType) { + return isWithinType(matchType).alwaysTrue() && + !matchType.isInterface(); + } + + private FuzzyBoolean isWithinType(ResolvedType type) { + while (type != null) { + if (testPointcut.getTypePattern().matchesStatically(type)) { + return FuzzyBoolean.YES; + } + type = type.getDeclaringType(); + } + return FuzzyBoolean.NO; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/PoliceExtensionUse.java b/org.aspectj.matcher/src/org/aspectj/weaver/PoliceExtensionUse.java new file mode 100644 index 000000000..e35a9b8a3 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/PoliceExtensionUse.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2006 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 API and implementation + *******************************************************************************/ +package org.aspectj.weaver; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.weaver.patterns.AbstractPatternNodeVisitor; +import org.aspectj.weaver.patterns.AndPointcut; +import org.aspectj.weaver.patterns.KindedPointcut; +import org.aspectj.weaver.patterns.NotPointcut; +import org.aspectj.weaver.patterns.OrPointcut; +import org.aspectj.weaver.patterns.Pointcut; + +/** + * Walks a pointcut and determines if the synchronization related designators have been used: lock() or unlock() + */ +public class PoliceExtensionUse extends AbstractPatternNodeVisitor { + + private boolean synchronizationDesignatorEncountered; + private World world; + private Pointcut p; + + public PoliceExtensionUse(World w, Pointcut p) { + this.world = w; + this.p = p; + this.synchronizationDesignatorEncountered = false; + } + + public boolean synchronizationDesignatorEncountered() { + return synchronizationDesignatorEncountered; + } + + public Object visit(KindedPointcut node, Object data) { + if (world == null) + return super.visit(node, data); // error scenario can sometimes lead to this LazyClassGen.toLongString() + if (node.getKind() == Shadow.SynchronizationLock || node.getKind() == Shadow.SynchronizationUnlock) + synchronizationDesignatorEncountered = true; + // Check it! + if (!world.isJoinpointSynchronizationEnabled()) { + if (node.getKind() == Shadow.SynchronizationLock) { + IMessage m = MessageUtil.warn( + "lock() pointcut designator cannot be used without the option -Xjoinpoints:synchronization", p + .getSourceLocation()); + world.getMessageHandler().handleMessage(m); + } else if (node.getKind() == Shadow.SynchronizationUnlock) { + IMessage m = MessageUtil.warn( + "unlock() pointcut designator cannot be used without the option -Xjoinpoints:synchronization", p + .getSourceLocation()); + world.getMessageHandler().handleMessage(m); + } + } + return super.visit(node, data); + } + + public Object visit(AndPointcut node, Object data) { + node.getLeft().accept(this, data); + node.getRight().accept(this, data); + return node; + } + + public Object visit(NotPointcut node, Object data) { + node.getNegatedPointcut().accept(this, data); + return node; + } + + public Object visit(OrPointcut node, Object data) { + node.getLeft().accept(this, data); + node.getRight().accept(this, data); + return node; + } + +} \ No newline at end of file diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/Position.java b/org.aspectj.matcher/src/org/aspectj/weaver/Position.java new file mode 100644 index 000000000..710958d8c --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/Position.java @@ -0,0 +1,32 @@ +/* ******************************************************************* + * 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; + +public class Position implements IHasPosition { + private int start, end; + + public Position(int start, int end) { + this.start = start; + this.end = end; + } + + public int getEnd() { + return end; + } + + public int getStart() { + return start; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/PrivilegedAccessMunger.java b/org.aspectj.matcher/src/org/aspectj/weaver/PrivilegedAccessMunger.java new file mode 100644 index 000000000..ca9152bae --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/PrivilegedAccessMunger.java @@ -0,0 +1,69 @@ +/* ******************************************************************* + * 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.DataOutputStream; +import java.io.IOException; + +public class PrivilegedAccessMunger extends ResolvedTypeMunger { + public PrivilegedAccessMunger(ResolvedMember member) { + super(PrivilegedAccess, member); + } + + + public void write(DataOutputStream s) throws IOException { + throw new RuntimeException("shouldn't be serialized"); + } + + public ResolvedMember getMember() { + return getSignature(); + } + + public ResolvedMember getMatchingSyntheticMember(Member member, ResolvedType aspectType) { + ResolvedMember ret; + if (getSignature().getKind() == Member.FIELD) { + ret = AjcMemberMaker.privilegedAccessMethodForFieldGet(aspectType, getSignature()); + if (ResolvedType.matches(ret, member)) return getSignature(); + ret = AjcMemberMaker.privilegedAccessMethodForFieldSet(aspectType, getSignature()); + if (ResolvedType.matches(ret, member)) return getSignature(); + } else { + //System.err.println("sig: " + getSignature()); + ret = AjcMemberMaker.privilegedAccessMethodForMethod(aspectType, getSignature()); + if (ResolvedType.matches(ret, member)) return getSignature(); + } + return null; + } + + public boolean equals(Object other) { + if (! (other instanceof PrivilegedAccessMunger)) return false; + PrivilegedAccessMunger o = (PrivilegedAccessMunger) other; + return kind.equals(o.kind) + && ((o.signature == null) ? (signature == null ) : signature.equals(o.signature)) + && ((o.declaredSignature == null) ? (declaredSignature == null ) : declaredSignature.equals(o.declaredSignature)) + && ((o.typeVariableAliases == null) ? (typeVariableAliases == null ) : typeVariableAliases.equals(o.typeVariableAliases)); + } + + public int hashCode() { + int result = 17; + result = 37*result + kind.hashCode(); + result = 37*result + ((signature == null) ? 0 : signature.hashCode()); + result = 37*result + ((declaredSignature == null) ? 0 : declaredSignature.hashCode()); + result = 37*result + ((typeVariableAliases == null) ? 0 : typeVariableAliases.hashCode()); + return result; + } + + public boolean existsToSupportShadowMunging() { + return true; + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/ReferenceType.java b/org.aspectj.matcher/src/org/aspectj/weaver/ReferenceType.java new file mode 100644 index 000000000..8961e2194 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/ReferenceType.java @@ -0,0 +1,810 @@ +/* ******************************************************************* + * 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 + * Andy Clement - June 2005 - separated out from ResolvedType + * ******************************************************************/ +package org.aspectj.weaver; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.weaver.patterns.Declare; +import org.aspectj.weaver.patterns.PerClause; + +/** + * A reference type represents some 'real' type, not a primitive, not an array - but a real type, for example java.util.List. Each + * ReferenceType has a delegate that is the underlying artifact - either an eclipse artifact or a bcel artifact. If the type + * represents a raw type (i.e. there is a generic form) then the genericType field is set to point to the generic type. If it is for + * a parameterized type then the generic type is also set to point to the generic form. + */ +public class ReferenceType extends ResolvedType { + + /** + * For generic types, this list holds references to all the derived raw and parameterized versions. We need this so that if the + * generic delegate is swapped during incremental compilation, the delegate of the derivatives is swapped also. + */ + private final List/* ReferenceType */derivativeTypes = new ArrayList(); + + /** + * For parameterized types (or the raw type) - this field points to the actual reference type from which they are derived. + */ + ReferenceType genericType = null; + + ReferenceTypeDelegate delegate = null; + int startPos = 0; + int endPos = 0; + + // cached values for members + ResolvedMember[] parameterizedMethods = null; + ResolvedMember[] parameterizedFields = null; + ResolvedMember[] parameterizedPointcuts = null; + ResolvedType[] parameterizedInterfaces = null; + Collection parameterizedDeclares = null; + Collection parameterizedTypeMungers = null; + + // ??? should set delegate before any use + public ReferenceType(String signature, World world) { + super(signature, world); + } + + public ReferenceType(String signature, String signatureErasure, World world) { + super(signature, signatureErasure, world); + } + + public static ReferenceType fromTypeX(UnresolvedType tx, World world) { + ReferenceType rt = new ReferenceType(tx.getErasureSignature(), world); + rt.typeKind = tx.typeKind; + return rt; + } + + /** + * Constructor used when creating a parameterized type. + */ + public ReferenceType(ResolvedType theGenericType, ResolvedType[] theParameters, World aWorld) { + super(makeParameterizedSignature(theGenericType, theParameters), theGenericType.signatureErasure, aWorld); + ReferenceType genericReferenceType = (ReferenceType) theGenericType; + this.typeParameters = theParameters; + this.genericType = genericReferenceType; + this.typeKind = TypeKind.PARAMETERIZED; + this.delegate = genericReferenceType.getDelegate(); + genericReferenceType.addDependentType(this); + } + + /** + * Constructor used when creating a raw type. + */ + // public ReferenceType( + // ResolvedType theGenericType, + // World aWorld) { + // super(theGenericType.getErasureSignature(), + // theGenericType.getErasureSignature(), + // aWorld); + // ReferenceType genericReferenceType = (ReferenceType) theGenericType; + // this.typeParameters = null; + // this.genericType = genericReferenceType; + // this.typeKind = TypeKind.RAW; + // this.delegate = genericReferenceType.getDelegate(); + // genericReferenceType.addDependentType(this); + // } + private void addDependentType(ReferenceType dependent) { + this.derivativeTypes.add(dependent); + } + + public String getSignatureForAttribute() { + if (genericType == null || typeParameters == null) + return getSignature(); + return makeDeclaredSignature(genericType, typeParameters); + } + + /** + * Create a reference type for a generic type + */ + public ReferenceType(UnresolvedType genericType, World world) { + super(genericType.getSignature(), world); + typeKind = TypeKind.GENERIC; + } + + public boolean isClass() { + return delegate.isClass(); + } + + public boolean isGenericType() { + return !isParameterizedType() && !isRawType() && delegate.isGeneric(); + } + + public String getGenericSignature() { + String sig = delegate.getDeclaredGenericSignature(); + return (sig == null) ? "" : sig; + } + + public AnnotationAJ[] getAnnotations() { + return delegate.getAnnotations(); + } + + public void addAnnotation(AnnotationAJ annotationX) { + delegate.addAnnotation(annotationX); + } + + public boolean hasAnnotation(UnresolvedType ofType) { + return delegate.hasAnnotation(ofType); + } + + public ResolvedType[] getAnnotationTypes() { + if (delegate == null) { + throw new BCException("Unexpected null delegate for type " + this.getName()); + } + return delegate.getAnnotationTypes(); + } + + public AnnotationAJ getAnnotationOfType(UnresolvedType ofType) { + AnnotationAJ[] axs = delegate.getAnnotations(); + if (axs == null) { + return null; + } + for (int i = 0; i < axs.length; i++) { + if (axs[i].getTypeSignature().equals(ofType.getSignature())) { + return axs[i]; + } + } + return null; + } + + public boolean isAspect() { + return delegate.isAspect(); + } + + public boolean isAnnotationStyleAspect() { + return delegate.isAnnotationStyleAspect(); + } + + public boolean isEnum() { + return delegate.isEnum(); + } + + public boolean isAnnotation() { + return delegate.isAnnotation(); + } + + public boolean isAnonymous() { + return delegate.isAnonymous(); + } + + public boolean isNested() { + return delegate.isNested(); + } + + public ResolvedType getOuterClass() { + return delegate.getOuterClass(); + } + + public String getRetentionPolicy() { + return delegate.getRetentionPolicy(); + } + + public boolean isAnnotationWithRuntimeRetention() { + return delegate.isAnnotationWithRuntimeRetention(); + } + + public boolean canAnnotationTargetType() { + return delegate.canAnnotationTargetType(); + } + + public AnnotationTargetKind[] getAnnotationTargetKinds() { + return delegate.getAnnotationTargetKinds(); + } + + // true iff the statement "this = (ThisType) other" would compile + public boolean isCoerceableFrom(ResolvedType o) { + ResolvedType other = o.resolve(world); + + if (this.isAssignableFrom(other) || other.isAssignableFrom(this)) { + return true; + } + + if (this.isParameterizedType() && other.isParameterizedType()) { + return isCoerceableFromParameterizedType(other); + } + + if (!this.isInterface() && !other.isInterface()) { + return false; + } + if (this.isFinal() || other.isFinal()) { + return false; + } + // ??? needs to be Methods, not just declared methods? JLS 5.5 unclear + ResolvedMember[] a = getDeclaredMethods(); + ResolvedMember[] b = other.getDeclaredMethods(); // ??? is this cast + // always safe + for (int ai = 0, alen = a.length; ai < alen; ai++) { + for (int bi = 0, blen = b.length; bi < blen; bi++) { + if (!b[bi].isCompatibleWith(a[ai])) + return false; + } + } + return true; + } + + private final boolean isCoerceableFromParameterizedType(ResolvedType other) { + if (!other.isParameterizedType()) + return false; + ResolvedType myRawType = (ResolvedType) getRawType(); + ResolvedType theirRawType = (ResolvedType) other.getRawType(); + if (myRawType == theirRawType) { + if (getTypeParameters().length == other.getTypeParameters().length) { + // there's a chance it can be done + ResolvedType[] myTypeParameters = getResolvedTypeParameters(); + ResolvedType[] theirTypeParameters = other.getResolvedTypeParameters(); + for (int i = 0; i < myTypeParameters.length; i++) { + if (myTypeParameters[i] != theirTypeParameters[i]) { + // thin ice now... but List may still be + // coerceable from e.g. List + if (myTypeParameters[i].isGenericWildcard()) { + BoundedReferenceType wildcard = (BoundedReferenceType) myTypeParameters[i]; + if (!wildcard.canBeCoercedTo(theirTypeParameters[i])) + return false; + } else if (myTypeParameters[i].isTypeVariableReference()) { + TypeVariableReferenceType tvrt = (TypeVariableReferenceType) myTypeParameters[i]; + TypeVariable tv = tvrt.getTypeVariable(); + tv.resolve(world); + if (!tv.canBeBoundTo(theirTypeParameters[i])) + return false; + } else if (theirTypeParameters[i].isTypeVariableReference()) { + TypeVariableReferenceType tvrt = (TypeVariableReferenceType) theirTypeParameters[i]; + TypeVariable tv = tvrt.getTypeVariable(); + tv.resolve(world); + if (!tv.canBeBoundTo(myTypeParameters[i])) + return false; + } else { + return false; + } + } + } + return true; + } + } else { + // we do this walk for situations like the following: + // Base, Sub extends Base + // is Sub coerceable from Base ??? + for (Iterator i = getDirectSupertypes(); i.hasNext();) { + ReferenceType parent = (ReferenceType) i.next(); + if (parent.isCoerceableFromParameterizedType(other)) + return true; + } + } + return false; + } + + public boolean isAssignableFrom(ResolvedType other) { + return isAssignableFrom(other, false); + } + + // true iff the statement "this = other" would compile. + public boolean isAssignableFrom(ResolvedType other, boolean allowMissing) { + if (other.isPrimitiveType()) { + if (!world.isInJava5Mode()) + return false; + if (ResolvedType.validBoxing.contains(this.getSignature() + other.getSignature())) + return true; + } + if (this == other) + return true; + if (this.getSignature().equals(ResolvedType.OBJECT.getSignature())) + return true; + + if ((this.isRawType() || this.isGenericType()) && other.isParameterizedType()) { + if (isAssignableFrom((ResolvedType) other.getRawType())) + return true; + } + if (this.isRawType() && other.isGenericType()) { + if (isAssignableFrom((ResolvedType) other.getRawType())) + return true; + } + if (this.isGenericType() && other.isRawType()) { + if (isAssignableFrom(other.getGenericType())) + return true; + } + + if (this.isParameterizedType()) { + // look at wildcards... + if (((ReferenceType) this.getRawType()).isAssignableFrom(other)) { + boolean wildcardsAllTheWay = true; + ResolvedType[] myParameters = this.getResolvedTypeParameters(); + for (int i = 0; i < myParameters.length; i++) { + if (!myParameters[i].isGenericWildcard()) { + wildcardsAllTheWay = false; + } else { + BoundedReferenceType boundedRT = (BoundedReferenceType) myParameters[i]; + if (boundedRT.isExtends() || boundedRT.isSuper()) { + wildcardsAllTheWay = false; + } + } + } + if (wildcardsAllTheWay && !other.isParameterizedType()) + return true; + // we have to match by parameters one at a time + ResolvedType[] theirParameters = other.getResolvedTypeParameters(); + boolean parametersAssignable = true; + if (myParameters.length == theirParameters.length) { + for (int i = 0; i < myParameters.length; i++) { + if (myParameters[i] == theirParameters[i]) + continue; + if (myParameters[i].isAssignableFrom(theirParameters[i], allowMissing)) { + continue; + } + if (!myParameters[i].isGenericWildcard()) { + parametersAssignable = false; + break; + } else { + BoundedReferenceType wildcardType = (BoundedReferenceType) myParameters[i]; + if (!wildcardType.alwaysMatches(theirParameters[i])) { + parametersAssignable = false; + break; + } + } + } + } else { + parametersAssignable = false; + } + if (parametersAssignable) + return true; + } + } + + if (isTypeVariableReference() && !other.isTypeVariableReference()) { // eg + // . + // this + // = + // T + // other + // = + // Ljava + // / + // lang + // / + // Object + // ; + TypeVariable aVar = ((TypeVariableReference) this).getTypeVariable(); + return aVar.resolve(world).canBeBoundTo(other); + } + + if (other.isTypeVariableReference()) { + TypeVariableReferenceType otherType = (TypeVariableReferenceType) other; + if (this instanceof TypeVariableReference) { + return ((TypeVariableReference) this).getTypeVariable().resolve(world).canBeBoundTo( + otherType.getTypeVariable().getFirstBound().resolve(world));// pr171952 + // return + // ((TypeVariableReference)this).getTypeVariable()==otherType + // .getTypeVariable(); + } else { + // FIXME asc should this say canBeBoundTo?? + return this.isAssignableFrom(otherType.getTypeVariable().getFirstBound().resolve(world)); + } + } + + if (allowMissing && other.isMissing()) + return false; + + for (Iterator i = other.getDirectSupertypes(); i.hasNext();) { + if (this.isAssignableFrom((ResolvedType) i.next(), allowMissing)) + return true; + } + return false; + } + + public ISourceContext getSourceContext() { + return delegate.getSourceContext(); + } + + public ISourceLocation getSourceLocation() { + ISourceContext isc = delegate.getSourceContext(); + return isc.makeSourceLocation(new Position(startPos, endPos)); + } + + public boolean isExposedToWeaver() { + return (delegate == null) || delegate.isExposedToWeaver(); // ??? where + // does this + // belong + } + + public WeaverStateInfo getWeaverState() { + return delegate.getWeaverState(); + } + + public ResolvedMember[] getDeclaredFields() { + if (parameterizedFields != null) + return parameterizedFields; + if (isParameterizedType() || isRawType()) { + ResolvedMember[] delegateFields = delegate.getDeclaredFields(); + parameterizedFields = new ResolvedMember[delegateFields.length]; + for (int i = 0; i < delegateFields.length; i++) { + parameterizedFields[i] = delegateFields[i].parameterizedWith(getTypesForMemberParameterization(), this, + isParameterizedType()); + } + return parameterizedFields; + } else { + return delegate.getDeclaredFields(); + } + } + + /** + * Find out from the generic signature the true signature of any interfaces I implement. If I am parameterized, these may then + * need to be parameterized before returning. + */ + public ResolvedType[] getDeclaredInterfaces() { + if (parameterizedInterfaces != null) + return parameterizedInterfaces; + if (isParameterizedType()) { + ResolvedType[] delegateInterfaces = delegate.getDeclaredInterfaces(); + // UnresolvedType[] paramTypes = + // getTypesForMemberParameterization(); + parameterizedInterfaces = new ResolvedType[delegateInterfaces.length]; + for (int i = 0; i < delegateInterfaces.length; i++) { + // We may have to sub/super set the set of parametertypes if the + // implemented interface + // needs more or less than this type does. (pr124803/pr125080) + + if (delegateInterfaces[i].isParameterizedType()) { + parameterizedInterfaces[i] = delegateInterfaces[i].parameterize(getMemberParameterizationMap()).resolve(world); + } else { + parameterizedInterfaces[i] = delegateInterfaces[i]; + } + } + return parameterizedInterfaces; + } else if (isRawType()) { + ResolvedType[] delegateInterfaces = delegate.getDeclaredInterfaces(); + UnresolvedType[] paramTypes = getTypesForMemberParameterization(); + parameterizedInterfaces = new ResolvedType[delegateInterfaces.length]; + for (int i = 0; i < parameterizedInterfaces.length; i++) { + parameterizedInterfaces[i] = delegateInterfaces[i]; + if (parameterizedInterfaces[i].isGenericType()) { + // a generic supertype of a raw type is replaced by its raw + // equivalent + parameterizedInterfaces[i] = parameterizedInterfaces[i].getRawType().resolve(getWorld()); + } else if (parameterizedInterfaces[i].isParameterizedType()) { + // a parameterized supertype collapses any type vars to + // their upper bounds + UnresolvedType[] toUseForParameterization = determineThoseTypesToUse(parameterizedInterfaces[i], paramTypes); + parameterizedInterfaces[i] = parameterizedInterfaces[i].parameterizedWith(toUseForParameterization); + } + } + return parameterizedInterfaces; + } + return delegate.getDeclaredInterfaces(); + } + + /** + * Locates the named type variable in the list of those on this generic type and returns the type parameter from the second list + * supplied. Returns null if it can't be found + */ + // private UnresolvedType findTypeParameterInList(String name, + // TypeVariable[] tvarsOnThisGenericType, UnresolvedType[] + // paramTypes) { + // int position = -1; + // for (int i = 0; i < tvarsOnThisGenericType.length; i++) { + // TypeVariable tv = tvarsOnThisGenericType[i]; + // if (tv.getName().equals(name)) position = i; + // } + // if (position == -1 ) return null; + // return paramTypes[position]; + // } + /** + * It is possible this type has multiple type variables but the interface we are about to parameterize only uses a subset - this + * method determines the subset to use by looking at the type variable names used. For example: + * class Foo implements SuperInterface {} + * where + * interface SuperInterface {} + * In that + * example, a use of the 'Foo' raw type should know that it implements the SuperInterface. + */ + private UnresolvedType[] determineThoseTypesToUse(ResolvedType parameterizedInterface, UnresolvedType[] paramTypes) { + // What are the type parameters for the supertype? + UnresolvedType[] tParms = parameterizedInterface.getTypeParameters(); + UnresolvedType[] retVal = new UnresolvedType[tParms.length]; + + // Go through the supertypes type parameters, if any of them is a type + // variable, use the + // real type variable on the declaring type. + + // it is possibly overkill to look up the type variable - ideally the + // entry in the type parameter list for the + // interface should be the a ref to the type variable in the current + // type ... but I'm not 100% confident right now. + for (int i = 0; i < tParms.length; i++) { + UnresolvedType tParm = tParms[i]; + if (tParm.isTypeVariableReference()) { + TypeVariableReference tvrt = (TypeVariableReference) tParm; + TypeVariable tv = tvrt.getTypeVariable(); + int rank = getRank(tv.getName()); + // -1 probably means it is a reference to a type variable on the + // outer generic type (see pr129566) + if (rank != -1) { + retVal[i] = paramTypes[rank]; + } else { + retVal[i] = tParms[i]; + } + } else { + retVal[i] = tParms[i]; + } + + } + return retVal; + } + + /** + * Returns the position within the set of type variables for this type for the specified type variable name. Returns -1 if there + * is no type variable with the specified name. + */ + private int getRank(String tvname) { + TypeVariable[] thisTypesTVars = getGenericType().getTypeVariables(); + for (int i = 0; i < thisTypesTVars.length; i++) { + TypeVariable tv = thisTypesTVars[i]; + if (tv.getName().equals(tvname)) + return i; + } + return -1; + } + + public ResolvedMember[] getDeclaredMethods() { + if (parameterizedMethods != null) + return parameterizedMethods; + if (isParameterizedType() || isRawType()) { + ResolvedMember[] delegateMethods = delegate.getDeclaredMethods(); + UnresolvedType[] parameters = getTypesForMemberParameterization(); + parameterizedMethods = new ResolvedMember[delegateMethods.length]; + for (int i = 0; i < delegateMethods.length; i++) { + parameterizedMethods[i] = delegateMethods[i].parameterizedWith(parameters, this, isParameterizedType()); + } + return parameterizedMethods; + } else { + return delegate.getDeclaredMethods(); + } + } + + public ResolvedMember[] getDeclaredPointcuts() { + if (parameterizedPointcuts != null) + return parameterizedPointcuts; + if (isParameterizedType()) { + ResolvedMember[] delegatePointcuts = delegate.getDeclaredPointcuts(); + parameterizedPointcuts = new ResolvedMember[delegatePointcuts.length]; + for (int i = 0; i < delegatePointcuts.length; i++) { + parameterizedPointcuts[i] = delegatePointcuts[i].parameterizedWith(getTypesForMemberParameterization(), this, + isParameterizedType()); + } + return parameterizedPointcuts; + } else { + return delegate.getDeclaredPointcuts(); + } + } + + private UnresolvedType[] getTypesForMemberParameterization() { + UnresolvedType[] parameters = null; + if (isParameterizedType()) { + parameters = getTypeParameters(); + } else if (isRawType()) { + // raw type, use upper bounds of type variables on generic type + TypeVariable[] tvs = getGenericType().getTypeVariables(); + parameters = new UnresolvedType[tvs.length]; + for (int i = 0; i < tvs.length; i++) { + parameters[i] = tvs[i].getFirstBound(); + } + } + return parameters; + } + + public UnresolvedType getRawType() { + return super.getRawType().resolve(getWorld()); + } + + public TypeVariable[] getTypeVariables() { + if (this.typeVariables == null) { + this.typeVariables = delegate.getTypeVariables(); + for (int i = 0; i < this.typeVariables.length; i++) { + this.typeVariables[i].resolve(world); + } + } + return this.typeVariables; + } + + public PerClause getPerClause() { + PerClause pclause = delegate.getPerClause(); + if (isParameterizedType()) { // could cache the result here... + Map parameterizationMap = getAjMemberParameterizationMap(); + pclause = (PerClause) pclause.parameterizeWith(parameterizationMap, world); + } + return pclause; + } + + public Collection getDeclares() { + if (parameterizedDeclares != null) + return parameterizedDeclares; + Collection declares = null; + if (ajMembersNeedParameterization()) { + Collection genericDeclares = delegate.getDeclares(); + parameterizedDeclares = new ArrayList(); + Map parameterizationMap = getAjMemberParameterizationMap(); + for (Iterator iter = genericDeclares.iterator(); iter.hasNext();) { + Declare declareStatement = (Declare) iter.next(); + parameterizedDeclares.add(declareStatement.parameterizeWith(parameterizationMap, world)); + } + declares = parameterizedDeclares; + } else { + declares = delegate.getDeclares(); + } + for (Iterator iter = declares.iterator(); iter.hasNext();) { + Declare d = (Declare) iter.next(); + d.setDeclaringType(this); + } + return declares; + } + + protected Collection getTypeMungers() { + return delegate.getTypeMungers(); + } + + // GENERICITDFIX + // // Map parameterizationMap = getAjMemberParameterizationMap(); + // + // // if (parameterizedTypeMungers != null) return parameterizedTypeMungers; + // Collection ret = null; + // if (ajMembersNeedParameterization()) { + // Collection genericDeclares = delegate.getTypeMungers(); + // parameterizedTypeMungers = new ArrayList(); + // Map parameterizationMap = getAjMemberParameterizationMap(); + // for (Iterator iter = genericDeclares.iterator(); iter.hasNext();) { + // ConcreteTypeMunger munger = (ConcreteTypeMunger)iter.next(); + // parameterizedTypeMungers.add(munger.parameterizeWith(parameterizationMap, + // world)); + // } + // ret = parameterizedTypeMungers; + // } else { + // ret = delegate.getTypeMungers(); + // } + // return ret; + // } + + protected Collection getPrivilegedAccesses() { + return delegate.getPrivilegedAccesses(); + } + + public int getModifiers() { + return delegate.getModifiers(); + } + + public ResolvedType getSuperclass() { + ResolvedType ret = null; + try { + world.setTypeVariableLookupScope(this); + ret = delegate.getSuperclass(); + } finally { + world.setTypeVariableLookupScope(null); + } + if (this.isParameterizedType() && ret.isParameterizedType()) { + ret = ret.parameterize(getMemberParameterizationMap()).resolve(getWorld()); + } + return ret; + } + + public ReferenceTypeDelegate getDelegate() { + return delegate; + } + + public void setDelegate(ReferenceTypeDelegate delegate) { + // Don't copy from BcelObjectType to EclipseSourceType - the context may + // be tidied (result null'd) after previous weaving + if (this.delegate != null && this.delegate.copySourceContext() + && this.delegate.getSourceContext() != SourceContextImpl.UNKNOWN_SOURCE_CONTEXT) + ((AbstractReferenceTypeDelegate) delegate).setSourceContext(this.delegate.getSourceContext()); + this.delegate = delegate; + for (Iterator it = this.derivativeTypes.iterator(); it.hasNext();) { + ReferenceType dependent = (ReferenceType) it.next(); + dependent.setDelegate(delegate); + } + + // If we are raw, we have a generic type - we should ensure it uses the + // same delegate + if (isRawType() && getGenericType() != null) { + ReferenceType genType = (ReferenceType) getGenericType(); + if (genType.getDelegate() != delegate) { // avoids circular updates + genType.setDelegate(delegate); + } + } + clearParameterizationCaches(); + } + + private void clearParameterizationCaches() { + parameterizedFields = null; + parameterizedInterfaces = null; + parameterizedMethods = null; + parameterizedPointcuts = null; + } + + public int getEndPos() { + return endPos; + } + + public int getStartPos() { + return startPos; + } + + public void setEndPos(int endPos) { + this.endPos = endPos; + } + + public void setStartPos(int startPos) { + this.startPos = startPos; + } + + public boolean doesNotExposeShadowMungers() { + return delegate.doesNotExposeShadowMungers(); + } + + public String getDeclaredGenericSignature() { + return delegate.getDeclaredGenericSignature(); + } + + public void setGenericType(ReferenceType rt) { + genericType = rt; + // Should we 'promote' this reference type from simple to raw? + // makes sense if someone is specifying that it has a generic form + if (typeKind == TypeKind.SIMPLE) { + typeKind = TypeKind.RAW; + signatureErasure = signature; + } + } + + public void demoteToSimpleType() { + genericType = null; + typeKind = TypeKind.SIMPLE; + signatureErasure = null; + } + + public ResolvedType getGenericType() { + if (isGenericType()) + return this; + return genericType; + } + + /** + * a parameterized signature starts with a "P" in place of the "L", see the comment on signatures in UnresolvedType. + * + * @param aGenericType + * @param someParameters + * @return + */ + private static String makeParameterizedSignature(ResolvedType aGenericType, ResolvedType[] someParameters) { + String rawSignature = aGenericType.getErasureSignature(); + StringBuffer ret = new StringBuffer(); + ret.append(PARAMETERIZED_TYPE_IDENTIFIER); + ret.append(rawSignature.substring(1, rawSignature.length() - 1)); + ret.append("<"); + for (int i = 0; i < someParameters.length; i++) { + ret.append(someParameters[i].getSignature()); + } + ret.append(">;"); + return ret.toString(); + } + + private static String makeDeclaredSignature(ResolvedType aGenericType, UnresolvedType[] someParameters) { + StringBuffer ret = new StringBuffer(); + String rawSig = aGenericType.getErasureSignature(); + ret.append(rawSig.substring(0, rawSig.length() - 1)); + ret.append("<"); + for (int i = 0; i < someParameters.length; i++) { + ret.append(((ReferenceType) someParameters[i]).getSignatureForAttribute()); + } + ret.append(">;"); + return ret.toString(); + } + +} \ No newline at end of file diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/ReferenceTypeDelegate.java b/org.aspectj.matcher/src/org/aspectj/weaver/ReferenceTypeDelegate.java new file mode 100644 index 000000000..ec9d83915 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/ReferenceTypeDelegate.java @@ -0,0 +1,105 @@ +/* ******************************************************************* + * 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 + * Andy Clement - June 2005 - separated out from ResolvedType + * ******************************************************************/ +package org.aspectj.weaver; + +import java.util.Collection; + +import org.aspectj.weaver.patterns.PerClause; + +/** + * Abstraction over a type - a reference type is Object and a descendant of Object, other types (int/etc) are considered primitive + * types. Abstract implementation provided by AbstractReferenceTypeDelegate. + */ +public interface ReferenceTypeDelegate { + + // TODO asc move to proxy + public void addAnnotation(AnnotationAJ annotationX); + + public void ensureDelegateConsistent(); // Required evil because of mutator + + // methods in delegates :( (see + // pr85132) + + public boolean isAspect(); + + public boolean isAnnotationStyleAspect(); + + public boolean isInterface(); + + public boolean isEnum(); + + public boolean isAnnotation(); + + public String getRetentionPolicy(); + + public boolean canAnnotationTargetType(); + + public AnnotationTargetKind[] getAnnotationTargetKinds(); + + public boolean isAnnotationWithRuntimeRetention(); + + public boolean isClass(); + + public boolean isGeneric(); + + public boolean isAnonymous(); + + public boolean isNested(); + + public boolean isExposedToWeaver(); + + public boolean hasAnnotation(UnresolvedType ofType); + + public AnnotationAJ[] getAnnotations(); + + public ResolvedType[] getAnnotationTypes(); + + public ResolvedMember[] getDeclaredFields(); + + public ResolvedType[] getDeclaredInterfaces(); + + public ResolvedMember[] getDeclaredMethods(); + + public ResolvedMember[] getDeclaredPointcuts(); + + public TypeVariable[] getTypeVariables(); + + public PerClause getPerClause(); + + public Collection getDeclares(); + + public Collection getTypeMungers(); + + public Collection getPrivilegedAccesses(); + + public int getModifiers(); + + public ResolvedType getSuperclass(); + + public WeaverStateInfo getWeaverState(); + + public ReferenceType getResolvedTypeX(); + + public boolean doesNotExposeShadowMungers(); + + public ISourceContext getSourceContext(); + + public String getSourcefilename(); + + public String getDeclaredGenericSignature(); + + public ResolvedType getOuterClass(); + + public boolean copySourceContext(); + +} \ No newline at end of file diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/ResolvedMember.java b/org.aspectj.matcher/src/org/aspectj/weaver/ResolvedMember.java new file mode 100644 index 000000000..3cbf33ecb --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/ResolvedMember.java @@ -0,0 +1,191 @@ +/* ******************************************************************* + * 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 + * AMC extracted as interface + * ******************************************************************/ +package org.aspectj.weaver; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import org.aspectj.bridge.ISourceLocation; + +public interface ResolvedMember extends Member, AnnotatedElement, + TypeVariableDeclaringElement { + + public static final ResolvedMember[] NONE = new ResolvedMember[0]; + + public int getModifiers(World world); + + public int getModifiers(); + + public UnresolvedType[] getExceptions(World world); + + public UnresolvedType[] getExceptions(); + + public ShadowMunger getAssociatedShadowMunger(); + + public boolean isAjSynthetic(); + + public boolean isCompatibleWith(Member am); + + public boolean hasAnnotations(); + + public boolean hasAnnotation(UnresolvedType ofType); + + public AnnotationAJ[] getAnnotations(); + + public ResolvedType[] getAnnotationTypes(); + + public void setAnnotationTypes(UnresolvedType[] annotationtypes); + + public void addAnnotation(AnnotationAJ annotation); + + public boolean isBridgeMethod(); + + public boolean isVarargsMethod(); + + public boolean isSynthetic(); + + public void write(DataOutputStream s) throws IOException; + + public ISourceContext getSourceContext(World world); + + public String[] getParameterNames(); + + public void setParameterNames(String[] names); + + public AnnotationAJ[][] getParameterAnnotations(); + + public ResolvedType[][] getParameterAnnotationTypes(); + + public String getAnnotationDefaultValue(); + + public String getParameterSignatureErased(); + + public String getSignatureErased(); + + public String[] getParameterNames(World world); + + public AjAttribute.EffectiveSignatureAttribute getEffectiveSignature(); + + public ISourceLocation getSourceLocation(); + + public int getEnd(); + + public ISourceContext getSourceContext(); + + public int getStart(); + + public void setPosition(int sourceStart, int sourceEnd); + + public void setSourceContext(ISourceContext sourceContext); + + public boolean isAbstract(); + + public boolean isPublic(); + + public boolean isProtected(); + + public boolean isNative(); + + public boolean isDefault(); + + public boolean isVisible(ResolvedType fromType); + + public void setCheckedExceptions(UnresolvedType[] checkedExceptions); + + public void setAnnotatedElsewhere(boolean b); + + public boolean isAnnotatedElsewhere(); + + // like toString but include generic signature info + public String toGenericString(); + + public String toDebugString(); + + public boolean hasBackingGenericMember(); + + public ResolvedMember getBackingGenericMember(); + + /** + * Get the UnresolvedType for the return type, taking generic signature into + * account + */ + public UnresolvedType getGenericReturnType(); + + /** + * Get the TypeXs of the parameter types, taking generic signature into + * account + */ + public UnresolvedType[] getGenericParameterTypes(); + + // return a resolved member in which all type variables in the signature of + // this + // member have been replaced with the given bindings. + // the isParameterized flag tells us whether we are creating a raw type + // version or not + // if isParameterized List will turn into List (for example), + // but if !isParameterized List will turn into List. + public ResolvedMemberImpl parameterizedWith( + UnresolvedType[] typeParameters, ResolvedType newDeclaringType, + boolean isParameterized); + + // this variant allows for aliases for type variables (i.e. allowing them to + // have another name) + // this is used for processing ITDs that share type variables with their + // target generic type + public ResolvedMemberImpl parameterizedWith( + UnresolvedType[] typeParameters, ResolvedType newDeclaringType, + boolean isParameterized, List aliases); + + public void setTypeVariables(TypeVariable[] types); + + public TypeVariable[] getTypeVariables(); + + // /** + // * If this member is defined by a parameterized super-type, return the + // erasure + // * of that member. + // * For example: + // * interface I { T foo(T aTea); } + // * class C implements I { + // * String foo(String aString) { return "something"; } + // * } + // * The resolved member for C.foo has signature String foo(String). The + // * erasure of that member is Object foo(Object) -- use upper bound of type + // * variable. + // * A type is a supertype of itself. + // */ + // public ResolvedMember getErasure(); + + /** + * Returns true if this member matches the other. The matching takes into + * account name and parameter types only. When comparing parameter types, we + * allow any type variable to match any other type variable regardless of + * bounds. + */ + public boolean matches(ResolvedMember aCandidateMatch); + + public void resetName(String newName); + + public void resetKind(MemberKind newKind); + + public void resetModifiers(int newModifiers); + + public void resetReturnTypeToObjectArray(); + + public void evictWeavingState(); + + public ResolvedMember parameterizedWith(Map m, World w); +} \ No newline at end of file diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/ResolvedMemberImpl.java b/org.aspectj.matcher/src/org/aspectj/weaver/ResolvedMemberImpl.java new file mode 100644 index 000000000..2bdd41697 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/ResolvedMemberImpl.java @@ -0,0 +1,1156 @@ +/* ******************************************************************* + * 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.DataOutputStream; +import java.io.IOException; +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.bridge.ISourceLocation; + +/** + * This is the declared member, i.e. it will always correspond to an actual method/... declaration + */ +public class ResolvedMemberImpl extends MemberImpl implements IHasPosition, AnnotatedElement, TypeVariableDeclaringElement, + ResolvedMember { + + private String[] parameterNames = null; + protected UnresolvedType[] checkedExceptions = UnresolvedType.NONE; + /** + * if this member is a parameterized version of a member in a generic type, then this field holds a reference to the member we + * parameterize. + */ + protected ResolvedMember backingGenericMember = null; + + protected Set annotationTypes = null; + protected ResolvedType[][] parameterAnnotationTypes = null; + + // Some members are 'created' to represent other things (for example ITDs). + // These + // members have their annotations stored elsewhere, and this flag indicates + // that is + // the case. It is up to the caller to work out where that is! + // Once determined the caller may choose to stash the annotations in this + // member... + private boolean isAnnotatedElsewhere = false; // this field is not + // serialized. + private boolean isAjSynthetic = false; + + // generic methods have type variables + protected TypeVariable[] typeVariables; + + // these three fields hold the source location of this member + protected int start, end; + protected ISourceContext sourceContext = null; + + // XXX deprecate this in favor of the constructor below + public ResolvedMemberImpl(MemberKind kind, UnresolvedType declaringType, int modifiers, UnresolvedType returnType, String name, + UnresolvedType[] parameterTypes) { + super(kind, declaringType, modifiers, returnType, name, parameterTypes); + } + + public ResolvedMemberImpl(MemberKind kind, UnresolvedType declaringType, int modifiers, UnresolvedType returnType, String name, + UnresolvedType[] parameterTypes, UnresolvedType[] checkedExceptions) { + super(kind, declaringType, modifiers, returnType, name, parameterTypes); + this.checkedExceptions = checkedExceptions; + } + + public ResolvedMemberImpl(MemberKind kind, UnresolvedType declaringType, int modifiers, UnresolvedType returnType, String name, + UnresolvedType[] parameterTypes, UnresolvedType[] checkedExceptions, ResolvedMember backingGenericMember) { + this(kind, declaringType, modifiers, returnType, name, parameterTypes, checkedExceptions); + this.backingGenericMember = backingGenericMember; + this.isAjSynthetic = backingGenericMember.isAjSynthetic(); + } + + public ResolvedMemberImpl(MemberKind kind, UnresolvedType declaringType, int modifiers, String name, String signature) { + super(kind, declaringType, modifiers, name, signature); + } + + /** + * Compute the full set of signatures for a member. This walks up the hierarchy giving the ResolvedMember in each defining type + * in the hierarchy. A shadowMember can be created with a target type (declaring type) that does not actually define the member. + * This is ok as long as the member is inherited in the declaring type. Each declaring type in the line to the actual declaring + * type is added as an additional signature. For example: + * + * class A { void foo(); } class B extends A {} + * + * shadowMember : void B.foo() + * + * gives { void B.foo(), void A.foo() } + * + * @param joinPointSignature + * @param inAWorld + */ + public static JoinPointSignature[] getJoinPointSignatures(Member joinPointSignature, World inAWorld) { + + // Walk up hierarchy creating one member for each type up to and + // including the + // first defining type + ResolvedType originalDeclaringType = joinPointSignature.getDeclaringType().resolve(inAWorld); + ResolvedMemberImpl firstDefiningMember = (ResolvedMemberImpl) joinPointSignature.resolve(inAWorld); + if (firstDefiningMember == null) { + return new JoinPointSignature[0]; + } + // declaringType can be unresolved if we matched a synthetic member + // generated by Aj... + // should be fixed elsewhere but add this resolve call on the end for + // now so that we can + // focus on one problem at a time... + ResolvedType firstDefiningType = firstDefiningMember.getDeclaringType().resolve(inAWorld); + if (firstDefiningType != originalDeclaringType) { + if (joinPointSignature.getKind() == Member.CONSTRUCTOR) { + return new JoinPointSignature[0]; + } + // else if (shadowMember.isStatic()) { + // return new ResolvedMember[] {firstDefiningMember}; + // } + } + + List declaringTypes = new ArrayList(); + accumulateTypesInBetween(originalDeclaringType, firstDefiningType, declaringTypes); + Set memberSignatures = new HashSet(); + for (Iterator iter = declaringTypes.iterator(); iter.hasNext();) { + ResolvedType declaringType = (ResolvedType) iter.next(); + ResolvedMember member = firstDefiningMember.withSubstituteDeclaringType(declaringType); + memberSignatures.add(member); + } + + if (shouldWalkUpHierarchyFor(firstDefiningMember)) { + // now walk up the hierarchy from the firstDefiningMember and + // include the signature for + // every type between the firstDefiningMember and the root defining + // member. + Iterator superTypeIterator = firstDefiningType.getDirectSupertypes(); + List typesAlreadyVisited = new ArrayList(); + accumulateMembersMatching(firstDefiningMember, superTypeIterator, typesAlreadyVisited, memberSignatures); + } + + JoinPointSignature[] ret = new JoinPointSignature[memberSignatures.size()]; + memberSignatures.toArray(ret); + return ret; + } + + private static boolean shouldWalkUpHierarchyFor(Member aMember) { + if (aMember.getKind() == Member.CONSTRUCTOR) + return false; + if (aMember.getKind() == Member.FIELD) + return false; + if (aMember.isStatic()) + return false; + return true; + } + + /** + * Build a list containing every type between subtype and supertype, inclusively. + */ + private static void accumulateTypesInBetween(ResolvedType subType, ResolvedType superType, List types) { + types.add(subType); + if (subType == superType) { + return; + } else { + for (Iterator iter = subType.getDirectSupertypes(); iter.hasNext();) { + ResolvedType parent = (ResolvedType) iter.next(); + if (superType.isAssignableFrom(parent)) { + accumulateTypesInBetween(parent, superType, types); + } + } + } + } + + /** + * We have a resolved member, possibly with type parameter references as parameters or return type. We need to find all its + * ancestor members. When doing this, a type parameter matches regardless of bounds (bounds can be narrowed down the hierarchy). + */ + private static void accumulateMembersMatching(ResolvedMemberImpl memberToMatch, Iterator typesToLookIn, + List typesAlreadyVisited, Set foundMembers) { + while (typesToLookIn.hasNext()) { + ResolvedType toLookIn = (ResolvedType) typesToLookIn.next(); + if (!typesAlreadyVisited.contains(toLookIn)) { + typesAlreadyVisited.add(toLookIn); + ResolvedMemberImpl foundMember = (ResolvedMemberImpl) toLookIn.lookupResolvedMember(memberToMatch, true); + if (foundMember != null && isVisibleTo(memberToMatch, foundMember)) { + List declaringTypes = new ArrayList(); + // declaring type can be unresolved if the member can from + // an ITD... + ResolvedType resolvedDeclaringType = foundMember.getDeclaringType().resolve(toLookIn.getWorld()); + accumulateTypesInBetween(toLookIn, resolvedDeclaringType, declaringTypes); + for (Iterator iter = declaringTypes.iterator(); iter.hasNext();) { + ResolvedType declaringType = (ResolvedType) iter.next(); + // typesAlreadyVisited.add(declaringType); + ResolvedMember member = foundMember.withSubstituteDeclaringType(declaringType); + foundMembers.add(member); + } + if (toLookIn.isParameterizedType() && (foundMember.backingGenericMember != null)) { + foundMembers.add(new JoinPointSignature(foundMember.backingGenericMember, foundMember.declaringType + .resolve(toLookIn.getWorld()))); + } + accumulateMembersMatching(foundMember, toLookIn.getDirectSupertypes(), typesAlreadyVisited, foundMembers); + // if this was a parameterized type, look in the generic + // type that backs it too + } + } + } + } + + /** + * Returns true if the parent member is visible to the child member In the same declaring type this is always true, otherwise if + * parent is private it is false. + * + * @param childMember + * @param parentMember + * @return + */ + private static boolean isVisibleTo(ResolvedMember childMember, ResolvedMember parentMember) { + if (childMember.getDeclaringType().equals(parentMember.getDeclaringType())) + return true; + if (Modifier.isPrivate(parentMember.getModifiers())) { + return false; + } else { + return true; + } + } + + // ---- + + public final int getModifiers(World world) { + return modifiers; + } + + public final int getModifiers() { + return modifiers; + } + + // ---- + + public final UnresolvedType[] getExceptions(World world) { + return getExceptions(); + } + + public UnresolvedType[] getExceptions() { + return checkedExceptions; + } + + public ShadowMunger getAssociatedShadowMunger() { + return null; + } + + // ??? true or false? + public boolean isAjSynthetic() { + return isAjSynthetic; + } + + protected void setAjSynthetic(boolean b) { + isAjSynthetic = b; + } + + public boolean hasAnnotations() { + return (annotationTypes != null); + } + + /** + * Check if this member has an annotation of the specified type. If the member has a backing generic member then this member + * represents a parameterization of a member in a generic type and the annotations available on the backing generic member + * should be used. + * + * @param ofType the type of the annotation being searched for + * @return true if the annotation is found on this member or its backing generic member + */ + public boolean hasAnnotation(UnresolvedType ofType) { + // The ctors don't allow annotations to be specified ... yet - but + // that doesn't mean it is an error to call this method. + // Normally the weaver will be working with subtypes of + // this type - BcelField/BcelMethod + if (backingGenericMember != null) { + if (annotationTypes != null) { + throw new BCException("Unexpectedly found a backing generic member and a local set of annotations"); + } + return backingGenericMember.hasAnnotation(ofType); + } + if (annotationTypes == null) { + return false; + } + return annotationTypes.contains(ofType); + } + + public ResolvedType[] getAnnotationTypes() { + // The ctors don't allow annotations to be specified ... yet - but + // that doesn't mean it is an error to call this method. + // Normally the weaver will be working with subtypes of + // this type - BcelField/BcelMethod + if (backingGenericMember != null) { + if (annotationTypes != null) { + throw new BCException("Unexpectedly found a backing generic member and a local set of annotations"); + } + return backingGenericMember.getAnnotationTypes(); + } + if (annotationTypes == null) + return null; + return (ResolvedType[]) annotationTypes.toArray(new ResolvedType[] {}); + } + + public String getAnnotationDefaultValue() { + throw new UnsupportedOperationException( + "You should resolve this member and call getAnnotationDefaultValue() on the result..."); + } + + public AnnotationAJ[] getAnnotations() { + if (backingGenericMember != null) + return backingGenericMember.getAnnotations(); + return super.getAnnotations(); + } + + public void setAnnotationTypes(UnresolvedType[] annotationtypes) { + if (annotationTypes == null) + annotationTypes = new HashSet(); + for (int i = 0; i < annotationtypes.length; i++) { + UnresolvedType typeX = annotationtypes[i]; + annotationTypes.add(typeX); + } + } + + public ResolvedType[][] getParameterAnnotationTypes() { + if (parameterAnnotationTypes == null) + return null; + return parameterAnnotationTypes; + } + + public AnnotationAJ[][] getParameterAnnotations() { + if (backingGenericMember != null) + return backingGenericMember.getParameterAnnotations(); + throw new BCException("Cannot return parameter annotations for a " + this.getClass().getName() + " member"); + // return super.getParameterAnnotations(); + } + + public void addAnnotation(AnnotationAJ annotation) { + // FIXME asc only allows for annotation types, not instances - should + // it? + if (annotationTypes == null) + annotationTypes = new HashSet(); + annotationTypes.add(annotation.getType()); + } + + public boolean isBridgeMethod() { + return (modifiers & Constants.ACC_BRIDGE) != 0 && getKind().equals(METHOD); + } + + public boolean isVarargsMethod() { + return (modifiers & Constants.ACC_VARARGS) != 0; + } + + public void setVarargsMethod() { + modifiers = modifiers | Constants.ACC_VARARGS; + } + + public boolean isSynthetic() { + // See Bcelmethod.isSynthetic() which takes account of preJava5 + // Synthetic modifier + return (modifiers & 4096) != 0; // do we know better? + } + + public void write(DataOutputStream s) throws IOException { + getKind().write(s); + getDeclaringType().write(s); + s.writeInt(modifiers); + s.writeUTF(getName()); + s.writeUTF(getSignature()); + UnresolvedType.writeArray(getExceptions(), s); + + s.writeInt(getStart()); + s.writeInt(getEnd()); + s.writeBoolean(isVarargsMethod()); + + // Write out any type variables... + if (typeVariables == null) { + s.writeInt(0); + } else { + s.writeInt(typeVariables.length); + for (int i = 0; i < typeVariables.length; i++) { + typeVariables[i].write(s); + } + } + String gsig = getGenericSignature(); + if (getSignature().equals(gsig)) { + s.writeBoolean(false); + } else { + s.writeBoolean(true); + s.writeInt(parameterTypes.length); + for (int i = 0; i < parameterTypes.length; i++) { + UnresolvedType array_element = parameterTypes[i]; + array_element.write(s); + } + returnType.write(s); + } + } + + /** + * Return the member generic signature that would be suitable for inclusion in a class file Signature attribute. For: + * List getThem(T t) {} we would create: (TT;)Ljava/util/List;; + * + * @return the generic signature for the member that could be inserted into a class file + */ + public String getSignatureForAttribute() { + StringBuffer sb = new StringBuffer(); + if (typeVariables != null) { + sb.append("<"); + for (int i = 0; i < typeVariables.length; i++) { + sb.append(typeVariables[i].getSignatureForAttribute()); // need + // a + // 'getSignatureForAttribute()' + } + sb.append(">"); + } + sb.append("("); + for (int i = 0; i < parameterTypes.length; i++) { + ResolvedType ptype = (ResolvedType) parameterTypes[i]; + sb.append(ptype.getSignatureForAttribute()); + } + sb.append(")"); + sb.append(((ResolvedType) returnType).getSignatureForAttribute()); + return sb.toString(); + } + + public String getGenericSignature() { + StringBuffer sb = new StringBuffer(); + if (typeVariables != null) { + sb.append("<"); + for (int i = 0; i < typeVariables.length; i++) { + sb.append(typeVariables[i].getSignature()); + } + sb.append(">"); + } + sb.append("("); + for (int i = 0; i < parameterTypes.length; i++) { + UnresolvedType ptype = parameterTypes[i]; + sb.append(ptype.getSignature()); + } + sb.append(")"); + sb.append(returnType.getSignature()); + return sb.toString(); + } + + public static void writeArray(ResolvedMember[] members, DataOutputStream s) throws IOException { + s.writeInt(members.length); + for (int i = 0, len = members.length; i < len; i++) { + members[i].write(s); + } + } + + public static ResolvedMemberImpl readResolvedMember(VersionedDataInputStream s, ISourceContext sourceContext) + throws IOException { + + ResolvedMemberImpl m = new ResolvedMemberImpl(MemberKind.read(s), UnresolvedType.read(s), s.readInt(), s.readUTF(), s + .readUTF()); + m.checkedExceptions = UnresolvedType.readArray(s); + + m.start = s.readInt(); + m.end = s.readInt(); + m.sourceContext = sourceContext; + + if (s.getMajorVersion() >= AjAttribute.WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ150) { + + if (s.getMajorVersion() >= AjAttribute.WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ150M4) { + boolean isvarargs = s.readBoolean(); + if (isvarargs) + m.setVarargsMethod(); + } + + int tvcount = s.readInt(); + if (tvcount != 0) { + m.typeVariables = new TypeVariable[tvcount]; + for (int i = 0; i < tvcount; i++) { + m.typeVariables[i] = TypeVariable.read(s); + m.typeVariables[i].setDeclaringElement(m); + } + } + if (s.getMajorVersion() >= AjAttribute.WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ150M4) { + boolean hasAGenericSignature = s.readBoolean(); + if (hasAGenericSignature) { + int ps = s.readInt(); + UnresolvedType[] params = new UnresolvedType[ps]; + for (int i = 0; i < params.length; i++) { + params[i] = TypeFactory.createTypeFromSignature(s.readUTF()); + } + UnresolvedType rt = TypeFactory.createTypeFromSignature(s.readUTF()); + m.parameterTypes = params; + m.returnType = rt; + } + } + } + return m; + } + + public static ResolvedMember[] readResolvedMemberArray(VersionedDataInputStream s, ISourceContext context) throws IOException { + int len = s.readInt(); + ResolvedMember[] members = new ResolvedMember[len]; + for (int i = 0; i < len; i++) { + members[i] = ResolvedMemberImpl.readResolvedMember(s, context); + } + return members; + } + + // OPTIMIZE dont like how resolve(world) on ResolvedMemberImpl does + // something different to world.resolve(member) + public ResolvedMember resolve(World world) { + // make sure all the pieces of a resolvedmember really are resolved + try { + if (typeVariables != null && typeVariables.length > 0) { + for (int i = 0; i < typeVariables.length; i++) { + typeVariables[i] = typeVariables[i].resolve(world); + } + } + world.setTypeVariableLookupScope(this); + if (annotationTypes != null) { + Set r = new HashSet(); + for (Iterator iter = annotationTypes.iterator(); iter.hasNext();) { + UnresolvedType element = (UnresolvedType) iter.next(); + r.add(world.resolve(element)); + } + annotationTypes = r; + } + declaringType = declaringType.resolve(world); + if (declaringType.isRawType()) + declaringType = ((ReferenceType) declaringType).getGenericType(); + + if (parameterTypes != null && parameterTypes.length > 0) { + for (int i = 0; i < parameterTypes.length; i++) { + parameterTypes[i] = parameterTypes[i].resolve(world); + } + } + + returnType = returnType.resolve(world); + + } finally { + world.setTypeVariableLookupScope(null); + } + return this; + } + + public ISourceContext getSourceContext(World world) { + return getDeclaringType().resolve(world).getSourceContext(); + } + + public String[] getParameterNames() { + return parameterNames; + } + + public final void setParameterNames(String[] pnames) { + parameterNames = pnames; + } + + public final String[] getParameterNames(World world) { + return getParameterNames(); + } + + public AjAttribute.EffectiveSignatureAttribute getEffectiveSignature() { + return null; + } + + public ISourceLocation getSourceLocation() { + // System.out.println("get context: " + this + " is " + sourceContext); + if (getSourceContext() == null) { + // System.err.println("no context: " + this); + return null; + } + return getSourceContext().makeSourceLocation(this); + } + + public int getEnd() { + return end; + } + + public ISourceContext getSourceContext() { + return sourceContext; + } + + public int getStart() { + return start; + } + + public void setPosition(int sourceStart, int sourceEnd) { + this.start = sourceStart; + this.end = sourceEnd; + } + + public void setDeclaringType(ReferenceType rt) { + declaringType = rt; + } + + public void setSourceContext(ISourceContext sourceContext) { + this.sourceContext = sourceContext; + } + + public boolean isAbstract() { + return Modifier.isAbstract(modifiers); + } + + public boolean isPublic() { + return Modifier.isPublic(modifiers); + } + + public boolean isProtected() { + return Modifier.isProtected(modifiers); + } + + public boolean isNative() { + return Modifier.isNative(modifiers); + } + + public boolean isDefault() { + return !(isPublic() || isProtected() || isPrivate()); + } + + public boolean isVisible(ResolvedType fromType) { + World world = fromType.getWorld(); + return ResolvedType.isVisible(getModifiers(), getDeclaringType().resolve(world), fromType); + } + + public void setCheckedExceptions(UnresolvedType[] checkedExceptions) { + this.checkedExceptions = checkedExceptions; + } + + public void setAnnotatedElsewhere(boolean b) { + isAnnotatedElsewhere = b; + } + + public boolean isAnnotatedElsewhere() { + return isAnnotatedElsewhere; + } + + /** + * Get the UnresolvedType for the return type, taking generic signature into account + */ + public UnresolvedType getGenericReturnType() { + return getReturnType(); + } + + /** + * Get the TypeXs of the parameter types, taking generic signature into account + */ + public UnresolvedType[] getGenericParameterTypes() { + return getParameterTypes(); + } + + public ResolvedMemberImpl parameterizedWith(UnresolvedType[] typeParameters, ResolvedType newDeclaringType, + boolean isParameterized) { + return parameterizedWith(typeParameters, newDeclaringType, isParameterized, null); + } + + /** + * Return a resolvedmember in which all the type variables in the signature have been replaced with the given bindings. The + * 'isParameterized' flag tells us whether we are creating a raw type version or not. if (isParameterized) then List will + * turn into List (for example) - if (!isParameterized) then List will turn into List. + */ + public ResolvedMemberImpl parameterizedWith(UnresolvedType[] typeParameters, ResolvedType newDeclaringType, + boolean isParameterized, List aliases) { + if (// isParameterized && <-- might need this bit... + !getDeclaringType().isGenericType()) { + throw new IllegalStateException("Can't ask to parameterize a member of non-generic type: " + getDeclaringType() + + " kind(" + getDeclaringType().typeKind + ")"); + } + TypeVariable[] typeVariables = getDeclaringType().getTypeVariables(); + if (isParameterized && (typeVariables.length != typeParameters.length)) { + throw new IllegalStateException("Wrong number of type parameters supplied"); + } + Map typeMap = new HashMap(); + boolean typeParametersSupplied = typeParameters != null && typeParameters.length > 0; + if (typeVariables != null) { + // If no 'replacements' were supplied in the typeParameters array + // then collapse + // type variables to their first bound. + for (int i = 0; i < typeVariables.length; i++) { + UnresolvedType ut = (!typeParametersSupplied ? typeVariables[i].getFirstBound() : typeParameters[i]); + typeMap.put(typeVariables[i].getName(), ut); + } + } + // For ITDs on generic types that use type variables from the target + // type, the aliases + // record the alternative names used throughout the ITD expression that + // must map to + // the same value as the type variables real name. + if (aliases != null) { + int posn = 0; + for (Iterator iter = aliases.iterator(); iter.hasNext();) { + String typeVariableAlias = (String) iter.next(); + typeMap.put(typeVariableAlias, (!typeParametersSupplied ? typeVariables[posn].getFirstBound() + : typeParameters[posn])); + posn++; + } + } + + UnresolvedType parameterizedReturnType = parameterize(getGenericReturnType(), typeMap, isParameterized); + UnresolvedType[] parameterizedParameterTypes = new UnresolvedType[getGenericParameterTypes().length]; + UnresolvedType[] genericParameterTypes = getGenericParameterTypes(); + for (int i = 0; i < parameterizedParameterTypes.length; i++) { + parameterizedParameterTypes[i] = parameterize(genericParameterTypes[i], typeMap, isParameterized); + } + ResolvedMemberImpl ret = new ResolvedMemberImpl(getKind(), newDeclaringType, getModifiers(), parameterizedReturnType, + getName(), parameterizedParameterTypes, getExceptions(), this); + ret.setTypeVariables(getTypeVariables()); + ret.setSourceContext(getSourceContext()); + ret.setPosition(getStart(), getEnd()); + ret.setParameterNames(getParameterNames()); + return ret; + } + + /** + * Replace occurrences of type variables in the signature with values contained in the map. The map is of the form + * A=String,B=Integer and so a signature List Foo.m(B i) {} would become List Foo.m(Integer i) {} + */ + public ResolvedMember parameterizedWith(Map m, World w) { + // if (//isParameterized && <-- might need this bit... + // !getDeclaringType().isGenericType()) { + // throw new IllegalStateException( + // "Can't ask to parameterize a member of non-generic type: " + // +getDeclaringType()+" kind("+ + // getDeclaringType().typeKind+")"); + // } + declaringType = declaringType.resolve(w); + if (declaringType.isRawType()) + declaringType = ((ResolvedType) declaringType).getGenericType(); + // TypeVariable[] typeVariables = getDeclaringType().getTypeVariables(); + // if (isParameterized && (typeVariables.length != + // typeParameters.length)) { + // throw new + // IllegalStateException("Wrong number of type parameters supplied"); + // } + // Map typeMap = new HashMap(); + // boolean typeParametersSupplied = typeParameters!=null && + // typeParameters.length>0; + // if (typeVariables!=null) { + // // If no 'replacements' were supplied in the typeParameters array + // then collapse + // // type variables to their first bound. + // for (int i = 0; i < typeVariables.length; i++) { + // UnresolvedType ut = + // (!typeParametersSupplied?typeVariables[i].getFirstBound + // ():typeParameters[i]); + // typeMap.put(typeVariables[i].getName(),ut); + // } + // } + // // For ITDs on generic types that use type variables from the target + // type, the aliases + // // record the alternative names used throughout the ITD expression + // that must map to + // // the same value as the type variables real name. + // if (aliases!=null) { + // int posn = 0; + // for (Iterator iter = aliases.iterator(); iter.hasNext();) { + // String typeVariableAlias = (String) iter.next(); + // typeMap.put(typeVariableAlias,(!typeParametersSupplied?typeVariables[ + // posn].getFirstBound():typeParameters[posn])); + // posn++; + // } + // } + + UnresolvedType parameterizedReturnType = parameterize(getGenericReturnType(), m, true, w); + UnresolvedType[] parameterizedParameterTypes = new UnresolvedType[getGenericParameterTypes().length]; + UnresolvedType[] genericParameterTypes = getGenericParameterTypes(); + for (int i = 0; i < parameterizedParameterTypes.length; i++) { + parameterizedParameterTypes[i] = parameterize(genericParameterTypes[i], m, true, w); + } + ResolvedMemberImpl ret = new ResolvedMemberImpl(getKind(), declaringType, getModifiers(), parameterizedReturnType, + getName(), parameterizedParameterTypes, getExceptions(), this); + ret.setTypeVariables(getTypeVariables()); + ret.setSourceContext(getSourceContext()); + ret.setPosition(getStart(), getEnd()); + ret.setParameterNames(getParameterNames()); + return ret; + } + + public void setTypeVariables(TypeVariable[] tvars) { + typeVariables = tvars; + } + + public TypeVariable[] getTypeVariables() { + return typeVariables; + } + + protected UnresolvedType parameterize(UnresolvedType aType, Map typeVariableMap, boolean inParameterizedType) { + return parameterize(aType, typeVariableMap, inParameterizedType, null); + } + + protected UnresolvedType parameterize(UnresolvedType aType, Map typeVariableMap, boolean inParameterizedType, World w) { + if (aType instanceof TypeVariableReference) { + String variableName = ((TypeVariableReference) aType).getTypeVariable().getName(); + if (!typeVariableMap.containsKey(variableName)) { + return aType; // if the type variable comes from the method (and + // not the type) thats OK + } + return (UnresolvedType) typeVariableMap.get(variableName); + } else if (aType.isParameterizedType()) { + if (inParameterizedType) { + // if (!(getDeclaringType() instanceof ResolvedType)) { + // int stop = 1; + // } + // if (aType!=null) {// instanceof UnresolvedType) { + if (w != null) + aType = aType.resolve(w); + else { + aType = aType.resolve(((ResolvedType) getDeclaringType()).getWorld()); + } + // } + return aType.parameterize(typeVariableMap); + } else { + return aType.getRawType(); + } + } else if (aType.isArray()) { + // The component type might be a type variable (pr150095) + int dims = 1; + String sig = aType.getSignature(); + // while (sig.charAt(dims) == '[') + // dims++; + UnresolvedType arrayType = null; + UnresolvedType componentSig = UnresolvedType.forSignature(sig.substring(dims)); + UnresolvedType parameterizedComponentSig = parameterize(componentSig, typeVariableMap, inParameterizedType); + if (parameterizedComponentSig.isTypeVariableReference() + && parameterizedComponentSig instanceof UnresolvedTypeVariableReferenceType + && typeVariableMap.containsKey(((UnresolvedTypeVariableReferenceType) parameterizedComponentSig) + .getTypeVariable().getName())) { // pr250632 + // TODO ASC bah, this code is rubbish - i should fix it properly + StringBuffer newsig = new StringBuffer(); + newsig.append("[T"); + newsig.append(((UnresolvedTypeVariableReferenceType) parameterizedComponentSig).getTypeVariable().getName()); + newsig.append(";"); + arrayType = UnresolvedType.forSignature(newsig.toString()); + } else { + arrayType = ResolvedType.makeArray(parameterizedComponentSig, dims); + } + return arrayType; + } + return aType; + } + + /** + * If this member is defined by a parameterized super-type, return the erasure of that member. For example: interface I { T + * foo(T aTea); } class C implements I { String foo(String aString) { return "something"; } } The resolved member for + * C.foo has signature String foo(String). The erasure of that member is Object foo(Object) -- use upper bound of type variable. + * A type is a supertype of itself. + */ + // public ResolvedMember getErasure() { + // if (calculatedMyErasure) return myErasure; + // calculatedMyErasure = true; + // ResolvedType resolvedDeclaringType = (ResolvedType) getDeclaringType(); + // // this next test is fast, and the result is cached. + // if (!resolvedDeclaringType.hasParameterizedSuperType()) { + // return null; + // } else { + // // we have one or more parameterized super types. + // // this member may be defined by one of them... we need to find out. + // Collection declaringTypes = + // this.getDeclaringTypes(resolvedDeclaringType.getWorld()); + // for (Iterator iter = declaringTypes.iterator(); iter.hasNext();) { + // ResolvedType aDeclaringType = (ResolvedType) iter.next(); + // if (aDeclaringType.isParameterizedType()) { + // // we've found the (a?) parameterized type that defines this member. + // // now get the erasure of it + // ResolvedMemberImpl matchingMember = (ResolvedMemberImpl) + // aDeclaringType.lookupMemberNoSupers(this); + // if (matchingMember != null && matchingMember.backingGenericMember != + // null) { + // myErasure = matchingMember.backingGenericMember; + // return myErasure; + // } + // } + // } + // } + // return null; + // } + // + // private ResolvedMember myErasure = null; + // private boolean calculatedMyErasure = false; + public boolean hasBackingGenericMember() { + return backingGenericMember != null; + } + + public ResolvedMember getBackingGenericMember() { + return backingGenericMember; + } + + /** + * For ITDs, we use the default factory methods to build a resolved member, then alter a couple of characteristics using this + * method - this is safe. + */ + public void resetName(String newName) { + this.name = newName; + } + + public void resetKind(MemberKind newKind) { + this.kind = newKind; + } + + public void resetModifiers(int newModifiers) { + this.modifiers = newModifiers; + } + + public void resetReturnTypeToObjectArray() { + returnType = UnresolvedType.OBJECTARRAY; + } + + /** + * Returns a copy of this member but with the declaring type swapped. Copy only needs to be shallow. + * + * @param newDeclaringType + */ + public JoinPointSignature withSubstituteDeclaringType(ResolvedType newDeclaringType) { + JoinPointSignature ret = new JoinPointSignature(this, newDeclaringType); + return ret; + } + + /** + * Returns true if this member matches the other. The matching takes into account name and parameter types only. When comparing + * parameter types, we allow any type variable to match any other type variable regardless of bounds. + */ + public boolean matches(ResolvedMember aCandidateMatch) { + ResolvedMemberImpl candidateMatchImpl = (ResolvedMemberImpl) aCandidateMatch; + if (!getName().equals(aCandidateMatch.getName())) + return false; + UnresolvedType[] myParameterTypes = getGenericParameterTypes(); + UnresolvedType[] candidateParameterTypes = aCandidateMatch.getGenericParameterTypes(); + if (myParameterTypes.length != candidateParameterTypes.length) + return false; + String myParameterSignature = getParameterSigWithBoundsRemoved(); + String candidateParameterSignature = candidateMatchImpl.getParameterSigWithBoundsRemoved(); + if (myParameterSignature.equals(candidateParameterSignature)) { + return true; + } else { + // try erasure + myParameterSignature = getParameterSignatureErased(); + candidateParameterSignature = candidateMatchImpl.getParameterSignatureErased(); + return myParameterSignature.equals(candidateParameterSignature); + } + } + + /** + * converts e.g. .... List to just Ljava/util/List; whereas the full signature would be + * Ljava/util/List; + */ + private String myParameterSignatureWithBoundsRemoved = null; + /** + * converts e.g. .... List to just Ljava/util/List; + */ + private String myParameterSignatureErasure = null; + + // does NOT produce a meaningful java signature, but does give a unique + // string suitable for + // comparison. + private String getParameterSigWithBoundsRemoved() { + if (myParameterSignatureWithBoundsRemoved != null) + return myParameterSignatureWithBoundsRemoved; + StringBuffer sig = new StringBuffer(); + UnresolvedType[] myParameterTypes = getGenericParameterTypes(); + for (int i = 0; i < myParameterTypes.length; i++) { + appendSigWithTypeVarBoundsRemoved(myParameterTypes[i], sig, new HashSet()); + } + myParameterSignatureWithBoundsRemoved = sig.toString(); + return myParameterSignatureWithBoundsRemoved; + } + + /** + * Return the erased form of the signature with bounds collapsed for type variables, etc. Does not include the return type, @see + * getParam + */ + public String getParameterSignatureErased() { + if (myParameterSignatureErasure != null) + return myParameterSignatureErasure; + StringBuffer sig = new StringBuffer(); + UnresolvedType[] myParameterTypes = getParameterTypes(); + for (int i = 0; i < myParameterTypes.length; i++) { + UnresolvedType thisParameter = myParameterTypes[i]; + if (thisParameter.isTypeVariableReference()) { + TypeVariableReferenceType typeVariableRT = (TypeVariableReferenceType) thisParameter; + sig.append(typeVariableRT.getUpperBound().getSignature()); + } else { + sig.append(thisParameter.getErasureSignature()); + } + } + myParameterSignatureErasure = sig.toString(); + return myParameterSignatureErasure; + } + + public String getSignatureErased() { + StringBuffer sb = new StringBuffer(); + sb.append("("); + sb.append(getParameterSignatureErased()); + sb.append(")"); + sb.append(getReturnType().getErasureSignature()); + return sb.toString(); + } + + // does NOT produce a meaningful java signature, but does give a unique + // string suitable for + // comparison. + public static void appendSigWithTypeVarBoundsRemoved(UnresolvedType aType, StringBuffer toBuffer, Set alreadyUsedTypeVars) { + if (aType.isTypeVariableReference()) { + TypeVariableReferenceType typeVariableRT = (TypeVariableReferenceType) aType; + // pr204505 + if (alreadyUsedTypeVars.contains(aType)) { + toBuffer.append("..."); + } else { + alreadyUsedTypeVars.add(aType); + appendSigWithTypeVarBoundsRemoved(typeVariableRT.getUpperBound(), toBuffer, alreadyUsedTypeVars); + } + // toBuffer.append("T;"); + } else if (aType.isParameterizedType()) { + toBuffer.append(aType.getRawType().getSignature()); + toBuffer.append("<"); + for (int i = 0; i < aType.getTypeParameters().length; i++) { + appendSigWithTypeVarBoundsRemoved(aType.getTypeParameters()[i], toBuffer, alreadyUsedTypeVars); + } + toBuffer.append(">;"); + } else { + toBuffer.append(aType.getSignature()); + } + } + + /** + * Useful for writing tests, returns *everything* we know about this member. + */ + public String toDebugString() { + StringBuffer r = new StringBuffer(); + + // modifiers + int mods = modifiers; + if ((mods & 4096) > 0) + mods = mods - 4096; // remove synthetic (added in the ASM case but + // not in the BCEL case...) + if ((mods & 512) > 0) + mods = mods - 512; // remove interface (added in the BCEL case but + // not in the ASM case...) + if ((mods & 131072) > 0) + mods = mods - 131072; // remove deprecated (added in the ASM case + // but not in the BCEL case...) + String modsStr = Modifier.toString(mods); + if (modsStr.length() != 0) + r.append(modsStr).append("(" + mods + ")").append(" "); + + // type variables + if (typeVariables != null && typeVariables.length > 0) { + r.append("<"); + for (int i = 0; i < typeVariables.length; i++) { + if (i > 0) + r.append(","); + TypeVariable t = typeVariables[i]; + r.append(t.toDebugString()); + } + r.append("> "); + } + + // 'declaring' type + r.append(getGenericReturnType().toDebugString()); + r.append(' '); + + // name + r.append(declaringType.getName()); + r.append('.'); + r.append(name); + + // parameter signature if a method + if (kind != FIELD) { + r.append("("); + UnresolvedType[] params = getGenericParameterTypes(); + boolean parameterNamesExist = showParameterNames && parameterNames != null && parameterNames.length == params.length; + if (params.length != 0) { + for (int i = 0, len = params.length; i < len; i++) { + if (i > 0) + r.append(", "); + r.append(params[i].toDebugString()); + if (parameterNamesExist) + r.append(" ").append(parameterNames[i]); + } + } + r.append(")"); + } + return r.toString(); + } + + // SECRETAPI - controlling whether parameter names come out in the debug + // string (for testing purposes) + public static boolean showParameterNames = true; + + public String toGenericString() { + StringBuffer buf = new StringBuffer(); + buf.append(getGenericReturnType().getSimpleName()); + buf.append(' '); + buf.append(declaringType.getName()); + buf.append('.'); + buf.append(name); + if (kind != FIELD) { + buf.append("("); + UnresolvedType[] params = getGenericParameterTypes(); + if (params.length != 0) { + buf.append(params[0].getSimpleName()); + for (int i = 1, len = params.length; i < len; i++) { + buf.append(", "); + buf.append(params[i].getSimpleName()); + } + } + buf.append(")"); + } + return buf.toString(); + } + + public boolean isCompatibleWith(Member am) { + if (kind != METHOD || am.getKind() != METHOD) + return true; + if (!name.equals(am.getName())) + return true; + if (!equalTypes(getParameterTypes(), am.getParameterTypes())) + return true; + return getReturnType().equals(am.getReturnType()); + } + + private static boolean equalTypes(UnresolvedType[] a, UnresolvedType[] b) { + int len = a.length; + if (len != b.length) + return false; + for (int i = 0; i < len; i++) { + if (!a[i].equals(b[i])) + return false; + } + return true; + } + + public TypeVariable getTypeVariableNamed(String name) { + // Check locally... + if (typeVariables != null) { + for (int i = 0; i < typeVariables.length; i++) { + if (typeVariables[i].getName().equals(name)) + return typeVariables[i]; + } + } + // check the declaring type! + return declaringType.getTypeVariableNamed(name); + + // Do generic aspects with ITDs that share type variables with the + // aspect and the target type and have their own tvars cause + // this to be messier? + } + + public void evictWeavingState() { + } + + public AnnotationAJ getAnnotationOfType(UnresolvedType ofType) { + throw new UnsupportedOperationException("You should resolve this member and call getAnnotationOfType() on the result..."); + } + + public boolean isEquivalentTo(Object other) { + return this.equals(other); + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/ResolvedPointcutDefinition.java b/org.aspectj.matcher/src/org/aspectj/weaver/ResolvedPointcutDefinition.java new file mode 100644 index 000000000..55e2741c8 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/ResolvedPointcutDefinition.java @@ -0,0 +1,165 @@ +/* ******************************************************************* + * 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.DataOutputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.aspectj.weaver.patterns.Pointcut; + + +public class ResolvedPointcutDefinition extends ResolvedMemberImpl { + private Pointcut pointcut; + + public ResolvedPointcutDefinition( + UnresolvedType declaringType, + int modifiers, + String name, + UnresolvedType[] parameterTypes, + Pointcut pointcut) + { + this(declaringType, modifiers, name, parameterTypes, ResolvedType.VOID, pointcut); + } + + /** + * An instance which can be given a specific returnType, used f.e. in if() pointcut for @AJ + * + * @param declaringType + * @param modifiers + * @param name + * @param parameterTypes + * @param returnType + * @param pointcut + */ + public ResolvedPointcutDefinition( + UnresolvedType declaringType, + int modifiers, + String name, + UnresolvedType[] parameterTypes, + UnresolvedType returnType, + Pointcut pointcut) + { + super( + POINTCUT, + declaringType, + modifiers, + returnType, + name, + parameterTypes); + this.pointcut = pointcut; + //XXXpointcut.assertState(Pointcut.RESOLVED); + checkedExceptions = UnresolvedType.NONE; + } + + // ---- + + public void write(DataOutputStream s) throws IOException { + getDeclaringType().write(s); + s.writeInt(getModifiers()); + s.writeUTF(getName()); + UnresolvedType.writeArray(getParameterTypes(), s); + pointcut.write(s); + } + + public static ResolvedPointcutDefinition read(VersionedDataInputStream s, ISourceContext context) throws IOException { + ResolvedPointcutDefinition rpd = + new ResolvedPointcutDefinition( + UnresolvedType.read(s), + s.readInt(), + s.readUTF(), + UnresolvedType.readArray(s), + Pointcut.read(s, context)); + rpd.setSourceContext(context); // whilst we have a source context, let's remember it + return rpd; + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("pointcut "); + buf.append((getDeclaringType()==null?"":getDeclaringType().getName())); + buf.append("."); + buf.append(getName()); + buf.append("("); + for (int i=0; i < getParameterTypes().length; i++) { + if (i > 0) buf.append(", "); + buf.append(getParameterTypes()[i].toString()); + } + buf.append(")"); + //buf.append(pointcut); + + return buf.toString(); + } + + public Pointcut getPointcut() { + return pointcut; + } + + public boolean isAjSynthetic() { + return true; + } + + /** + * Called when asking a parameterized super-aspect for its pointcuts. + */ + public ResolvedMemberImpl parameterizedWith(UnresolvedType[] typeParameters, ResolvedType newDeclaringType, boolean isParameterized) { + TypeVariable[] typeVariables = getDeclaringType().resolve(newDeclaringType.getWorld()).getTypeVariables(); + if (isParameterized && (typeVariables.length != typeParameters.length)) { + throw new IllegalStateException("Wrong number of type parameters supplied"); + } + Map typeMap = new HashMap(); + boolean typeParametersSupplied = typeParameters!=null && typeParameters.length>0; + if (typeVariables!=null) { + // If no 'replacements' were supplied in the typeParameters array then collapse + // type variables to their first bound. + for (int i = 0; i < typeVariables.length; i++) { + UnresolvedType ut = (!typeParametersSupplied?typeVariables[i].getFirstBound():typeParameters[i]); + typeMap.put(typeVariables[i].getName(),ut); + } + } + UnresolvedType parameterizedReturnType = parameterize(getGenericReturnType(),typeMap,isParameterized); + UnresolvedType[] parameterizedParameterTypes = new UnresolvedType[getGenericParameterTypes().length]; + for (int i = 0; i < parameterizedParameterTypes.length; i++) { + parameterizedParameterTypes[i] = + parameterize(getGenericParameterTypes()[i], typeMap,isParameterized); + } + ResolvedPointcutDefinition ret = new ResolvedPointcutDefinition( + newDeclaringType, + getModifiers(), + getName(), + parameterizedParameterTypes, + parameterizedReturnType, + pointcut.parameterizeWith(typeMap,newDeclaringType.getWorld()) + ); + ret.setTypeVariables(getTypeVariables()); + ret.setSourceContext(getSourceContext()); + ret.setPosition(getStart(),getEnd()); + ret.setParameterNames(getParameterNames()); + return ret; + //return this; + } + + // for testing + public static final ResolvedPointcutDefinition DUMMY = + new ResolvedPointcutDefinition(UnresolvedType.OBJECT, 0, "missing", + UnresolvedType.NONE, Pointcut.makeMatchesNothing(Pointcut.RESOLVED)); + + public static final ResolvedPointcutDefinition[] NO_POINTCUTS = new ResolvedPointcutDefinition[]{}; + + public void setPointcut(Pointcut pointcut) { + this.pointcut = pointcut; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/ResolvedType.java b/org.aspectj.matcher/src/org/aspectj/weaver/ResolvedType.java new file mode 100644 index 000000000..670eb3af2 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/ResolvedType.java @@ -0,0 +1,2201 @@ +/* ******************************************************************* + * 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; + +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +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.bridge.IMessage; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.Message; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.patterns.Declare; +import org.aspectj.weaver.patterns.PerClause; + +public abstract class ResolvedType extends UnresolvedType implements AnnotatedElement { + + public static final ResolvedType[] EMPTY_RESOLVED_TYPE_ARRAY = new ResolvedType[0]; + public static final String PARAMETERIZED_TYPE_IDENTIFIER = "P"; + + // Set during a type pattern match call - this currently used to hold the + // annotations + // that may be attached to a type when it used as a parameter + public ResolvedType[] temporaryAnnotationTypes; + private ResolvedType[] resolvedTypeParams; + private String binaryPath; + + protected World world; + + protected ResolvedType(String signature, World world) { + super(signature); + this.world = world; + } + + protected ResolvedType(String signature, String signatureErasure, World world) { + super(signature, signatureErasure); + this.world = world; + } + + // ---- things that don't require a world + + /** + * Returns an iterator through ResolvedType objects representing all the direct supertypes of this type. That is, through the + * superclass, if any, and all declared interfaces. + */ + public final Iterator getDirectSupertypes() { + Iterator ifacesIterator = Iterators.array(getDeclaredInterfaces()); + ResolvedType superclass = getSuperclass(); + if (superclass == null) { + return ifacesIterator; + } else { + return Iterators.snoc(ifacesIterator, superclass); + } + } + + public abstract ResolvedMember[] getDeclaredFields(); + + public abstract ResolvedMember[] getDeclaredMethods(); + + public abstract ResolvedType[] getDeclaredInterfaces(); + + public abstract ResolvedMember[] getDeclaredPointcuts(); + + /** + * Returns a ResolvedType object representing the superclass of this type, or null. If this represents a java.lang.Object, a + * primitive type, or void, this method returns null. + */ + public abstract ResolvedType getSuperclass(); + + /** + * Returns the modifiers for this type. + *

+ * See {@link Class#getModifiers()} for a description of the weirdness of this methods on primitives and arrays. + * + * @param world the {@link World} in which the lookup is made. + * @return an int representing the modifiers for this type + * @see java.lang.reflect.Modifier + */ + public abstract int getModifiers(); + + // return true if this resolved type couldn't be found (but we know it's + // name maybe) + public boolean isMissing() { + return false; + } + + // FIXME asc I wonder if in some circumstances MissingWithKnownSignature + // should not be considered + // 'really' missing as some code can continue based solely on the signature + public static boolean isMissing(UnresolvedType unresolved) { + if (unresolved instanceof ResolvedType) { + ResolvedType resolved = (ResolvedType) unresolved; + return resolved.isMissing(); + } else + return (unresolved == MISSING); + } + + public ResolvedType[] getAnnotationTypes() { + return EMPTY_RESOLVED_TYPE_ARRAY; + } + + public AnnotationAJ getAnnotationOfType(UnresolvedType ofType) { + return null; + } + + // public final UnresolvedType getSuperclass(World world) { + // return getSuperclass(); + // } + + // This set contains pairs of types whose signatures are concatenated + // together, this means with a fast lookup we can tell if two types + // are equivalent. + protected static Set validBoxing = new HashSet(); + + static { + validBoxing.add("Ljava/lang/Byte;B"); + validBoxing.add("Ljava/lang/Character;C"); + validBoxing.add("Ljava/lang/Double;D"); + validBoxing.add("Ljava/lang/Float;F"); + validBoxing.add("Ljava/lang/Integer;I"); + validBoxing.add("Ljava/lang/Long;J"); + validBoxing.add("Ljava/lang/Short;S"); + validBoxing.add("Ljava/lang/Boolean;Z"); + validBoxing.add("BLjava/lang/Byte;"); + validBoxing.add("CLjava/lang/Character;"); + validBoxing.add("DLjava/lang/Double;"); + validBoxing.add("FLjava/lang/Float;"); + validBoxing.add("ILjava/lang/Integer;"); + validBoxing.add("JLjava/lang/Long;"); + validBoxing.add("SLjava/lang/Short;"); + validBoxing.add("ZLjava/lang/Boolean;"); + } + + // utilities + public ResolvedType getResolvedComponentType() { + return null; + } + + public World getWorld() { + return world; + } + + // ---- things from object + + public final boolean equals(Object other) { + if (other instanceof ResolvedType) { + return this == other; + } else { + return super.equals(other); + } + } + + // ---- difficult things + + /** + * returns an iterator through all of the fields of this type, in order for checking from JVM spec 2ed 5.4.3.2. This means that + * the order is + *

+ *

    + *
  • fields from current class
  • + *
  • recur into direct superinterfaces
  • + *
  • recur into superclass
  • + *
+ *

+ * We keep a hashSet of interfaces that we've visited so we don't spiral out into 2^n land. + */ + public Iterator getFields() { + final Iterators.Filter dupFilter = Iterators.dupFilter(); + Iterators.Getter typeGetter = new Iterators.Getter() { + public Iterator get(Object o) { + return dupFilter.filter(((ResolvedType) o).getDirectSupertypes()); + } + }; + Iterators.Getter fieldGetter = new Iterators.Getter() { + public Iterator get(Object o) { + return Iterators.array(((ResolvedType) o).getDeclaredFields()); + } + }; + return Iterators.mapOver(Iterators.recur(this, typeGetter), fieldGetter); + } + + /** + * returns an iterator through all of the methods of this type, in order for checking from JVM spec 2ed 5.4.3.3. This means that + * the order is + *

+ *

    + *
  • methods from current class
  • + *
  • recur into superclass, all the way up, not touching interfaces
  • + *
  • recur into all superinterfaces, in some unspecified order
  • + *
+ *

+ * We keep a hashSet of interfaces that we've visited so we don't spiral out into 2^n land. NOTE: Take a look at the javadoc on + * getMethodsWithoutIterator() to see if you are sensitive to a quirk in getMethods() + */ + public Iterator getMethods() { + final Iterators.Filter dupFilter = Iterators.dupFilter(); + Iterators.Getter ifaceGetter = new Iterators.Getter() { + public Iterator get(Object o) { + return dupFilter.filter(Iterators.array(((ResolvedType) o).getDeclaredInterfaces())); + } + }; + Iterators.Getter methodGetter = new Iterators.Getter() { + public Iterator get(Object o) { + return Iterators.array(((ResolvedType) o).getDeclaredMethods()); + } + }; + return Iterators.mapOver(Iterators.append(new Iterator() { + ResolvedType curr = ResolvedType.this; + + public boolean hasNext() { + return curr != null; + } + + public Object next() { + ResolvedType ret = curr; + curr = curr.getSuperclass(); + return ret; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }, Iterators.recur(this, ifaceGetter)), methodGetter); + } + + /** + * Return a list of methods, first those declared on this class, then those declared on the superclass (recurse) and then those + * declared on the superinterfaces. The getMethods() call above doesn't quite work the same as it will (through the iterator) + * return methods declared on *this* class twice, once at the start and once at the end - I couldn't debug that problem, so + * created this alternative. + */ + public List getMethodsWithoutIterator(boolean includeITDs, boolean allowMissing) { + List methods = new ArrayList(); + Set knowninterfaces = new HashSet(); + addAndRecurse(knowninterfaces, methods, this, includeITDs, allowMissing); + return methods; + } + + private void addAndRecurse(Set knowninterfaces, List collector, ResolvedType rtx, boolean includeITDs, boolean allowMissing) { + collector.addAll(Arrays.asList(rtx.getDeclaredMethods())); // Add the + // methods + // declared + // on this + // type + // now add all the inter-typed members too + if (includeITDs && rtx.interTypeMungers != null) { + for (Iterator i = interTypeMungers.iterator(); i.hasNext();) { + ConcreteTypeMunger tm = (ConcreteTypeMunger) i.next(); + ResolvedMember rm = tm.getSignature(); + if (rm != null) { // new parent type munger can have null + // signature... + collector.add(tm.getSignature()); + } + } + } + if (!rtx.equals(ResolvedType.OBJECT)) { + ResolvedType superType = rtx.getSuperclass(); + if (superType != null && !superType.isMissing()) { + addAndRecurse(knowninterfaces, collector, superType, includeITDs, allowMissing); // Recurse if we aren't at + // the top + } + } + ResolvedType[] interfaces = rtx.getDeclaredInterfaces(); // Go through + // the + // interfaces + // on the + // way back + // down + for (int i = 0; i < interfaces.length; i++) { + ResolvedType iface = interfaces[i]; + + // we need to know if it is an interface from Parent kind munger + // as those are used for @AJ ITD and we precisely want to skip those + boolean shouldSkip = false; + for (int j = 0; j < rtx.interTypeMungers.size(); j++) { + ConcreteTypeMunger munger = (ConcreteTypeMunger) rtx.interTypeMungers.get(j); + if (munger.getMunger() != null && munger.getMunger().getKind() == ResolvedTypeMunger.Parent + && ((NewParentTypeMunger) munger.getMunger()).getNewParent().equals(iface) // pr171953 + ) { + shouldSkip = true; + break; + } + } + + if (!shouldSkip && !knowninterfaces.contains(iface)) { // Dont do + // interfaces + // more than + // once + knowninterfaces.add(iface); + if (allowMissing && iface.isMissing()) { + if (iface instanceof MissingResolvedTypeWithKnownSignature) { + ((MissingResolvedTypeWithKnownSignature) iface).raiseWarningOnMissingInterfaceWhilstFindingMethods(); + } + } else { + addAndRecurse(knowninterfaces, collector, iface, includeITDs, allowMissing); + } + } + } + } + + public ResolvedType[] getResolvedTypeParameters() { + if (resolvedTypeParams == null) { + resolvedTypeParams = world.resolve(typeParameters); + } + return resolvedTypeParams; + } + + /** + * described in JVM spec 2ed 5.4.3.2 + */ + public ResolvedMember lookupField(Member m) { + return lookupMember(m, getFields()); + } + + /** + * described in JVM spec 2ed 5.4.3.3. Doesnt check ITDs. + */ + public ResolvedMember lookupMethod(Member m) { + return lookupMember(m, getMethods()); + } + + public ResolvedMember lookupMethodInITDs(Member m) { + if (interTypeMungers != null) { + for (Iterator i = interTypeMungers.iterator(); i.hasNext();) { + ConcreteTypeMunger tm = (ConcreteTypeMunger) i.next(); + if (matches(tm.getSignature(), m)) { + return tm.getSignature(); + } + } + } + return null; + } + + /** + * return null if not found + */ + private ResolvedMember lookupMember(Member m, Iterator i) { + while (i.hasNext()) { + ResolvedMember f = (ResolvedMember) i.next(); + if (matches(f, m)) + return f; + if (f.hasBackingGenericMember() && m.getName().equals(f.getName())) { // might + // be + // worth + // checking + // the + // method + // behind + // the + // parameterized method (see pr137496) + if (matches(f.getBackingGenericMember(), m)) + return f; + } + } + return null; // ResolvedMember.Missing; + // throw new BCException("can't find " + m); + } + + /** + * return null if not found + */ + private ResolvedMember lookupMember(Member m, ResolvedMember[] a) { + for (int i = 0; i < a.length; i++) { + ResolvedMember f = a[i]; + if (matches(f, m)) + return f; + } + return null; + } + + /** + * Looks for the first member in the hierarchy matching aMember. This method differs from lookupMember(Member) in that it takes + * into account parameters which are type variables - which clearly an unresolved Member cannot do since it does not know + * anything about type variables. + */ + public ResolvedMember lookupResolvedMember(ResolvedMember aMember, boolean allowMissing) { + Iterator toSearch = null; + ResolvedMember found = null; + if ((aMember.getKind() == Member.METHOD) || (aMember.getKind() == Member.CONSTRUCTOR)) { + toSearch = getMethodsWithoutIterator(true, allowMissing).iterator(); + } else { + if (aMember.getKind() != Member.FIELD) + throw new IllegalStateException("I didn't know you would look for members of kind " + aMember.getKind()); + toSearch = getFields(); + } + while (toSearch.hasNext()) { + ResolvedMemberImpl candidate = (ResolvedMemberImpl) toSearch.next(); + if (candidate.matches(aMember)) { + found = candidate; + break; + } + } + + return found; + } + + public static boolean matches(Member m1, Member m2) { + if (m1 == null) + return m2 == null; + if (m2 == null) + return false; + + // Check the names + boolean equalNames = m1.getName().equals(m2.getName()); + if (!equalNames) + return false; + + // Check the signatures + boolean equalSignatures = m1.getSignature().equals(m2.getSignature()); + if (equalSignatures) + return true; + + // If they aren't the same, we need to allow for covariance ... where + // one sig might be ()LCar; and + // the subsig might be ()LFastCar; - where FastCar is a subclass of Car + boolean equalCovariantSignatures = m1.getParameterSignature().equals(m2.getParameterSignature()); + if (equalCovariantSignatures) + return true; + + return false; + } + + public static boolean conflictingSignature(Member m1, Member m2) { + if (m1 == null || m2 == null) + return false; + + if (!m1.getName().equals(m2.getName())) { + return false; + } + if (m1.getKind() != m2.getKind()) { + return false; + } + + if (m1.getKind() == Member.FIELD) { + return m1.getDeclaringType().equals(m2.getDeclaringType()); + } else if (m1.getKind() == Member.POINTCUT) { + return true; + } + + UnresolvedType[] p1 = m1.getGenericParameterTypes(); + UnresolvedType[] p2 = m2.getGenericParameterTypes(); + if (p1 == null) + p1 = m1.getParameterTypes(); + if (p2 == null) + p2 = m2.getParameterTypes(); + int n = p1.length; + if (n != p2.length) + return false; + + for (int i = 0; i < n; i++) { + if (!p1[i].equals(p2[i])) + return false; + } + return true; + } + + /** + * returns an iterator through all of the pointcuts of this type, in order for checking from JVM spec 2ed 5.4.3.2 (as for + * fields). This means that the order is + *

+ *

    + *
  • pointcuts from current class
  • + *
  • recur into direct superinterfaces
  • + *
  • recur into superclass
  • + *
+ *

+ * We keep a hashSet of interfaces that we've visited so we don't spiral out into 2^n land. + */ + public Iterator getPointcuts() { + final Iterators.Filter dupFilter = Iterators.dupFilter(); + // same order as fields + Iterators.Getter typeGetter = new Iterators.Getter() { + public Iterator get(Object o) { + return dupFilter.filter(((ResolvedType) o).getDirectSupertypes()); + } + }; + Iterators.Getter pointcutGetter = new Iterators.Getter() { + public Iterator get(Object o) { + // System.err.println("getting for " + o); + return Iterators.array(((ResolvedType) o).getDeclaredPointcuts()); + } + }; + return Iterators.mapOver(Iterators.recur(this, typeGetter), pointcutGetter); + } + + public ResolvedPointcutDefinition findPointcut(String name) { + // System.err.println("looking for pointcuts " + this); + for (Iterator i = getPointcuts(); i.hasNext();) { + ResolvedPointcutDefinition f = (ResolvedPointcutDefinition) i.next(); + // System.err.println(f); + if (name.equals(f.getName())) { + return f; + } + } + // pr120521 + if (!getOutermostType().equals(this)) { + ResolvedType outerType = getOutermostType().resolve(world); + ResolvedPointcutDefinition rpd = outerType.findPointcut(name); + return rpd; + } + return null; // should we throw an exception here? + } + + // all about collecting CrosscuttingMembers + + // ??? collecting data-structure, shouldn't really be a field + public CrosscuttingMembers crosscuttingMembers; + + public CrosscuttingMembers collectCrosscuttingMembers(boolean shouldConcretizeIfNeeded) { + crosscuttingMembers = new CrosscuttingMembers(this, shouldConcretizeIfNeeded); + crosscuttingMembers.setPerClause(getPerClause()); + crosscuttingMembers.addShadowMungers(collectShadowMungers()); + // GENERICITDFIX + // crosscuttingMembers.addTypeMungers(collectTypeMungers()); + crosscuttingMembers.addTypeMungers(getTypeMungers()); + // FIXME AV - skip but needed ?? or ?? + // crosscuttingMembers.addLateTypeMungers(getLateTypeMungers()); + crosscuttingMembers.addDeclares(collectDeclares(!this.doesNotExposeShadowMungers())); + crosscuttingMembers.addPrivilegedAccesses(getPrivilegedAccesses()); + + // System.err.println("collected cc members: " + this + ", " + + // collectDeclares()); + return crosscuttingMembers; + } + + public final Collection collectDeclares(boolean includeAdviceLike) { + if (!this.isAspect()) + return Collections.EMPTY_LIST; + + ArrayList ret = new ArrayList(); + // if (this.isAbstract()) { + // for (Iterator i = getDeclares().iterator(); i.hasNext();) { + // Declare dec = (Declare) i.next(); + // if (!dec.isAdviceLike()) ret.add(dec); + // } + // + // if (!includeAdviceLike) return ret; + + if (!this.isAbstract()) { + // ret.addAll(getDeclares()); + final Iterators.Filter dupFilter = Iterators.dupFilter(); + Iterators.Getter typeGetter = new Iterators.Getter() { + public Iterator get(Object o) { + return dupFilter.filter(((ResolvedType) o).getDirectSupertypes()); + } + }; + Iterator typeIterator = Iterators.recur(this, typeGetter); + + while (typeIterator.hasNext()) { + ResolvedType ty = (ResolvedType) typeIterator.next(); + // System.out.println("super: " + ty + ", " + ); + for (Iterator i = ty.getDeclares().iterator(); i.hasNext();) { + Declare dec = (Declare) i.next(); + if (dec.isAdviceLike()) { + if (includeAdviceLike) + ret.add(dec); + } else { + ret.add(dec); + } + } + } + } + + return ret; + } + + private final Collection collectShadowMungers() { + if (!this.isAspect() || this.isAbstract() || this.doesNotExposeShadowMungers()) + return Collections.EMPTY_LIST; + + ArrayList acc = new ArrayList(); + final Iterators.Filter dupFilter = Iterators.dupFilter(); + Iterators.Getter typeGetter = new Iterators.Getter() { + public Iterator get(Object o) { + return dupFilter.filter(((ResolvedType) o).getDirectSupertypes()); + } + }; + Iterator typeIterator = Iterators.recur(this, typeGetter); + + while (typeIterator.hasNext()) { + ResolvedType ty = (ResolvedType) typeIterator.next(); + acc.addAll(ty.getDeclaredShadowMungers()); + } + + return acc; + } + + protected boolean doesNotExposeShadowMungers() { + return false; + } + + public PerClause getPerClause() { + return null; + } + + public Collection getDeclares() { + return Collections.EMPTY_LIST; + } + + protected Collection getTypeMungers() { + return Collections.EMPTY_LIST; + } + + protected Collection getPrivilegedAccesses() { + return Collections.EMPTY_LIST; + } + + // ---- useful things + + public final boolean isInterface() { + return Modifier.isInterface(getModifiers()); + } + + public final boolean isAbstract() { + return Modifier.isAbstract(getModifiers()); + } + + public boolean isClass() { + return false; + } + + public boolean isAspect() { + return false; + } + + public boolean isAnnotationStyleAspect() { + return false; + } + + /** + * Note: Only overridden by Name subtype. + */ + public boolean isEnum() { + return false; + } + + /** + * Note: Only overridden by Name subtype. + */ + public boolean isAnnotation() { + return false; + } + + public boolean isAnonymous() { + return false; + } + + public boolean isNested() { + return false; + } + + public void addAnnotation(AnnotationAJ annotationX) { + throw new RuntimeException("ResolvedType.addAnnotation() should never be called"); + } + + public AnnotationAJ[] getAnnotations() { + throw new RuntimeException("ResolvedType.getAnnotations() should never be called"); + } + + /** + * Note: Only overridden by ReferenceType subtype + */ + public boolean canAnnotationTargetType() { + return false; + } + + /** + * Note: Only overridden by ReferenceType subtype + */ + public AnnotationTargetKind[] getAnnotationTargetKinds() { + return null; + } + + /** + * Note: Only overridden by Name subtype. + */ + public boolean isAnnotationWithRuntimeRetention() { + return false; + } + + public boolean isSynthetic() { + return signature.indexOf("$ajc") != -1; + } + + public final boolean isFinal() { + return Modifier.isFinal(getModifiers()); + } + + protected Map /* Type variable name -> UnresolvedType */getMemberParameterizationMap() { + if (!isParameterizedType()) + return Collections.EMPTY_MAP; + TypeVariable[] tvs = getGenericType().getTypeVariables(); + Map parameterizationMap = new HashMap(); + for (int i = 0; i < tvs.length; i++) { + parameterizationMap.put(tvs[i].getName(), typeParameters[i]); + } + return parameterizationMap; + } + + public Collection getDeclaredAdvice() { + List l = new ArrayList(); + ResolvedMember[] methods = getDeclaredMethods(); + if (isParameterizedType()) + methods = getGenericType().getDeclaredMethods(); + Map typeVariableMap = getAjMemberParameterizationMap(); + for (int i = 0, len = methods.length; i < len; i++) { + ShadowMunger munger = methods[i].getAssociatedShadowMunger(); + if (munger != null) { + if (ajMembersNeedParameterization()) { + // munger.setPointcut(munger.getPointcut().parameterizeWith( + // typeVariableMap)); + munger = munger.parameterizeWith(this, typeVariableMap); + if (munger instanceof Advice) { + Advice advice = (Advice) munger; + // update to use the parameterized signature... + UnresolvedType[] ptypes = methods[i].getGenericParameterTypes(); + UnresolvedType[] newPTypes = new UnresolvedType[ptypes.length]; + for (int j = 0; j < ptypes.length; j++) { + if (ptypes[j] instanceof TypeVariableReferenceType) { + TypeVariableReferenceType tvrt = (TypeVariableReferenceType) ptypes[j]; + if (typeVariableMap.containsKey(tvrt.getTypeVariable().getName())) { + newPTypes[j] = (UnresolvedType) typeVariableMap.get(tvrt.getTypeVariable().getName()); + } else { + newPTypes[j] = ptypes[j]; + } + } else { + newPTypes[j] = ptypes[j]; + } + } + advice.setBindingParameterTypes(newPTypes); + } + } + munger.setDeclaringType(this); + l.add(munger); + } + } + return l; + } + + public Collection getDeclaredShadowMungers() { + Collection c = getDeclaredAdvice(); + return c; + } + + // ---- only for testing! + + public ResolvedMember[] getDeclaredJavaFields() { + return filterInJavaVisible(getDeclaredFields()); + } + + public ResolvedMember[] getDeclaredJavaMethods() { + return filterInJavaVisible(getDeclaredMethods()); + } + + public ShadowMunger[] getDeclaredShadowMungersArray() { + List l = (List) getDeclaredShadowMungers(); + return (ShadowMunger[]) l.toArray(new ShadowMunger[l.size()]); + } + + private ResolvedMember[] filterInJavaVisible(ResolvedMember[] ms) { + List l = new ArrayList(); + for (int i = 0, len = ms.length; i < len; i++) { + if (!ms[i].isAjSynthetic() && ms[i].getAssociatedShadowMunger() == null) { + l.add(ms[i]); + } + } + return (ResolvedMember[]) l.toArray(new ResolvedMember[l.size()]); + } + + public abstract ISourceContext getSourceContext(); + + // ---- fields + + public static final ResolvedType[] NONE = new ResolvedType[0]; + + public static final Primitive BYTE = new Primitive("B", 1, 0); + public static final Primitive CHAR = new Primitive("C", 1, 1); + public static final Primitive DOUBLE = new Primitive("D", 2, 2); + public static final Primitive FLOAT = new Primitive("F", 1, 3); + public static final Primitive INT = new Primitive("I", 1, 4); + public static final Primitive LONG = new Primitive("J", 2, 5); + public static final Primitive SHORT = new Primitive("S", 1, 6); + public static final Primitive VOID = new Primitive("V", 0, 8); + public static final Primitive BOOLEAN = new Primitive("Z", 1, 7); + public static final Missing MISSING = new Missing(); + + /** Reset the static state in the primitive types */ + // OPTIMIZE I think we have a bug here because primitives are static and the + // world they use may vary (or may even be + // null) + public static void resetPrimitives() { + BYTE.world = null; + CHAR.world = null; + DOUBLE.world = null; + FLOAT.world = null; + INT.world = null; + LONG.world = null; + SHORT.world = null; + VOID.world = null; + BOOLEAN.world = null; + } + + // ---- types + public static ResolvedType makeArray(ResolvedType type, int dim) { + if (dim == 0) + return type; + ResolvedType array = new ArrayReferenceType("[" + type.getSignature(), "[" + type.getErasureSignature(), type.getWorld(), + type); + return makeArray(array, dim - 1); + } + + static class Primitive extends ResolvedType { + private final int size; + private final int index; + + Primitive(String signature, int size, int index) { + super(signature, null); + this.size = size; + this.index = index; + this.typeKind = TypeKind.PRIMITIVE; + } + + public final int getSize() { + return size; + } + + public final int getModifiers() { + return Modifier.PUBLIC | Modifier.FINAL; + } + + public final boolean isPrimitiveType() { + return true; + } + + public boolean hasAnnotation(UnresolvedType ofType) { + return false; + } + + public final boolean isAssignableFrom(ResolvedType other) { + if (!other.isPrimitiveType()) { + if (!world.isInJava5Mode()) + return false; + return validBoxing.contains(this.getSignature() + other.getSignature()); + } + return assignTable[((Primitive) other).index][index]; + } + + public final boolean isAssignableFrom(ResolvedType other, boolean allowMissing) { + return isAssignableFrom(other); + } + + public final boolean isCoerceableFrom(ResolvedType other) { + if (this == other) + return true; + if (!other.isPrimitiveType()) + return false; + if (index > 6 || ((Primitive) other).index > 6) + return false; + return true; + } + + public ResolvedType resolve(World world) { + this.world = world; + return super.resolve(world); + } + + public final boolean needsNoConversionFrom(ResolvedType other) { + if (!other.isPrimitiveType()) + return false; + return noConvertTable[((Primitive) other).index][index]; + } + + private static final boolean[][] assignTable = {// to: B C D F I J S V Z + // from + { true, true, true, true, true, true, true, false, false }, // B + { false, true, true, true, true, true, false, false, false }, // C + { false, false, true, false, false, false, false, false, false }, // D + { false, false, true, true, false, false, false, false, false }, // F + { false, false, true, true, true, true, false, false, false }, // I + { false, false, true, true, false, true, false, false, false }, // J + { false, false, true, true, true, true, true, false, false }, // S + { false, false, false, false, false, false, false, true, false }, // V + { false, false, false, false, false, false, false, false, true }, // Z + }; + private static final boolean[][] noConvertTable = {// to: B C D F I J S + // V Z from + { true, true, false, false, true, false, true, false, false }, // B + { false, true, false, false, true, false, false, false, false }, // C + { false, false, true, false, false, false, false, false, false }, // D + { false, false, false, true, false, false, false, false, false }, // F + { false, false, false, false, true, false, false, false, false }, // I + { false, false, false, false, false, true, false, false, false }, // J + { false, false, false, false, true, false, true, false, false }, // S + { false, false, false, false, false, false, false, true, false }, // V + { false, false, false, false, false, false, false, false, true }, // Z + }; + + // ---- + + public final ResolvedMember[] getDeclaredFields() { + return ResolvedMember.NONE; + } + + public final ResolvedMember[] getDeclaredMethods() { + return ResolvedMember.NONE; + } + + public final ResolvedType[] getDeclaredInterfaces() { + return ResolvedType.NONE; + } + + public final ResolvedMember[] getDeclaredPointcuts() { + return ResolvedMember.NONE; + } + + public final ResolvedType getSuperclass() { + return null; + } + + public ISourceContext getSourceContext() { + return null; + } + + } + + static class Missing extends ResolvedType { + Missing() { + super(MISSING_NAME, null); + } + + // public final String toString() { + // return ""; + // } + public final String getName() { + return MISSING_NAME; + } + + public final boolean isMissing() { + return true; + } + + public boolean hasAnnotation(UnresolvedType ofType) { + return false; + } + + public final ResolvedMember[] getDeclaredFields() { + return ResolvedMember.NONE; + } + + public final ResolvedMember[] getDeclaredMethods() { + return ResolvedMember.NONE; + } + + public final ResolvedType[] getDeclaredInterfaces() { + return ResolvedType.NONE; + } + + public final ResolvedMember[] getDeclaredPointcuts() { + return ResolvedMember.NONE; + } + + public final ResolvedType getSuperclass() { + return null; + } + + public final int getModifiers() { + return 0; + } + + public final boolean isAssignableFrom(ResolvedType other) { + return false; + } + + public final boolean isAssignableFrom(ResolvedType other, boolean allowMissing) { + return false; + } + + public final boolean isCoerceableFrom(ResolvedType other) { + return false; + } + + public boolean needsNoConversionFrom(ResolvedType other) { + return false; + } + + public ISourceContext getSourceContext() { + return null; + } + + } + + /** + * Look up a member, takes into account any ITDs on this type. return null if not found + */ + public ResolvedMember lookupMemberNoSupers(Member member) { + ResolvedMember ret = lookupDirectlyDeclaredMemberNoSupers(member); + if (ret == null && interTypeMungers != null) { + for (Iterator i = interTypeMungers.iterator(); i.hasNext();) { + ConcreteTypeMunger tm = (ConcreteTypeMunger) i.next(); + if (matches(tm.getSignature(), member)) { + return tm.getSignature(); + } + } + } + return ret; + } + + public ResolvedMember lookupMemberWithSupersAndITDs(Member member) { + ResolvedMember ret = lookupMemberNoSupers(member); + if (ret != null) + return ret; + + ResolvedType supert = getSuperclass(); + while (ret == null && supert != null) { + ret = supert.lookupMemberNoSupers(member); + if (ret == null) + supert = supert.getSuperclass(); + } + + return ret; + } + + /** + * as lookupMemberNoSupers, but does not include ITDs + * + * @param member + * @return + */ + public ResolvedMember lookupDirectlyDeclaredMemberNoSupers(Member member) { + ResolvedMember ret; + if (member.getKind() == Member.FIELD) { + ret = lookupMember(member, getDeclaredFields()); + } else { + // assert member.getKind() == Member.METHOD || member.getKind() == + // Member.CONSTRUCTOR + ret = lookupMember(member, getDeclaredMethods()); + } + return ret; + } + + /** + * This lookup has specialized behaviour - a null result tells the EclipseTypeMunger that it should make a default + * implementation of a method on this type. + * + * @param member + * @return + */ + public ResolvedMember lookupMemberIncludingITDsOnInterfaces(Member member) { + return lookupMemberIncludingITDsOnInterfaces(member, this); + } + + private ResolvedMember lookupMemberIncludingITDsOnInterfaces(Member member, ResolvedType onType) { + ResolvedMember ret = onType.lookupMemberNoSupers(member); + if (ret != null) { + return ret; + } else { + ResolvedType superType = onType.getSuperclass(); + if (superType != null) { + ret = lookupMemberIncludingITDsOnInterfaces(member, superType); + } + if (ret == null) { + // try interfaces then, but only ITDs now... + ResolvedType[] superInterfaces = onType.getDeclaredInterfaces(); + for (int i = 0; i < superInterfaces.length; i++) { + ret = superInterfaces[i].lookupMethodInITDs(member); + if (ret != null) + return ret; + } + } + } + return ret; + } + + protected List interTypeMungers = new ArrayList(0); + + public List getInterTypeMungers() { + return interTypeMungers; + } + + public List getInterTypeParentMungers() { + List l = new ArrayList(); + for (Iterator iter = interTypeMungers.iterator(); iter.hasNext();) { + ConcreteTypeMunger element = (ConcreteTypeMunger) iter.next(); + if (element.getMunger() instanceof NewParentTypeMunger) + l.add(element); + } + return l; + } + + /** + * ??? This method is O(N*M) where N = number of methods and M is number of inter-type declarations in my super + */ + public List getInterTypeMungersIncludingSupers() { + ArrayList ret = new ArrayList(); + collectInterTypeMungers(ret); + return ret; + } + + public List getInterTypeParentMungersIncludingSupers() { + ArrayList ret = new ArrayList(); + collectInterTypeParentMungers(ret); + return ret; + } + + private void collectInterTypeParentMungers(List collector) { + for (Iterator iter = getDirectSupertypes(); iter.hasNext();) { + ResolvedType superType = (ResolvedType) iter.next(); + superType.collectInterTypeParentMungers(collector); + } + collector.addAll(getInterTypeParentMungers()); + } + + protected void collectInterTypeMungers(List collector) { + for (Iterator iter = getDirectSupertypes(); iter.hasNext();) { + ResolvedType superType = (ResolvedType) iter.next(); + if (superType == null) { + throw new BCException("UnexpectedProblem: a supertype in the hierarchy for " + this.getName() + " is null"); + } + superType.collectInterTypeMungers(collector); + } + + outer: for (Iterator iter1 = collector.iterator(); iter1.hasNext();) { + ConcreteTypeMunger superMunger = (ConcreteTypeMunger) iter1.next(); + if (superMunger.getSignature() == null) + continue; + + if (!superMunger.getSignature().isAbstract()) + continue; + + for (Iterator iter = getInterTypeMungers().iterator(); iter.hasNext();) { + ConcreteTypeMunger myMunger = (ConcreteTypeMunger) iter.next(); + if (conflictingSignature(myMunger.getSignature(), superMunger.getSignature())) { + iter1.remove(); + continue outer; + } + } + + if (!superMunger.getSignature().isPublic()) + continue; + + for (Iterator iter = getMethods(); iter.hasNext();) { + ResolvedMember method = (ResolvedMember) iter.next(); + if (conflictingSignature(method, superMunger.getSignature())) { + iter1.remove(); + continue outer; + } + } + } + + collector.addAll(getInterTypeMungers()); + } + + /** + * Check: 1) That we don't have any abstract type mungers unless this type is abstract. 2) That an abstract ITDM on an interface + * is declared public. (Compiler limitation) (PR70794) + */ + public void checkInterTypeMungers() { + if (isAbstract()) + return; + + boolean itdProblem = false; + + for (Iterator iter = getInterTypeMungersIncludingSupers().iterator(); iter.hasNext();) { + ConcreteTypeMunger munger = (ConcreteTypeMunger) iter.next(); + itdProblem = checkAbstractDeclaration(munger) || itdProblem; // Rule + // 2 + + } + + if (itdProblem) + return; // If the rules above are broken, return right now + + for (Iterator iter = getInterTypeMungersIncludingSupers().iterator(); iter.hasNext();) { + ConcreteTypeMunger munger = (ConcreteTypeMunger) iter.next(); + if (munger.getSignature() != null && munger.getSignature().isAbstract()) { // Rule 1 + if (munger.getMunger().getKind() == ResolvedTypeMunger.MethodDelegate) { + // ignore for @AJ ITD as munger.getSignature() is the + // interface method hence abstract + } else { + world.getMessageHandler() + .handleMessage( + new Message("must implement abstract inter-type declaration: " + munger.getSignature(), "", + IMessage.ERROR, getSourceLocation(), null, + new ISourceLocation[] { getMungerLocation(munger) })); + } + } + } + } + + /** + * See PR70794. This method checks that if an abstract inter-type method declaration is made on an interface then it must also + * be public. This is a compiler limitation that could be made to work in the future (if someone provides a worthwhile usecase) + * + * @return indicates if the munger failed the check + */ + private boolean checkAbstractDeclaration(ConcreteTypeMunger munger) { + if (munger.getMunger() != null && (munger.getMunger() instanceof NewMethodTypeMunger)) { + ResolvedMember itdMember = munger.getSignature(); + ResolvedType onType = itdMember.getDeclaringType().resolve(world); + if (onType.isInterface() && itdMember.isAbstract() && !itdMember.isPublic()) { + world.getMessageHandler().handleMessage( + new Message(WeaverMessages.format(WeaverMessages.ITD_ABSTRACT_MUST_BE_PUBLIC_ON_INTERFACE, munger + .getSignature(), onType), "", Message.ERROR, getSourceLocation(), null, + new ISourceLocation[] { getMungerLocation(munger) })); + return true; + } + } + return false; + } + + /** + * Get a source location for the munger. Until intertype mungers remember where they came from, the source location for the + * munger itself is null. In these cases use the source location for the aspect containing the ITD. + */ + private ISourceLocation getMungerLocation(ConcreteTypeMunger munger) { + ISourceLocation sloc = munger.getSourceLocation(); + if (sloc == null) { + sloc = munger.getAspectType().getSourceLocation(); + } + return sloc; + } + + /** + * Returns a ResolvedType object representing the declaring type of this type, or null if this type does not represent a + * non-package-level-type. + *

+ * Warning: This is guaranteed to work for all member types. For anonymous/local types, the only guarantee is + * given in JLS 13.1, where it guarantees that if you call getDeclaringType() repeatedly, you will eventually get the top-level + * class, but it does not say anything about classes in between. + * + * @return the declaring UnresolvedType object, or null. + */ + public ResolvedType getDeclaringType() { + if (isArray()) + return null; + String name = getName(); + int lastDollar = name.lastIndexOf('$'); + while (lastDollar > 0) { // allow for classes starting '$' (pr120474) + ResolvedType ret = world.resolve(UnresolvedType.forName(name.substring(0, lastDollar)), true); + if (!ResolvedType.isMissing(ret)) + return ret; + lastDollar = name.lastIndexOf('$', lastDollar - 1); + } + return null; + } + + public static boolean isVisible(int modifiers, ResolvedType targetType, ResolvedType fromType) { + // System.err.println("mod: " + modifiers + ", " + targetType + " and " + // + fromType); + + if (Modifier.isPublic(modifiers)) { + return true; + } else if (Modifier.isPrivate(modifiers)) { + return targetType.getOutermostType().equals(fromType.getOutermostType()); + } else if (Modifier.isProtected(modifiers)) { + return samePackage(targetType, fromType) || targetType.isAssignableFrom(fromType); + } else { // package-visible + return samePackage(targetType, fromType); + } + } + + private static boolean samePackage(ResolvedType targetType, ResolvedType fromType) { + String p1 = targetType.getPackageName(); + String p2 = fromType.getPackageName(); + if (p1 == null) + return p2 == null; + if (p2 == null) + return false; + return p1.equals(p2); + } + + /** + * Checks if the generic type for 'this' and the generic type for 'other' are the same - it can be passed raw or parameterized + * versions and will just compare the underlying generic type. + */ + private boolean genericTypeEquals(ResolvedType other) { + ResolvedType rt = other; + if (rt.isParameterizedType() || rt.isRawType()) + rt.getGenericType(); + if (((isParameterizedType() || isRawType()) && getGenericType().equals(rt)) || (this.equals(other))) + return true; + return false; + } + + /** + * Look up the actual occurence of a particular type in the hierarchy for 'this' type. The input is going to be a generic type, + * and the caller wants to know if it was used in its RAW or a PARAMETERIZED form in this hierarchy. + * + * returns null if it can't be found. + */ + public ResolvedType discoverActualOccurrenceOfTypeInHierarchy(ResolvedType lookingFor) { + if (!lookingFor.isGenericType()) + throw new BCException("assertion failed: method should only be called with generic type, but " + lookingFor + " is " + + lookingFor.typeKind); + + if (this.equals(ResolvedType.OBJECT)) + return null; + + if (genericTypeEquals(lookingFor)) + return this; + + ResolvedType superT = getSuperclass(); + if (superT.genericTypeEquals(lookingFor)) + return superT; + + ResolvedType[] superIs = getDeclaredInterfaces(); + for (int i = 0; i < superIs.length; i++) { + ResolvedType superI = superIs[i]; + if (superI.genericTypeEquals(lookingFor)) + return superI; + ResolvedType checkTheSuperI = superI.discoverActualOccurrenceOfTypeInHierarchy(lookingFor); + if (checkTheSuperI != null) + return checkTheSuperI; + } + return superT.discoverActualOccurrenceOfTypeInHierarchy(lookingFor); + } + + /** + * Called for all type mungers but only does something if they share type variables with a generic type which they target. When + * this happens this routine will check for the target type in the target hierarchy and 'bind' any type parameters as + * appropriate. For example, for the ITD "List I.x" against a type like this: "class A implements I" this routine + * will return a parameterized form of the ITD "List I.x" + */ + public ConcreteTypeMunger fillInAnyTypeParameters(ConcreteTypeMunger munger) { + boolean debug = false; + ResolvedMember member = munger.getSignature(); + if (munger.isTargetTypeParameterized()) { + if (debug) + System.err.println("Processing attempted parameterization of " + munger + " targetting type " + this); + if (debug) + System.err.println(" This type is " + this + " (" + typeKind + ")"); + // need to tailor this munger instance for the particular target... + if (debug) + System.err.println(" Signature that needs parameterizing: " + member); + // Retrieve the generic type + ResolvedType onType = world.resolve(member.getDeclaringType()).getGenericType(); + member.resolve(world); // Ensure all parts of the member are + // resolved + if (debug) + System.err.println(" Actual target ontype: " + onType + " (" + onType.typeKind + ")"); + // quickly find the targettype in the type hierarchy for this type + // (it will be either RAW or PARAMETERIZED) + ResolvedType actualTarget = discoverActualOccurrenceOfTypeInHierarchy(onType); + if (actualTarget == null) + throw new BCException("assertion failed: asked " + this + " for occurrence of " + onType + " in its hierarchy??"); + + // only bind the tvars if its a parameterized type or the raw type + // (in which case they collapse to bounds) - don't do it + // for generic types ;) + if (!actualTarget.isGenericType()) { + if (debug) + System.err.println("Occurrence in " + this + " is actually " + actualTarget + " (" + actualTarget.typeKind + + ")"); + // parameterize the signature + // ResolvedMember newOne = + // member.parameterizedWith(actualTarget.getTypeParameters(), + // onType,actualTarget.isParameterizedType()); + } + // if (!actualTarget.isRawType()) + munger = munger.parameterizedFor(actualTarget); + if (debug) + System.err.println("New sig: " + munger.getSignature()); + + if (debug) + System.err.println("====================================="); + } + return munger; + } + + public void addInterTypeMunger(ConcreteTypeMunger munger) { + ResolvedMember sig = munger.getSignature(); + if (sig == null || munger.getMunger() == null || munger.getMunger().getKind() == ResolvedTypeMunger.PrivilegedAccess) { + interTypeMungers.add(munger); + return; + } + + // ConcreteTypeMunger originalMunger = munger; + // we will use the 'parameterized' ITD for all the comparisons but we + // say the original + // one passed in actually matched as it will be added to the intertype + // member finder + // for the target type. It is possible we only want to do this if a + // generic type + // is discovered and the tvar is collapsed to a bound? + munger = fillInAnyTypeParameters(munger); + sig = munger.getSignature(); // possibly changed when type parms filled + // in + + // System.err.println("add: " + munger + " to " + this.getClassName() + + // " with " + interTypeMungers); + if (sig.getKind() == Member.METHOD) { + if (!compareToExistingMembers(munger, getMethodsWithoutIterator(false, true) /* getMethods() */)) + return; + if (this.isInterface()) { + if (!compareToExistingMembers(munger, Arrays.asList(world.getCoreType(OBJECT).getDeclaredMethods()).iterator())) + return; + } + } else if (sig.getKind() == Member.FIELD) { + if (!compareToExistingMembers(munger, Arrays.asList(getDeclaredFields()).iterator())) + return; + } else { + if (!compareToExistingMembers(munger, Arrays.asList(getDeclaredMethods()).iterator())) + return; + } + + // now compare to existingMungers + for (Iterator i = interTypeMungers.iterator(); i.hasNext();) { + ConcreteTypeMunger existingMunger = (ConcreteTypeMunger) i.next(); + if (conflictingSignature(existingMunger.getSignature(), munger.getSignature())) { + // System.err.println("match " + munger + " with " + + // existingMunger); + if (isVisible(munger.getSignature().getModifiers(), munger.getAspectType(), existingMunger.getAspectType())) { + // System.err.println(" is visible"); + int c = compareMemberPrecedence(sig, existingMunger.getSignature()); + if (c == 0) { + c = getWorld().compareByPrecedenceAndHierarchy(munger.getAspectType(), existingMunger.getAspectType()); + } + // System.err.println(" compare: " + c); + if (c < 0) { + // the existing munger dominates the new munger + checkLegalOverride(munger.getSignature(), existingMunger.getSignature()); + return; + } else if (c > 0) { + // the new munger dominates the existing one + checkLegalOverride(existingMunger.getSignature(), munger.getSignature()); + i.remove(); + break; + } else { + interTypeConflictError(munger, existingMunger); + interTypeConflictError(existingMunger, munger); + return; + } + } + } + } + // System.err.println("adding: " + munger + " to " + this); + // we are adding the parameterized form of the ITD to the list of + // mungers. Within it, the munger knows the original declared + // signature for the ITD so it can be retrieved. + interTypeMungers.add(munger); + } + + private boolean compareToExistingMembers(ConcreteTypeMunger munger, List existingMembersList) { + return compareToExistingMembers(munger, existingMembersList.iterator()); + } + + // ??? returning too soon + private boolean compareToExistingMembers(ConcreteTypeMunger munger, Iterator existingMembers) { + ResolvedMember sig = munger.getSignature(); + + // ResolvedType declaringAspectType = munger.getAspectType(); + // if (declaringAspectType.isRawType()) declaringAspectType = + // declaringAspectType.getGenericType(); + // if (declaringAspectType.isGenericType()) { + // + // ResolvedType genericOnType = + // getWorld().resolve(sig.getDeclaringType()).getGenericType(); + // ConcreteTypeMunger ctm = + // munger.parameterizedFor(discoverActualOccurrenceOfTypeInHierarchy + // (genericOnType)); + // sig = ctm.getSignature(); // possible sig change when type + // } + // if (munger.getMunger().hasTypeVariableAliases()) { + // ResolvedType genericOnType = + // getWorld().resolve(sig.getDeclaringType()).getGenericType(); + // ConcreteTypeMunger ctm = + // munger.parameterizedFor(discoverActualOccurrenceOfTypeInHierarchy( + // genericOnType)); + // sig = ctm.getSignature(); // possible sig change when type parameters + // filled in + // } + while (existingMembers.hasNext()) { + + ResolvedMember existingMember = (ResolvedMember) existingMembers.next(); + // don't worry about clashing with bridge methods + if (existingMember.isBridgeMethod()) + continue; + // System.err.println("Comparing munger: "+sig+" with member "+ + // existingMember); + if (conflictingSignature(existingMember, munger.getSignature())) { + // System.err.println("conflict: existingMember=" + + // existingMember + " typeMunger=" + munger); + // System.err.println(munger.getSourceLocation() + ", " + + // munger.getSignature() + ", " + + // munger.getSignature().getSourceLocation()); + + if (isVisible(existingMember.getModifiers(), this, munger.getAspectType())) { + int c = compareMemberPrecedence(sig, existingMember); + // System.err.println(" c: " + c); + if (c < 0) { + // existingMember dominates munger + checkLegalOverride(munger.getSignature(), existingMember); + return false; + } else if (c > 0) { + // munger dominates existingMember + checkLegalOverride(existingMember, munger.getSignature()); + // interTypeMungers.add(munger); + // ??? might need list of these overridden abstracts + continue; + } else { + // bridge methods can differ solely in return type. + // FIXME this whole method seems very hokey - unaware of + // covariance/varargs/bridging - it + // could do with a rewrite ! + boolean sameReturnTypes = (existingMember.getReturnType().equals(sig.getReturnType())); + if (sameReturnTypes) { + // pr206732 - if the existingMember is due to a + // previous application of this same ITD (which can + // happen if this is a binary type being brought in + // from the aspectpath). The 'better' fix is + // to recognize it is from the aspectpath at a + // higher level and dont do this, but that is rather + // more work. + boolean isDuplicateOfPreviousITD = false; + ResolvedType declaringRt = existingMember.getDeclaringType().resolve(world); + WeaverStateInfo wsi = declaringRt.getWeaverState(); + if (wsi != null) { + List mungersAffectingThisType = wsi.getTypeMungers(declaringRt); + if (mungersAffectingThisType != null) { + for (Iterator iterator = mungersAffectingThisType.iterator(); iterator.hasNext() + && !isDuplicateOfPreviousITD;) { + ConcreteTypeMunger ctMunger = (ConcreteTypeMunger) iterator.next(); + // relatively crude check - is the ITD + // for the same as the existingmember + // and does it come + // from the same aspect + if (ctMunger.getSignature().equals(existingMember) + && ctMunger.aspectType.equals(munger.getAspectType())) { + isDuplicateOfPreviousITD = true; + } + } + } + } + if (!isDuplicateOfPreviousITD) { + getWorld().getMessageHandler().handleMessage( + MessageUtil.error(WeaverMessages.format(WeaverMessages.ITD_MEMBER_CONFLICT, munger + .getAspectType().getName(), existingMember), munger.getSourceLocation())); + } + } + } + } else if (isDuplicateMemberWithinTargetType(existingMember, this, sig)) { + getWorld().getMessageHandler().handleMessage( + MessageUtil.error(WeaverMessages.format(WeaverMessages.ITD_MEMBER_CONFLICT, munger.getAspectType() + .getName(), existingMember), munger.getSourceLocation())); + + } + // return; + } + } + return true; + } + + // we know that the member signature matches, but that the member in the + // target type is not visible to the aspect. + // this may still be disallowed if it would result in two members within the + // same declaring type with the same + // signature AND more than one of them is concrete AND they are both visible + // within the target type. + private boolean isDuplicateMemberWithinTargetType(ResolvedMember existingMember, ResolvedType targetType, + ResolvedMember itdMember) { + if ((existingMember.isAbstract() || itdMember.isAbstract())) + return false; + UnresolvedType declaringType = existingMember.getDeclaringType(); + if (!targetType.equals(declaringType)) + return false; + // now have to test that itdMember is visible from targetType + if (itdMember.isPrivate()) + return false; + if (itdMember.isPublic()) + return true; + // must be in same package to be visible then... + if (!targetType.getPackageName().equals(itdMember.getDeclaringType().getPackageName())) + return false; + + // trying to put two members with the same signature into the exact same + // type..., and both visible in that type. + return true; + } + + /** + * @return true if the override is legal note: calling showMessage with two locations issues TWO messages, not ONE message with + * an additional source location. + */ + public boolean checkLegalOverride(ResolvedMember parent, ResolvedMember child) { + // System.err.println("check: " + child.getDeclaringType() + + // " overrides " + parent.getDeclaringType()); + if (Modifier.isFinal(parent.getModifiers())) { + world.showMessage(Message.ERROR, WeaverMessages.format(WeaverMessages.CANT_OVERRIDE_FINAL_MEMBER, parent), child + .getSourceLocation(), null); + return false; + } + + boolean incompatibleReturnTypes = false; + // In 1.5 mode, allow for covariance on return type + if (world.isInJava5Mode() && parent.getKind() == Member.METHOD) { + + // Look at the generic types when doing this comparison + ResolvedType rtParentReturnType = parent.resolve(world).getGenericReturnType().resolve(world); + ResolvedType rtChildReturnType = child.resolve(world).getGenericReturnType().resolve(world); + incompatibleReturnTypes = !rtParentReturnType.isAssignableFrom(rtChildReturnType); + // For debug, uncomment this bit and we'll repeat the check - stick + // a breakpoint on the call + // if (incompatibleReturnTypes) { + // incompatibleReturnTypes = + // !rtParentReturnType.isAssignableFrom(rtChildReturnType); + // } + } else { + incompatibleReturnTypes = !parent.getReturnType().equals(child.getReturnType()); + } + + if (incompatibleReturnTypes) { + world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.ITD_RETURN_TYPE_MISMATCH, parent, child), child + .getSourceLocation(), parent.getSourceLocation()); + return false; + } + if (parent.getKind() == Member.POINTCUT) { + UnresolvedType[] pTypes = parent.getParameterTypes(); + UnresolvedType[] cTypes = child.getParameterTypes(); + if (!Arrays.equals(pTypes, cTypes)) { + world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.ITD_PARAM_TYPE_MISMATCH, parent, child), + child.getSourceLocation(), parent.getSourceLocation()); + return false; + } + } + // System.err.println("check: " + child.getModifiers() + + // " more visible " + parent.getModifiers()); + if (isMoreVisible(parent.getModifiers(), child.getModifiers())) { + world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.ITD_VISIBILITY_REDUCTION, parent, child), child + .getSourceLocation(), parent.getSourceLocation()); + return false; + } + + // check declared exceptions + ResolvedType[] childExceptions = world.resolve(child.getExceptions()); + ResolvedType[] parentExceptions = world.resolve(parent.getExceptions()); + ResolvedType runtimeException = world.resolve("java.lang.RuntimeException"); + ResolvedType error = world.resolve("java.lang.Error"); + + outer: for (int i = 0, leni = childExceptions.length; i < leni; i++) { + // System.err.println("checking: " + childExceptions[i]); + if (runtimeException.isAssignableFrom(childExceptions[i])) + continue; + if (error.isAssignableFrom(childExceptions[i])) + continue; + + for (int j = 0, lenj = parentExceptions.length; j < lenj; j++) { + if (parentExceptions[j].isAssignableFrom(childExceptions[i])) + continue outer; + } + + // this message is now better handled my MethodVerifier in JDT core. + // world.showMessage(IMessage.ERROR, + // WeaverMessages.format(WeaverMessages.ITD_DOESNT_THROW, + // childExceptions[i].getName()), + // child.getSourceLocation(), null); + + return false; + } + if (parent.isStatic() && !child.isStatic()) { + world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.ITD_OVERRIDDEN_STATIC, child, parent), child + .getSourceLocation(), null); + return false; + } else if (child.isStatic() && !parent.isStatic()) { + world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.ITD_OVERIDDING_STATIC, child, parent), child + .getSourceLocation(), null); + return false; + } + return true; + + } + + private int compareMemberPrecedence(ResolvedMember m1, ResolvedMember m2) { + // if (!m1.getReturnType().equals(m2.getReturnType())) return 0; + + // need to allow for the special case of 'clone' - which is like + // abstract but is + // not marked abstract. The code below this next line seems to make + // assumptions + // about what will have gotten through the compiler based on the normal + // java rules. clone goes against these... + if (m2.isProtected() && m2.getName().charAt(0) == 'c') { + UnresolvedType declaring = m2.getDeclaringType(); + if (declaring != null) { + if (declaring.getName().equals("java.lang.Object") && m2.getName().equals("clone")) + return +1; + } + } + + if (Modifier.isAbstract(m1.getModifiers())) + return -1; + if (Modifier.isAbstract(m2.getModifiers())) + return +1; + + if (m1.getDeclaringType().equals(m2.getDeclaringType())) + return 0; + + ResolvedType t1 = m1.getDeclaringType().resolve(world); + ResolvedType t2 = m2.getDeclaringType().resolve(world); + if (t1.isAssignableFrom(t2)) { + return -1; + } + if (t2.isAssignableFrom(t1)) { + return +1; + } + return 0; + } + + public static boolean isMoreVisible(int m1, int m2) { + if (Modifier.isPrivate(m1)) + return false; + if (isPackage(m1)) + return Modifier.isPrivate(m2); + if (Modifier.isProtected(m1)) + return /* private package */(Modifier.isPrivate(m2) || isPackage(m2)); + if (Modifier.isPublic(m1)) + return /* private package protected */!Modifier.isPublic(m2); + throw new RuntimeException("bad modifier: " + m1); + } + + private static boolean isPackage(int i) { + return (0 == (i & (Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED))); + } + + private void interTypeConflictError(ConcreteTypeMunger m1, ConcreteTypeMunger m2) { + // XXX this works only if we ignore separate compilation issues + // XXX dual errors possible if (this instanceof BcelObjectType) return; + + // System.err.println("conflict at " + m2.getSourceLocation()); + getWorld().showMessage( + IMessage.ERROR, + WeaverMessages.format(WeaverMessages.ITD_CONFLICT, m1.getAspectType().getName(), m2.getSignature(), m2 + .getAspectType().getName()), m2.getSourceLocation(), getSourceLocation()); + } + + public ResolvedMember lookupSyntheticMember(Member member) { + // ??? horribly inefficient + // for (Iterator i = + // System.err.println("lookup " + member + " in " + interTypeMungers); + for (Iterator i = interTypeMungers.iterator(); i.hasNext();) { + ConcreteTypeMunger m = (ConcreteTypeMunger) i.next(); + ResolvedMember ret = m.getMatchingSyntheticMember(member); + if (ret != null) { + // System.err.println(" found: " + ret); + return ret; + } + } + + // Handling members for the new array join point + if (world.isJoinpointArrayConstructionEnabled() && this.isArray()) { + if (member.getKind() == Member.CONSTRUCTOR) { + ResolvedMemberImpl ret = new ResolvedMemberImpl(Member.CONSTRUCTOR, this, Modifier.PUBLIC, ResolvedType.VOID, + "", world.resolve(member.getParameterTypes())); + return ret; + } + } + + // if (this.getSuperclass() != ResolvedType.OBJECT && + // this.getSuperclass() != null) { + // return getSuperclass().lookupSyntheticMember(member); + // } + + return null; + } + + public void clearInterTypeMungers() { + if (isRawType()) + getGenericType().clearInterTypeMungers(); + interTypeMungers = new ArrayList(); + } + + public boolean isTopmostImplementor(ResolvedType interfaceType) { + if (isInterface()) + return false; + if (!interfaceType.isAssignableFrom(this, true)) + return false; + // check that I'm truly the topmost implementor + if (this.getSuperclass().isMissing()) + return true; // we don't know anything about supertype, and it can't + // be exposed to weaver + if (interfaceType.isAssignableFrom(this.getSuperclass(), true)) { + return false; + } + return true; + } + + public ResolvedType getTopmostImplementor(ResolvedType interfaceType) { + if (isInterface()) + return null; + if (!interfaceType.isAssignableFrom(this)) + return null; + // Check if my super class is an implementor? + ResolvedType higherType = this.getSuperclass().getTopmostImplementor(interfaceType); + if (higherType != null) + return higherType; + return this; + } + + public List getExposedPointcuts() { + List ret = new ArrayList(); + if (getSuperclass() != null) + ret.addAll(getSuperclass().getExposedPointcuts()); + + for (Iterator i = Arrays.asList(getDeclaredInterfaces()).iterator(); i.hasNext();) { + ResolvedType t = (ResolvedType) i.next(); + addPointcutsResolvingConflicts(ret, Arrays.asList(t.getDeclaredPointcuts()), false); + } + addPointcutsResolvingConflicts(ret, Arrays.asList(getDeclaredPointcuts()), true); + for (Iterator i = ret.iterator(); i.hasNext();) { + ResolvedPointcutDefinition inherited = (ResolvedPointcutDefinition) i.next(); + // System.err.println("looking at: " + inherited + " in " + this); + // System.err.println(" " + inherited.isAbstract() + + // " in " + this.isAbstract()); + if (inherited.isAbstract()) { + if (!this.isAbstract()) { + getWorld().showMessage(IMessage.ERROR, + WeaverMessages.format(WeaverMessages.POINCUT_NOT_CONCRETE, inherited, this.getName()), + inherited.getSourceLocation(), this.getSourceLocation()); + } + } + } + + return ret; + } + + private void addPointcutsResolvingConflicts(List acc, List added, boolean isOverriding) { + for (Iterator i = added.iterator(); i.hasNext();) { + ResolvedPointcutDefinition toAdd = (ResolvedPointcutDefinition) i.next(); + // System.err.println("adding: " + toAdd); + for (Iterator j = acc.iterator(); j.hasNext();) { + ResolvedPointcutDefinition existing = (ResolvedPointcutDefinition) j.next(); + if (existing == toAdd) + continue; + if (!isVisible(existing.getModifiers(), existing.getDeclaringType().resolve(getWorld()), this)) { + // if they intended to override it but it is not visible, + // give them a nicer message + if (existing.isAbstract() && conflictingSignature(existing, toAdd)) { + getWorld().showMessage( + IMessage.ERROR, + WeaverMessages.format(WeaverMessages.POINTCUT_NOT_VISIBLE, existing.getDeclaringType().getName() + + "." + existing.getName() + "()", this.getName()), toAdd.getSourceLocation(), null); + j.remove(); + } + continue; + } + if (conflictingSignature(existing, toAdd)) { + if (isOverriding) { + checkLegalOverride(existing, toAdd); + j.remove(); + } else { + getWorld().showMessage( + IMessage.ERROR, + WeaverMessages.format(WeaverMessages.CONFLICTING_INHERITED_POINTCUTS, this.getName() + + toAdd.getSignature()), existing.getSourceLocation(), toAdd.getSourceLocation()); + j.remove(); + } + } + } + acc.add(toAdd); + } + } + + public ISourceLocation getSourceLocation() { + return null; + } + + public boolean isExposedToWeaver() { + return false; + } + + public WeaverStateInfo getWeaverState() { + return null; + } + + /** + * Overridden by ReferenceType to return a sensible answer for parameterized and raw types. + * + * @return + */ + public ResolvedType getGenericType() { + if (!(isParameterizedType() || isRawType())) + throw new BCException("The type " + getBaseName() + " is not parameterized or raw - it has no generic type"); + return null; + } + + public ResolvedType parameterizedWith(UnresolvedType[] typeParameters) { + if (!(isGenericType() || isParameterizedType())) + return this; + return TypeFactory.createParameterizedType(this.getGenericType(), typeParameters, getWorld()); + } + + /** + * Iff I am a parameterized type, and any of my parameters are type variable references, return a version with those type + * parameters replaced in accordance with the passed bindings. + */ + public UnresolvedType parameterize(Map typeBindings) { + if (!isParameterizedType()) + return this;// throw new IllegalStateException( + // "Can't parameterize a type that is not a parameterized type" + // ); + boolean workToDo = false; + for (int i = 0; i < typeParameters.length; i++) { + if (typeParameters[i].isTypeVariableReference() || (typeParameters[i] instanceof BoundedReferenceType)) { + workToDo = true; + } + } + if (!workToDo) { + return this; + } else { + UnresolvedType[] newTypeParams = new UnresolvedType[typeParameters.length]; + for (int i = 0; i < newTypeParams.length; i++) { + newTypeParams[i] = typeParameters[i]; + if (newTypeParams[i].isTypeVariableReference()) { + TypeVariableReferenceType tvrt = (TypeVariableReferenceType) newTypeParams[i]; + UnresolvedType binding = (UnresolvedType) typeBindings.get(tvrt.getTypeVariable().getName()); + if (binding != null) + newTypeParams[i] = binding; + } else if (newTypeParams[i] instanceof BoundedReferenceType) { + BoundedReferenceType brType = (BoundedReferenceType) newTypeParams[i]; + newTypeParams[i] = brType.parameterize(typeBindings); + // brType.parameterize(typeBindings) + } + } + return TypeFactory.createParameterizedType(getGenericType(), newTypeParams, getWorld()); + } + } + + // public boolean hasParameterizedSuperType() { + // getParameterizedSuperTypes(); + // return parameterizedSuperTypes.length > 0; + // } + + // public boolean hasGenericSuperType() { + // ResolvedType[] superTypes = getDeclaredInterfaces(); + // for (int i = 0; i < superTypes.length; i++) { + // if (superTypes[i].isGenericType()) + // return true; + // } + // return false; + // } + + // private ResolvedType[] parameterizedSuperTypes = null; + + /** + * Similar to the above method, but accumulates the super types + * + * @return + */ + // public ResolvedType[] getParameterizedSuperTypes() { + // if (parameterizedSuperTypes != null) + // return parameterizedSuperTypes; + // List accumulatedTypes = new ArrayList(); + // accumulateParameterizedSuperTypes(this, accumulatedTypes); + // ResolvedType[] ret = new ResolvedType[accumulatedTypes.size()]; + // parameterizedSuperTypes = (ResolvedType[]) accumulatedTypes.toArray(ret); + // return parameterizedSuperTypes; + // } + // private void accumulateParameterizedSuperTypes(ResolvedType forType, List + // parameterizedTypeList) { + // if (forType.isParameterizedType()) { + // parameterizedTypeList.add(forType); + // } + // if (forType.getSuperclass() != null) { + // accumulateParameterizedSuperTypes(forType.getSuperclass(), + // parameterizedTypeList); + // } + // ResolvedType[] interfaces = forType.getDeclaredInterfaces(); + // for (int i = 0; i < interfaces.length; i++) { + // accumulateParameterizedSuperTypes(interfaces[i], parameterizedTypeList); + // } + // } + /** + * @return true if assignable to java.lang.Exception + */ + public boolean isException() { + return (world.getCoreType(UnresolvedType.JAVA_LANG_EXCEPTION).isAssignableFrom(this)); + } + + /** + * @return true if it is an exception and it is a checked one, false otherwise. + */ + public boolean isCheckedException() { + if (!isException()) + return false; + if (world.getCoreType(UnresolvedType.RUNTIME_EXCEPTION).isAssignableFrom(this)) + return false; + return true; + } + + /** + * Determines if variables of this type could be assigned values of another with lots of help. java.lang.Object is convertable + * from all types. A primitive type is convertable from X iff it's assignable from X. A reference type is convertable from X iff + * it's coerceable from X. In other words, X isConvertableFrom Y iff the compiler thinks that _some_ value of Y could be + * assignable to a variable of type X without loss of precision. + * + * @param other the other type + * @param world the {@link World} in which the possible assignment should be checked. + * @return true iff variables of this type could be assigned values of other with possible conversion + */ + public final boolean isConvertableFrom(ResolvedType other) { + + // // version from TypeX + // if (this.equals(OBJECT)) return true; + // if (this.isPrimitiveType() || other.isPrimitiveType()) return + // this.isAssignableFrom(other); + // return this.isCoerceableFrom(other); + // + + // version from ResolvedTypeX + if (this.equals(OBJECT)) + return true; + if (world.isInJava5Mode()) { + if (this.isPrimitiveType() ^ other.isPrimitiveType()) { // If one is + // primitive + // and the + // other + // isnt + if (validBoxing.contains(this.getSignature() + other.getSignature())) + return true; + } + } + if (this.isPrimitiveType() || other.isPrimitiveType()) + return this.isAssignableFrom(other); + return this.isCoerceableFrom(other); + } + + /** + * Determines if the variables of this type could be assigned values of another type without casting. This still allows for + * assignment conversion as per JLS 2ed 5.2. For object types, this means supertypeOrEqual(THIS, OTHER). + * + * @param other the other type + * @param world the {@link World} in which the possible assignment should be checked. + * @return true iff variables of this type could be assigned values of other without casting + * @throws NullPointerException if other is null + */ + public abstract boolean isAssignableFrom(ResolvedType other); + + public abstract boolean isAssignableFrom(ResolvedType other, boolean allowMissing); + + /** + * Determines if values of another type could possibly be cast to this type. The rules followed are from JLS 2ed 5.5, + * "Casting Conversion". + *

+ *

+ * This method should be commutative, i.e., for all UnresolvedType a, b and all World w: + *

+ *

+ * + *
+	 * a.isCoerceableFrom(b, w) == b.isCoerceableFrom(a, w)
+	 * 
+ * + *
+ * + * @param other the other type + * @param world the {@link World} in which the possible coersion should be checked. + * @return true iff values of other could possibly be cast to this type. + * @throws NullPointerException if other is null. + */ + public abstract boolean isCoerceableFrom(ResolvedType other); + + public boolean needsNoConversionFrom(ResolvedType o) { + return isAssignableFrom(o); + } + + public String getSignatureForAttribute() { + return signature; // Assume if this is being called that it is for a + // simple type (eg. void, int, etc) + } + + private FuzzyBoolean parameterizedWithTypeVariable = FuzzyBoolean.MAYBE; + + /** + * return true if the parameterization of this type includes a member type variable. Member type variables occur in generic + * methods/ctors. + */ + public boolean isParameterizedWithTypeVariable() { + // MAYBE means we haven't worked it out yet... + if (parameterizedWithTypeVariable == FuzzyBoolean.MAYBE) { + + // if there are no type parameters then we cant be... + if (typeParameters == null || typeParameters.length == 0) { + parameterizedWithTypeVariable = FuzzyBoolean.NO; + return false; + } + + for (int i = 0; i < typeParameters.length; i++) { + ResolvedType aType = (ResolvedType) typeParameters[i]; + if (aType.isTypeVariableReference() + // Changed according to the problems covered in bug 222648 + // Don't care what kind of type variable - the fact that there + // is one + // at all means we can't risk caching it against we get confused + // later + // by another variation of the parameterization that just + // happens to + // use the same type variable name + + // assume the worst - if its definetly not a type declared one, + // it could be anything + // && ((TypeVariableReference)aType).getTypeVariable(). + // getDeclaringElementKind()!=TypeVariable.TYPE + ) { + parameterizedWithTypeVariable = FuzzyBoolean.YES; + return true; + } + if (aType.isParameterizedType()) { + boolean b = aType.isParameterizedWithTypeVariable(); + if (b) { + parameterizedWithTypeVariable = FuzzyBoolean.YES; + return true; + } + } + if (aType.isGenericWildcard()) { + BoundedReferenceType boundedRT = (BoundedReferenceType) aType; + if (boundedRT.isExtends()) { + boolean b = false; + UnresolvedType upperBound = boundedRT.getUpperBound(); + if (upperBound.isParameterizedType()) { + b = ((ResolvedType) upperBound).isParameterizedWithTypeVariable(); + } else if (upperBound.isTypeVariableReference() + && ((TypeVariableReference) upperBound).getTypeVariable().getDeclaringElementKind() == TypeVariable.METHOD) { + b = true; + } + if (b) { + parameterizedWithTypeVariable = FuzzyBoolean.YES; + return true; + } + // FIXME asc need to check additional interface bounds + } + if (boundedRT.isSuper()) { + boolean b = false; + UnresolvedType lowerBound = boundedRT.getLowerBound(); + if (lowerBound.isParameterizedType()) { + b = ((ResolvedType) lowerBound).isParameterizedWithTypeVariable(); + } else if (lowerBound.isTypeVariableReference() + && ((TypeVariableReference) lowerBound).getTypeVariable().getDeclaringElementKind() == TypeVariable.METHOD) { + b = true; + } + if (b) { + parameterizedWithTypeVariable = FuzzyBoolean.YES; + return true; + } + } + } + } + parameterizedWithTypeVariable = FuzzyBoolean.NO; + } + return parameterizedWithTypeVariable.alwaysTrue(); + } + + protected boolean ajMembersNeedParameterization() { + if (isParameterizedType()) + return true; + if (getSuperclass() != null) + return getSuperclass().ajMembersNeedParameterization(); + return false; + } + + protected Map getAjMemberParameterizationMap() { + Map myMap = getMemberParameterizationMap(); + if (myMap.isEmpty()) { + // might extend a parameterized aspect that we also need to + // consider... + if (getSuperclass() != null) + return getSuperclass().getAjMemberParameterizationMap(); + } + return myMap; + } + + public void setBinaryPath(String binaryPath) { + this.binaryPath = binaryPath; + } + + /** + * Returns the path to the jar or class file from which this binary aspect came or null if not a binary aspect + */ + public String getBinaryPath() { + return binaryPath; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/ResolvedTypeMunger.java b/org.aspectj.matcher/src/org/aspectj/weaver/ResolvedTypeMunger.java new file mode 100644 index 000000000..1a76644f9 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/ResolvedTypeMunger.java @@ -0,0 +1,446 @@ +/* ******************************************************************* + * 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; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.File; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.SourceLocation; +import org.aspectj.util.TypeSafeEnum; + +/** + * This is an abstraction over method/field introduction. It might not have the chops to handle other inter-type declarations. This + * is the thing that is used on the eclipse side and serialized into a ConcreteTypeMunger. + */ +public abstract class ResolvedTypeMunger { + protected Kind kind; + protected ResolvedMember signature; + + /** + * The declared signature is filled in when a type munger is parameterized for application to a particular type. It represents + * the signature originally declared in the source file. + */ + protected ResolvedMember declaredSignature; + + // This list records the occurences (in order) of any names specified in the <> + // for a target type for the ITD. So for example, for List this list + // will be C,B,A - the list is used later to map other occurrences of C,B,A + // across the intertype declaration to the right type variables in the generic + // type upon which the itd is being made. + // might need serializing the class file for binary weaving. + protected List /* String */typeVariableAliases; + + private Set /* resolvedMembers */superMethodsCalled = Collections.EMPTY_SET; + + private ISourceLocation location; // Lost during serialize/deserialize ! + + public ResolvedTypeMunger(Kind kind, ResolvedMember signature) { + this.kind = kind; + this.signature = signature; + UnresolvedType declaringType = signature != null ? signature.getDeclaringType() : null; + if (declaringType != null) { + if (declaringType.isRawType()) + throw new IllegalStateException("Use generic type, not raw type"); + if (declaringType.isParameterizedType()) + throw new IllegalStateException("Use generic type, not parameterized type"); + } + // boolean aChangeOccurred = false; + // + // UnresolvedType rt = signature.getReturnType(); + // if (rt.isParameterizedType() || rt.isGenericType()) {rt = rt.getRawType();aChangeOccurred=true;} + // UnresolvedType[] pt = signature.getParameterTypes(); + // for (int i = 0; i < pt.length; i++) { + // if (pt[i].isParameterizedType() || pt[i].isGenericType()) { pt[i] = pt[i].getRawType();aChangeOccurred=true;} + // } + // if (aChangeOccurred) { + // this.signature = new + // ResolvedMemberImpl(signature.getKind(),signature.getDeclaringType(),signature.getModifiers(),rt,signature + // .getName(),pt,signature.getExceptions()); + // } + } + + public void setSourceLocation(ISourceLocation isl) { + location = isl; + } + + public ISourceLocation getSourceLocation() { + return location; + } + + // ---- + + // fromType is guaranteed to be a non-abstract aspect + // public ConcreteTypeMunger concretize(World world, ResolvedType aspectType) { + // + // ConcreteTypeMunger munger = world.concreteTypeMunger(this, aspectType); + // return munger; + // } + + public boolean matches(ResolvedType matchType, ResolvedType aspectType) { + ResolvedType onType = matchType.getWorld().resolve(signature.getDeclaringType()); + if (onType.isRawType()) + onType = onType.getGenericType(); + // System.err.println("matching: " + this + " to " + matchType + " onType = " + onType); + if (matchType.equals(onType)) { + if (!onType.isExposedToWeaver()) { + // if the onType is an interface, and it already has the member we are about + // to munge, then this is ok... + boolean ok = (onType.isInterface() && (onType.lookupMemberWithSupersAndITDs(getSignature()) != null)); + + if (!ok && onType.getWeaverState() == null) { + if (matchType.getWorld().getLint().typeNotExposedToWeaver.isEnabled()) { + matchType.getWorld().getLint().typeNotExposedToWeaver.signal(matchType.getName(), signature + .getSourceLocation()); + } + } + } + return true; + } + // System.err.println("NO MATCH DIRECT"); + + if (onType.isInterface()) { + return matchType.isTopmostImplementor(onType); + } else { + return false; + } + } + + // ---- + + public String toString() { + return "ResolvedTypeMunger(" + getKind() + ", " + getSignature() + ")"; + // .superMethodsCalled + ")"; + } + + // ---- + + public static ResolvedTypeMunger read(VersionedDataInputStream s, ISourceContext context) throws IOException { + Kind kind = Kind.read(s); + if (kind == Field) { + return NewFieldTypeMunger.readField(s, context); + } else if (kind == Method) { + return NewMethodTypeMunger.readMethod(s, context); + } else if (kind == Constructor) { + return NewConstructorTypeMunger.readConstructor(s, context); + } else if (kind == MethodDelegate) { + return MethodDelegateTypeMunger.readMethod(s, context); + } else if (kind == FieldHost) { + return MethodDelegateTypeMunger.FieldHostTypeMunger.readFieldHost(s, context); + } else { + throw new RuntimeException("unimplemented"); + } + } + + protected static Set readSuperMethodsCalled(VersionedDataInputStream s) throws IOException { + + Set ret = new HashSet(); + int n = s.readInt(); + if (n < 0) + throw new BCException("Problem deserializing type munger"); + for (int i = 0; i < n; i++) { + ret.add(ResolvedMemberImpl.readResolvedMember(s, null)); + } + return ret; + } + + protected void writeSuperMethodsCalled(DataOutputStream s) throws IOException { + + if (superMethodsCalled == null || superMethodsCalled.size() == 0) { + s.writeInt(0); + return; + } + + List ret = new ArrayList(superMethodsCalled); + Collections.sort(ret); + int n = ret.size(); + s.writeInt(n); + for (Iterator i = ret.iterator(); i.hasNext();) { + ResolvedMember m = (ResolvedMember) i.next(); + m.write(s); + } + + } + + protected static ISourceLocation readSourceLocation(VersionedDataInputStream s) throws IOException { + // Location persistence for type mungers was added after 1.2.1 was shipped... + if (s.getMajorVersion() < AjAttribute.WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ150) + return null; + SourceLocation ret = null; + ObjectInputStream ois = null; + try { + // This logic copes with the location missing from the attribute - an EOFException will + // occur on the next line and we ignore it. + ois = new ObjectInputStream(s); + Boolean validLocation = (Boolean) ois.readObject(); + if (validLocation.booleanValue()) { + File f = (File) ois.readObject(); + Integer ii = (Integer) ois.readObject(); + Integer offset = (Integer) ois.readObject(); + ret = new SourceLocation(f, ii.intValue()); + ret.setOffset(offset.intValue()); + } + } catch (EOFException eof) { + return null; // This exception occurs if processing an 'old style' file where the + // type munger attributes don't include the source location. + } catch (IOException ioe) { + // Something went wrong, maybe this is an 'old style' file that doesnt attach locations to mungers? + // (but I thought that was just an EOFException?) + ioe.printStackTrace(); + return null; + } catch (ClassNotFoundException e) { + } finally { + if (ois != null) + ois.close(); + } + return ret; + } + + protected void writeSourceLocation(DataOutputStream s) throws IOException { + ObjectOutputStream oos = new ObjectOutputStream(s); + // oos.writeObject(location); + oos.writeObject(new Boolean(location != null)); + if (location != null) { + oos.writeObject(location.getSourceFile()); + oos.writeObject(new Integer(location.getLine())); + oos.writeObject(new Integer(location.getOffset())); + } + oos.flush(); + oos.close(); + } + + public abstract void write(DataOutputStream s) throws IOException; + + public Kind getKind() { + return kind; + } + + public static class Kind extends TypeSafeEnum { + /* private */Kind(String name, int key) { + super(name, key); + } + + public static Kind read(DataInputStream s) throws IOException { + int key = s.readByte(); + switch (key) { + case 1: + return Field; + case 2: + return Method; + case 5: + return Constructor; + case 9: + return MethodDelegate; + case 10: + return FieldHost; + } + throw new BCException("bad kind: " + key); + } + + public String toString() { + // we want MethodDelegate to appear as Method in WeaveInfo messages + // TODO we may want something for fieldhost ? + if (MethodDelegate.getName().equals(getName())) { + return Method.toString(); + } else { + return super.toString(); + } + } + } + + // ---- fields + + public static final Kind Field = new Kind("Field", 1); + public static final Kind Method = new Kind("Method", 2); + public static final Kind Constructor = new Kind("Constructor", 5); + + // not serialized, only created during concretization of aspects + public static final Kind PerObjectInterface = new Kind("PerObjectInterface", 3); + public static final Kind PrivilegedAccess = new Kind("PrivilegedAccess", 4); + + public static final Kind Parent = new Kind("Parent", 6); + public static final Kind PerTypeWithinInterface = new Kind("PerTypeWithinInterface", 7); // PTWIMPL not serialized, used during + // concretization of aspects + + public static final Kind AnnotationOnType = new Kind("AnnotationOnType", 8); // not serialized + + public static final Kind MethodDelegate = new Kind("MethodDelegate", 9);// serialized, @AJ ITDs + public static final Kind FieldHost = new Kind("FieldHost", 10);// serialized, @AJ ITDs + + public static final String SUPER_DISPATCH_NAME = "superDispatch"; + + public void setSuperMethodsCalled(Set c) { + this.superMethodsCalled = c; + } + + public Set getSuperMethodsCalled() { + return superMethodsCalled; + } + + public ResolvedMember getSignature() { + return signature; + } + + // ---- + + public ResolvedMember getMatchingSyntheticMember(Member member, ResolvedType aspectType) { + if ((getSignature() != null) && getSignature().isPublic() && member.equals(getSignature())) { + return getSignature(); + } + + return null; + } + + public boolean changesPublicSignature() { + return kind == Field || kind == Method || kind == Constructor; + } + + public boolean needsAccessToTopmostImplementor() { + if (kind == Field) { + return true; + } else if (kind == Method) { + return !signature.isAbstract(); + } else { + return false; + } + } + + protected static List readInTypeAliases(VersionedDataInputStream s) throws IOException { + if (s.getMajorVersion() >= AjAttribute.WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ150) { + int count = s.readInt(); + if (count != 0) { + List aliases = new ArrayList(); + for (int i = 0; i < count; i++) { + aliases.add(s.readUTF()); + } + return aliases; + } + } + return null; + } + + protected void writeOutTypeAliases(DataOutputStream s) throws IOException { + // Write any type variable aliases + if (typeVariableAliases == null || typeVariableAliases.size() == 0) { + s.writeInt(0); + } else { + s.writeInt(typeVariableAliases.size()); + for (Iterator iter = typeVariableAliases.iterator(); iter.hasNext();) { + String element = (String) iter.next(); + s.writeUTF(element); + } + } + } + + public List getTypeVariableAliases() { + return typeVariableAliases; + } + + public boolean hasTypeVariableAliases() { + return (typeVariableAliases != null && typeVariableAliases.size() > 0); + } + + /** + * return true if type variables are specified with the target type for this ITD. e.g. this would return true: + * "int I.m() { return 42; }" + */ + public boolean sharesTypeVariablesWithGenericType() { + return (typeVariableAliases != null && typeVariableAliases.size() > 0); + } + + /** + * Parameterizes a resolved type munger for a particular usage of its target type (this is used when the target type is generic + * and the ITD shares type variables with the target) see ConcreteTypeMunger.parameterizedFor + */ + public ResolvedTypeMunger parameterizedFor(ResolvedType target) { + throw new BCException("Dont call parameterizedFor on a type munger of this kind: " + this.getClass()); + } + + // ResolvedType genericType = target; + // if (target.isRawType() || target.isParameterizedType()) genericType = genericType.getGenericType(); + // ResolvedMember parameterizedSignature = null; + // // If we are parameterizing it for a generic type, we just need to 'swap the letters' from the ones used + // // in the original ITD declaration to the ones used in the actual target type declaration. + // if (target.isGenericType()) { + // TypeVariable vars[] = target.getTypeVariables(); + // UnresolvedTypeVariableReferenceType[] varRefs = new UnresolvedTypeVariableReferenceType[vars.length]; + // for (int i = 0; i < vars.length; i++) { + // varRefs[i] = new UnresolvedTypeVariableReferenceType(vars[i]); + // } + // parameterizedSignature = getSignature().parameterizedWith(varRefs,genericType,true,typeVariableAliases); + // } else { + // // For raw and 'normal' parameterized targets (e.g. Interface, Interface) + // parameterizedSignature = + // getSignature().parameterizedWith(target.getTypeParameters(),genericType,target.isParameterizedType(),typeVariableAliases); + // } + // return new NewMethodTypeMunger(parameterizedSignature,getSuperMethodsCalled(),typeVariableAliases); + // } + // /** + // * see ResolvedTypeMunger.parameterizedFor(ResolvedType) + // */ + // public ResolvedTypeMunger parameterizedFor(ResolvedType target) { + // ResolvedType genericType = target; + // if (target.isRawType() || target.isParameterizedType()) genericType = genericType.getGenericType(); + // ResolvedMember parameterizedSignature = + // getSignature().parameterizedWith(target.getTypeParameters(),genericType,target.isParameterizedType(),typeVariableAliases); + // return new NewFieldTypeMunger(parameterizedSignature,getSuperMethodsCalled(),typeVariableAliases); + // } + + public void setDeclaredSignature(ResolvedMember rm) { + declaredSignature = rm; + } + + public ResolvedMember getDeclaredSignature() { + return declaredSignature; + } + + /** + * A late munger has to be done after shadow munging since which shadows are matched can affect the operation of the late + * munger. e.g. perobjectinterfacemunger + */ + public boolean isLateMunger() { + return false; + } + + /** + * Some type mungers are created purely to help with the implementation of shadow mungers. For example to support the cflow() + * pointcut we create a new cflow field in the aspect, and that is added via a BcelCflowCounterFieldAdder. + * + * During compilation we need to compare sets of type mungers, and if some only come into existence after the 'shadowy' type + * things have been processed, we need to ignore them during the comparison. + * + * Returning true from this method indicates the type munger exists to support 'shadowy' stuff - and so can be ignored in some + * comparison. + */ + public boolean existsToSupportShadowMunging() { + return false; + } + + public ResolvedTypeMunger parameterizeWith(Map m, World w) { + throw new BCException("Dont call parameterizeWith() on a type munger of this kind: " + this.getClass()); + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/Shadow.java b/org.aspectj.matcher/src/org/aspectj/weaver/Shadow.java new file mode 100644 index 000000000..edc8fd121 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/Shadow.java @@ -0,0 +1,666 @@ +/* ******************************************************************* + * 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.DataInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.PartialOrder; +import org.aspectj.util.TypeSafeEnum; +import org.aspectj.weaver.ast.Var; + +/* + * The superclass of anything representing a the shadow of a join point. A shadow represents + * some bit of code, and encompasses both entry and exit from that code. All shadows have a kind + * and a signature. + */ + +public abstract class Shadow { + + // every Shadow has a unique id, doesn't matter if it wraps... + private static int nextShadowID = 100; // easier to spot than zero. // OPTIMIZE is this a bug? static? + + private final Kind kind; + private final Member signature; + private Member matchingSignature; + private ResolvedMember resolvedSignature; + protected final Shadow enclosingShadow; + protected List mungers = Collections.EMPTY_LIST; + + public int shadowId = nextShadowID++; // every time we build a shadow, it gets a new id + + // ---- + protected Shadow(Kind kind, Member signature, Shadow enclosingShadow) { + this.kind = kind; + this.signature = signature; + this.enclosingShadow = enclosingShadow; + } + + // ---- + + public abstract World getIWorld(); + + public List /* ShadowMunger */getMungers() { + return mungers; + } + + /** + * could this(*) pcd ever match + */ + public final boolean hasThis() { + if (getKind().neverHasThis()) { + return false; + } else if (getKind().isEnclosingKind()) { + return !getSignature().isStatic(); + } else if (enclosingShadow == null) { + return false; + } else { + return enclosingShadow.hasThis(); + } + } + + /** + * the type of the this object here + * + * @throws IllegalStateException if there is no this here + */ + public final UnresolvedType getThisType() { + if (!hasThis()) + throw new IllegalStateException("no this"); + if (getKind().isEnclosingKind()) { + return getSignature().getDeclaringType(); + } else { + return enclosingShadow.getThisType(); + } + } + + /** + * a var referencing this + * + * @throws IllegalStateException if there is no target here + */ + public abstract Var getThisVar(); + + /** + * could target(*) pcd ever match + */ + public final boolean hasTarget() { + if (getKind().neverHasTarget()) { + return false; + } else if (getKind().isTargetSameAsThis()) { + return hasThis(); + } else { + return !getSignature().isStatic(); + } + } + + /** + * the type of the target object here + * + * @throws IllegalStateException if there is no target here + */ + public final UnresolvedType getTargetType() { + if (!hasTarget()) + throw new IllegalStateException("no target"); + return getSignature().getDeclaringType(); + } + + /** + * a var referencing the target + * + * @throws IllegalStateException if there is no target here + */ + public abstract Var getTargetVar(); + + public UnresolvedType[] getArgTypes() { + if (getKind() == FieldSet) + return new UnresolvedType[] { getSignature().getReturnType() }; + return getSignature().getParameterTypes(); + } + + public boolean isShadowForArrayConstructionJoinpoint() { + return (getKind() == ConstructorCall && signature.getDeclaringType().isArray()); + } + + public boolean isShadowForMonitor() { + return (getKind() == SynchronizationLock || getKind() == SynchronizationUnlock); + } + + // will return the right length array of ints depending on how many dimensions the array has + public ResolvedType[] getArgumentTypesForArrayConstructionShadow() { + String s = signature.getDeclaringType().getSignature(); + int pos = s.indexOf("["); + int dims = 1; + while (pos < s.length()) { + pos++; + if (pos < s.length()) + dims += (s.charAt(pos) == '[' ? 1 : 0); + } + if (dims == 1) + return new ResolvedType[] { ResolvedType.INT }; + ResolvedType[] someInts = new ResolvedType[dims]; + for (int i = 0; i < dims; i++) + someInts[i] = ResolvedType.INT; + return someInts; + } + + public UnresolvedType[] getGenericArgTypes() { + if (isShadowForArrayConstructionJoinpoint()) { + return getArgumentTypesForArrayConstructionShadow(); + } + if (isShadowForMonitor()) { + return UnresolvedType.ARRAY_WITH_JUST_OBJECT; + } + if (getKind() == FieldSet) + return new UnresolvedType[] { getResolvedSignature().getGenericReturnType() }; + return getResolvedSignature().getGenericParameterTypes(); + } + + public UnresolvedType getArgType(int arg) { + if (getKind() == FieldSet) + return getSignature().getReturnType(); + return getSignature().getParameterTypes()[arg]; + } + + public int getArgCount() { + if (getKind() == FieldSet) + return 1; + return getSignature().getParameterTypes().length; + } + + // /** + // * Return name of the argument at position 'i' at this shadow. This does not make sense for all shadows - but can be useful in + // * the case of, for example, method-execution. + // * + // * @return null if it cannot be determined + // */ + // public String getArgName(int i, World w) { + // String[] names = getSignature().getParameterNames(w); + // if (names == null || i >= names.length) + // return null; + // return names[i]; + // } + + public abstract UnresolvedType getEnclosingType(); + + public abstract Var getArgVar(int i); + + public abstract Var getThisJoinPointVar(); + + public abstract Var getThisJoinPointStaticPartVar(); + + public abstract Var getThisEnclosingJoinPointStaticPartVar(); + + // annotation variables + public abstract Var getKindedAnnotationVar(UnresolvedType forAnnotationType); + + public abstract Var getWithinAnnotationVar(UnresolvedType forAnnotationType); + + public abstract Var getWithinCodeAnnotationVar(UnresolvedType forAnnotationType); + + public abstract Var getThisAnnotationVar(UnresolvedType forAnnotationType); + + public abstract Var getTargetAnnotationVar(UnresolvedType forAnnotationType); + + public abstract Var getArgAnnotationVar(int i, UnresolvedType forAnnotationType); + + public abstract Member getEnclosingCodeSignature(); + + /** + * returns the kind of shadow this is, representing what happens under this shadow + */ + public Kind getKind() { + return kind; + } + + /** + * returns the signature of the thing under this shadow + */ + public Member getSignature() { + return signature; + } + + /** + * returns the signature of the thing under this shadow, with any synthetic arguments removed + */ + public Member getMatchingSignature() { + return matchingSignature != null ? matchingSignature : signature; + } + + public void setMatchingSignature(Member member) { + this.matchingSignature = member; + } + + /** + * returns the resolved signature of the thing under this shadow + * + */ + public ResolvedMember getResolvedSignature() { + if (resolvedSignature == null) { + resolvedSignature = signature.resolve(getIWorld()); + } + return resolvedSignature; + } + + public UnresolvedType getReturnType() { + if (kind == ConstructorCall) + return getSignature().getDeclaringType(); + else if (kind == FieldSet) + return ResolvedType.VOID; + else if (kind == SynchronizationLock || kind == SynchronizationUnlock) + return ResolvedType.VOID; + return getResolvedSignature().getGenericReturnType(); + } + + public static String METHOD_EXECUTION = "method-execution"; + public static String METHOD_CALL = "method-call"; + public static String CONSTRUCTOR_EXECUTION = "constructor-execution"; + public static String CONSTRUCTOR_CALL = "constructor-call"; + public static String FIELD_GET = "field-get"; + public static String FIELD_SET = "field-set"; + public static String STATICINITIALIZATION = "staticinitialization"; + public static String PREINITIALIZATION = "preinitialization"; + public static String INITIALIZATION = "initialization"; + public static String EXCEPTION_HANDLER = "exception-handler"; + public static String SYNCHRONIZATION_LOCK = "lock"; + public static String SYNCHRONIZATION_UNLOCK = "unlock"; + public static String ADVICE_EXECUTION = "adviceexecution"; + + /** + * These names are the ones that will be returned by thisJoinPoint.getKind() Those need to be documented somewhere + */ + public static final Kind MethodCall = new Kind(METHOD_CALL, 1, true); + public static final Kind ConstructorCall = new Kind(CONSTRUCTOR_CALL, 2, true); + public static final Kind MethodExecution = new Kind(METHOD_EXECUTION, 3, false); + public static final Kind ConstructorExecution = new Kind(CONSTRUCTOR_EXECUTION, 4, false); + public static final Kind FieldGet = new Kind(FIELD_GET, 5, true); + public static final Kind FieldSet = new Kind(FIELD_SET, 6, true); + public static final Kind StaticInitialization = new Kind(STATICINITIALIZATION, 7, false); + public static final Kind PreInitialization = new Kind(PREINITIALIZATION, 8, false); + public static final Kind AdviceExecution = new Kind(ADVICE_EXECUTION, 9, false); + public static final Kind Initialization = new Kind(INITIALIZATION, 10, false); + public static final Kind ExceptionHandler = new Kind(EXCEPTION_HANDLER, 11, true); + public static final Kind SynchronizationLock = new Kind(SYNCHRONIZATION_LOCK, 12, true); + public static final Kind SynchronizationUnlock = new Kind(SYNCHRONIZATION_UNLOCK, 13, true); + + // Bits here are 1<<(Kind.getKey()) - and unfortunately keys didn't start at zero so bits here start at 2 + public static final int MethodCallBit = 0x002; + public static final int ConstructorCallBit = 0x004; + public static final int MethodExecutionBit = 0x008; + public static final int ConstructorExecutionBit = 0x010; + public static final int FieldGetBit = 0x020; + public static final int FieldSetBit = 0x040; + public static final int StaticInitializationBit = 0x080; + public static final int PreInitializationBit = 0x100; + public static final int AdviceExecutionBit = 0x200; + public static final int InitializationBit = 0x400; + public static final int ExceptionHandlerBit = 0x800; + public static final int SynchronizationLockBit = 0x1000; + public static final int SynchronizationUnlockBit = 0x2000; + + public static final int MAX_SHADOW_KIND = 13; + public static final Kind[] SHADOW_KINDS = new Kind[] { MethodCall, ConstructorCall, MethodExecution, ConstructorExecution, + FieldGet, FieldSet, StaticInitialization, PreInitialization, AdviceExecution, Initialization, ExceptionHandler, + SynchronizationLock, SynchronizationUnlock }; + + public static final int ALL_SHADOW_KINDS_BITS; + public static final int NO_SHADOW_KINDS_BITS; + + static { + ALL_SHADOW_KINDS_BITS = 0x3ffe; + NO_SHADOW_KINDS_BITS = 0x0000; + } + + /** + * Return count of how many bits set in the supplied parameter. + */ + public static int howMany(int i) { + int count = 0; + for (int j = 0; j < SHADOW_KINDS.length; j++) { + if ((i & SHADOW_KINDS[j].bit) != 0) + count++; + } + return count; + } + + /** + * A type-safe enum representing the kind of shadows + */ + public static final class Kind extends TypeSafeEnum { + // private boolean argsOnStack; //XXX unused + + public int bit; + + public Kind(String name, int key, boolean argsOnStack) { + super(name, key); + bit = 1 << key; + // this.argsOnStack = argsOnStack; + } + + public String toLegalJavaIdentifier() { + return getName().replace('-', '_'); + } + + public boolean argsOnStack() { + return !isTargetSameAsThis(); + } + + // !!! this is false for handlers! + public boolean allowsExtraction() { + return true; + } + + public boolean isSet(int i) { + return (i & bit) != 0; + } + + // XXX revisit along with removal of priorities + public boolean hasHighPriorityExceptions() { + return !isTargetSameAsThis(); + } + + private final static int hasReturnValueFlag = MethodCallBit | ConstructorCallBit | MethodExecutionBit | FieldGetBit + | AdviceExecutionBit; + + /** + * These shadow kinds have return values that can be bound in after returning(Dooberry doo) advice. + * + * @return + */ + public boolean hasReturnValue() { + return (bit & hasReturnValueFlag) != 0; + } + + private final static int isEnclosingKindFlag = MethodExecutionBit | ConstructorExecutionBit | AdviceExecutionBit + | StaticInitializationBit | InitializationBit; + + /** + * These are all the shadows that contains other shadows within them and are often directly associated with methods. + */ + public boolean isEnclosingKind() { + return (bit & isEnclosingKindFlag) != 0; + } + + private final static int isTargetSameAsThisFlag = MethodExecutionBit | ConstructorExecutionBit | StaticInitializationBit + | PreInitializationBit | AdviceExecutionBit | InitializationBit; + + public boolean isTargetSameAsThis() { + return (bit & isTargetSameAsThisFlag) != 0; + } + + private final static int neverHasTargetFlag = ConstructorCallBit | ExceptionHandlerBit | PreInitializationBit + | StaticInitializationBit | SynchronizationLockBit | SynchronizationUnlockBit; + + public boolean neverHasTarget() { + return (bit & neverHasTargetFlag) != 0; + } + + private final static int neverHasThisFlag = PreInitializationBit | StaticInitializationBit; + + public boolean neverHasThis() { + return (bit & neverHasThisFlag) != 0; + } + + public String getSimpleName() { + int dash = getName().lastIndexOf('-'); + if (dash == -1) + return getName(); + else + return getName().substring(dash + 1); + } + + public static Kind read(DataInputStream s) throws IOException { + int key = s.readByte(); + switch (key) { + case 1: + return MethodCall; + case 2: + return ConstructorCall; + case 3: + return MethodExecution; + case 4: + return ConstructorExecution; + case 5: + return FieldGet; + case 6: + return FieldSet; + case 7: + return StaticInitialization; + case 8: + return PreInitialization; + case 9: + return AdviceExecution; + case 10: + return Initialization; + case 11: + return ExceptionHandler; + case 12: + return SynchronizationLock; + case 13: + return SynchronizationUnlock; + } + throw new BCException("unknown kind: " + key); + } + } + + /** + * Only does the check if the munger requires it (@AJ aspects don't) + * + * @param munger + * @return + */ + protected boolean checkMunger(ShadowMunger munger) { + if (munger.mustCheckExceptions()) { + for (Iterator i = munger.getThrownExceptions().iterator(); i.hasNext();) { + if (!checkCanThrow(munger, (ResolvedType) i.next())) + return false; + } + } + return true; + } + + protected boolean checkCanThrow(ShadowMunger munger, ResolvedType resolvedTypeX) { + if (getKind() == ExceptionHandler) { + // XXX much too lenient rules here, need to walk up exception handlers + return true; + } + + if (!isDeclaredException(resolvedTypeX, getSignature())) { + getIWorld().showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.CANT_THROW_CHECKED, resolvedTypeX, this), // from + // advice + // in + // \ + // '" + // + + // munger + // . + // + + // "\'" + // , + getSourceLocation(), munger.getSourceLocation()); + } + + return true; + } + + private boolean isDeclaredException(ResolvedType resolvedTypeX, Member member) { + ResolvedType[] excs = getIWorld().resolve(member.getExceptions(getIWorld())); + for (int i = 0, len = excs.length; i < len; i++) { + if (excs[i].isAssignableFrom(resolvedTypeX)) + return true; + } + return false; + } + + public void addMunger(ShadowMunger munger) { + if (checkMunger(munger)) { + if (mungers == Collections.EMPTY_LIST) + mungers = new ArrayList(); + this.mungers.add(munger); + } + } + + public final void implement() { + sortMungers(); + if (mungers == null) + return; + prepareForMungers(); + implementMungers(); + } + + private void sortMungers() { + + List sorted = PartialOrder.sort(mungers); + + // Bunch of code to work out whether to report xlints for advice that isn't ordered at this Joinpoint + possiblyReportUnorderedAdvice(sorted); + + if (sorted == null) { + // this means that we have circular dependencies + for (Iterator i = mungers.iterator(); i.hasNext();) { + ShadowMunger m = (ShadowMunger) i.next(); + getIWorld().getMessageHandler().handleMessage( + MessageUtil.error(WeaverMessages.format(WeaverMessages.CIRCULAR_DEPENDENCY, this), m.getSourceLocation())); + } + } + mungers = sorted; + } + + // not quite optimal... but the xlint is ignore by default + private void possiblyReportUnorderedAdvice(List sorted) { + if (sorted != null && getIWorld().getLint().unorderedAdviceAtShadow.isEnabled() && mungers.size() > 1) { + + // Stores a set of strings of the form 'aspect1:aspect2' which indicates there is no + // precedence specified between the two aspects at this shadow. + Set clashingAspects = new HashSet(); + int max = mungers.size(); + + // Compare every pair of advice mungers + for (int i = max - 1; i >= 0; i--) { + for (int j = 0; j < i; j++) { + Object a = mungers.get(i); + Object b = mungers.get(j); + + // Make sure they are the right type + if (a instanceof Advice && b instanceof Advice) { + Advice adviceA = (Advice) a; + Advice adviceB = (Advice) b; + if (!adviceA.concreteAspect.equals(adviceB.concreteAspect)) { + AdviceKind adviceKindA = adviceA.getKind(); + AdviceKind adviceKindB = adviceB.getKind(); + + // make sure they are the nice ones (<6) and not any synthetic advice ones we + // create to support other features of the language. + if (adviceKindA.getKey() < (byte) 6 && adviceKindB.getKey() < (byte) 6 + && adviceKindA.getPrecedence() == adviceKindB.getPrecedence()) { + + // Ask the world if it knows about precedence between these + Integer order = getIWorld().getPrecedenceIfAny(adviceA.concreteAspect, adviceB.concreteAspect); + + if (order != null && order.equals(new Integer(0))) { + String key = adviceA.getDeclaringAspect() + ":" + adviceB.getDeclaringAspect(); + String possibleExistingKey = adviceB.getDeclaringAspect() + ":" + adviceA.getDeclaringAspect(); + if (!clashingAspects.contains(possibleExistingKey)) + clashingAspects.add(key); + } + } + } + } + } + } + for (Iterator iter = clashingAspects.iterator(); iter.hasNext();) { + String element = (String) iter.next(); + String aspect1 = element.substring(0, element.indexOf(":")); + String aspect2 = element.substring(element.indexOf(":") + 1); + getIWorld().getLint().unorderedAdviceAtShadow.signal(new String[] { this.toString(), aspect1, aspect2 }, this + .getSourceLocation(), null); + } + } + } + + /** + * Prepare the shadow for implementation. After this is done, the shadow should be in such a position that each munger simply + * needs to be implemented. + */ + protected void prepareForMungers() { + throw new RuntimeException("Generic shadows cannot be prepared"); + } + + /** Actually implement the (non-empty) mungers associated with this shadow */ + private void implementMungers() { + World world = getIWorld(); + for (Iterator iter = mungers.iterator(); iter.hasNext();) { + ShadowMunger munger = (ShadowMunger) iter.next(); + munger.implementOn(this); + world.reportMatch(munger, this); + } + } + + public abstract ISourceLocation getSourceLocation(); + + // ---- utility + + public String toString() { + return getKind() + "(" + getSignature() + ")"; // + getSourceLines(); + } + + public String toResolvedString(World world) { + StringBuffer sb = new StringBuffer(); + sb.append(getKind()); + sb.append("("); + Member m = getSignature(); + if (m == null) { + sb.append("<>"); + } else { + ResolvedMember rm = world.resolve(m); + if (rm == null) { + sb.append("<>"); + } else { + String genString = rm.toGenericString(); + if (genString == null) { + sb.append("<>"); + } else { + sb.append(genString); + } + + } + } + sb.append(")"); + return sb.toString(); + // was: return getKind() + "(" + world.resolve(getSignature()).toGenericString() + ")"; + } + + /** + * Convert a bit array for the shadow kinds into a set of them... should only be used for testing - mainline code should do bit + * manipulation! + */ + public static Set toSet(int i) { + Set results = new HashSet(); + for (int j = 0; j < Shadow.SHADOW_KINDS.length; j++) { + Kind k = Shadow.SHADOW_KINDS[j]; + if (k.isSet(i)) + results.add(k); + } + return results; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/ShadowMunger.java b/org.aspectj.matcher/src/org/aspectj/weaver/ShadowMunger.java new file mode 100644 index 000000000..bc6e3982c --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/ShadowMunger.java @@ -0,0 +1,191 @@ +/* ******************************************************************* + * 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 java.util.Collection; +import java.util.Map; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.SourceLocation; +import org.aspectj.util.PartialOrder; +import org.aspectj.weaver.patterns.PerClause; +import org.aspectj.weaver.patterns.Pointcut; + +/** + * For every shadow munger, nothing can be done with it until it is concretized. Then... + * + * (Then we call fast match.) + * + * For every shadow munger, for every shadow, first match is called, then (if match returned true) the shadow munger is specialized + * for the shadow, which may modify state. Then implement is called. + */ +public abstract class ShadowMunger implements PartialOrder.PartialComparable, IHasPosition { + + protected Pointcut pointcut; + + // these three fields hold the source location of this munger + protected int start, end; + protected ISourceContext sourceContext; + private ISourceLocation sourceLocation; + private ISourceLocation binarySourceLocation; + private File binaryFile; + public String handle = null; + private ResolvedType declaringType; // the type that declared this munger. + + public ShadowMunger(Pointcut pointcut, int start, int end, ISourceContext sourceContext) { + this.pointcut = pointcut; + this.start = start; + this.end = end; + this.sourceContext = sourceContext; + } + + public abstract ShadowMunger concretize(ResolvedType fromType, World world, PerClause clause); + + public abstract void specializeOn(Shadow shadow); + + public abstract void implementOn(Shadow shadow); + + /** + * All overriding methods should call super + */ + public boolean match(Shadow shadow, World world) { + return pointcut.match(shadow).maybeTrue(); + } + + public abstract ShadowMunger parameterizeWith(ResolvedType declaringType, Map typeVariableMap); + + public int fallbackCompareTo(Object other) { + return toString().compareTo(toString()); + } + + public int getEnd() { + return end; + } + + public int getStart() { + return start; + } + + public ISourceLocation getSourceLocation() { + if (sourceLocation == null) { + if (sourceContext != null) { + sourceLocation = sourceContext.makeSourceLocation(this); + } + } + if (isBinary()) { + if (binarySourceLocation == null) { + binarySourceLocation = getBinarySourceLocation(sourceLocation); + } + return binarySourceLocation; + } + return sourceLocation; + } + + // ---- fields + + public static final ShadowMunger[] NONE = new ShadowMunger[0]; + + public Pointcut getPointcut() { + return pointcut; + } + + // pointcut may be updated during rewriting... + public void setPointcut(Pointcut pointcut) { + this.pointcut = pointcut; + } + + /** + * Invoked when the shadow munger of a resolved type are processed. + * + * @param aType + */ + public void setDeclaringType(ResolvedType aType) { + declaringType = aType; + } + + public ResolvedType getDeclaringType() { + return declaringType; + } + + /** + * @return a Collection of ResolvedType for all checked exceptions that might be thrown by this munger + */ + public abstract Collection getThrownExceptions(); + + /** + * Does the munger has to check that its exception are accepted by the shadow ? ATAJ: It s not the case for @AJ around advice + * f.e. that can throw Throwable, even if the advised method does not throw any exceptions. + * + * @return true if munger has to check that its exceptions can be throwned based on the shadow + */ + public abstract boolean mustCheckExceptions(); + + /** + * 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. + */ + public ISourceLocation getBinarySourceLocation(ISourceLocation sl) { + if (sl == null) + return null; + String sourceFileName = null; + if (getDeclaringType() instanceof ReferenceType) { + String s = ((ReferenceType) getDeclaringType()).getDelegate().getSourcefilename(); + int i = s.lastIndexOf('/'); + if (i != -1) { + sourceFileName = s.substring(i + 1); + } else { + sourceFileName = s; + } + } + ISourceLocation sLoc = new SourceLocation(getBinaryFile(), sl.getLine(), sl.getEndLine(), + ((sl.getColumn() == 0) ? ISourceLocation.NO_COLUMN : sl.getColumn()), sl.getContext(), sourceFileName); + return sLoc; + } + + /** + * 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 File getBinaryFile() { + if (binaryFile == null) { + String s = getDeclaringType().getBinaryPath(); + File f = getDeclaringType().getSourceLocation().getSourceFile(); + int i = f.getPath().lastIndexOf('.'); + String path = f.getPath().substring(0, i) + ".class"; + binaryFile = new File(s + "!" + path); + } + return binaryFile; + } + + /** + * Returns whether or not this shadow munger came from a binary aspect - keep a record of whether or not we've checked if we're + * binary otherwise we keep caluclating the same thing many times + */ + public boolean isBinary() { + if (!checkedIsBinary) { + ResolvedType rt = getDeclaringType(); + if (rt != null) { + isBinary = ((rt.getBinaryPath() == null) ? false : true); + } + checkedIsBinary = true; + } + return isBinary; + } + + private boolean isBinary; + private boolean checkedIsBinary; + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/SignatureUtils.java b/org.aspectj.matcher/src/org/aspectj/weaver/SignatureUtils.java new file mode 100644 index 000000000..af1cace6a --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/SignatureUtils.java @@ -0,0 +1,246 @@ +/* ******************************************************************* + * 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 - refactored out of MemberImpl + * ******************************************************************/ +package org.aspectj.weaver; + +import java.lang.reflect.Modifier; + +public class SignatureUtils { + + public static String getSignatureString(Member m, World world) { + MemberKind kind = m.getKind(); + if (kind == Member.METHOD) { + return getMethodSignatureString(m,world); + } else if (kind == Member.CONSTRUCTOR) { + return getConstructorSignatureString(m,world); + } else if (kind == Member.FIELD) { + return getFieldSignatureString(m,world); + } else if (kind == Member.HANDLER) { + return getHandlerSignatureString(m,world); + } else if (kind == Member.STATIC_INITIALIZATION) { + return getStaticInitializationSignatureString(m,world); + } else if (kind == Member.ADVICE) { + return getAdviceSignatureString(m,world); + } else if (kind == Member.MONITORENTER || kind == Member.MONITOREXIT) { + return getMonitorSignatureString(m,world); + } else { + throw new BCException("Do not know the signature string for MemberKind "+kind); + } + } + + public static String getSignatureMakerName(Member m) { + MemberKind kind = m.getKind(); + if (kind == Member.METHOD) { + return "makeMethodSig"; + } else if (kind == Member.CONSTRUCTOR) { + return "makeConstructorSig"; + } else if (kind == Member.FIELD) { + return "makeFieldSig"; + } else if (kind == Member.HANDLER) { + return "makeCatchClauseSig"; + } else if (kind == Member.STATIC_INITIALIZATION) { + return "makeInitializerSig"; + } else if (kind == Member.ADVICE) { + return "makeAdviceSig"; + } else if (kind == Member.MONITORENTER) { + return "makeLockSig"; + } else if (kind == Member.MONITOREXIT) { + return "makeUnlockSig"; + } else { + throw new BCException("Do not know the signature maker name for MemberKind "+kind); + } + } + + public static String getSignatureType(Member m) { + MemberKind kind = m.getKind(); + if (m.getName().equals("") && kind!=Member.STATIC_INITIALIZATION) throw new BCException(); +// if (m.getName().equals("")) return "org.aspectj.lang.reflect.InitializerSignature"; + + if (kind == Member.METHOD) { + return "org.aspectj.lang.reflect.MethodSignature"; + } else if (kind == Member.CONSTRUCTOR) { + return "org.aspectj.lang.reflect.ConstructorSignature"; + } else if (kind == Member.FIELD) { + return "org.aspectj.lang.reflect.FieldSignature"; + } else if (kind == Member.HANDLER) { + return "org.aspectj.lang.reflect.CatchClauseSignature"; + } else if (kind == Member.STATIC_INITIALIZATION) { + return "org.aspectj.lang.reflect.InitializerSignature"; + } else if (kind == Member.ADVICE) { + return "org.aspectj.lang.reflect.AdviceSignature"; + } else if (kind == Member.MONITORENTER) { + return "org.aspectj.lang.reflect.LockSignature"; + } else if (kind == Member.MONITOREXIT) { + return "org.aspectj.lang.reflect.UnlockSignature"; + } else { + throw new BCException("Do not know the signature type for MemberKind "+kind); + } + } + + + // --- + + + private static String getHandlerSignatureString(Member m, World world) { + StringBuffer buf = new StringBuffer(); + buf.append(makeString(0)); + buf.append('-'); + //buf.append(getName()); + buf.append('-'); + buf.append(makeString(m.getDeclaringType())); + buf.append('-'); + buf.append(makeString(m.getParameterTypes()[0])); + buf.append('-'); + String pName = ""; + String[] names = m.getParameterNames(world); + if (names != null) pName = names[0]; + buf.append(pName); + buf.append('-'); + return buf.toString(); + } + + private static String getStaticInitializationSignatureString(Member m, World world) { + StringBuffer buf = new StringBuffer(); + buf.append(makeString(m.getModifiers(world))); + buf.append('-'); + //buf.append(getName()); + buf.append('-'); + buf.append(makeString(m.getDeclaringType())); + buf.append('-'); + return buf.toString(); + } + + protected static String getAdviceSignatureString(Member m, World world) { + StringBuffer buf = new StringBuffer(); + buf.append(makeString(m.getModifiers(world))); + buf.append('-'); + buf.append(m.getName()); + buf.append('-'); + buf.append(makeString(m.getDeclaringType())); + buf.append('-'); + buf.append(makeString(m.getParameterTypes())); + buf.append('-'); + buf.append(makeString(m.getParameterNames(world))); + buf.append('-'); + buf.append(makeString(m.getExceptions(world))); + buf.append('-'); + buf.append(makeString(m.getReturnType())); + buf.append('-'); + return buf.toString(); + } + + + protected static String getMethodSignatureString(Member m, World world) { + StringBuffer buf = new StringBuffer(); + buf.append(makeString(m.getModifiers(world))); + buf.append('-'); + buf.append(m.getName()); + buf.append('-'); + buf.append(makeString(m.getDeclaringType())); + buf.append('-'); + buf.append(makeString(m.getParameterTypes())); + buf.append('-'); + buf.append(makeString(m.getParameterNames(world))); + buf.append('-'); + buf.append(makeString(m.getExceptions(world))); + buf.append('-'); + buf.append(makeString(m.getReturnType())); + buf.append('-'); + return buf.toString(); + } + + protected static String getMonitorSignatureString(Member m, World world) { + StringBuffer buf = new StringBuffer(); + buf.append(makeString(Modifier.STATIC)); // modifiers + buf.append('-'); + buf.append(m.getName()); // name + buf.append('-'); + buf.append(makeString(m.getDeclaringType())); // Declaring Type + buf.append('-'); + buf.append(makeString(m.getParameterTypes()[0])); // Parameter Types + buf.append('-'); + buf.append(""); // Parameter names + buf.append('-'); + return buf.toString(); + } + + protected static String getConstructorSignatureString(Member m, World world) { + StringBuffer buf = new StringBuffer(); + buf.append(makeString(m.getModifiers(world))); + buf.append('-'); + buf.append('-'); + buf.append(makeString(m.getDeclaringType())); + buf.append('-'); + buf.append(makeString(m.getParameterTypes())); + buf.append('-'); + buf.append(makeString(m.getParameterNames(world))); + buf.append('-'); + buf.append(makeString(m.getExceptions(world))); + buf.append('-'); + return buf.toString(); + } + + + + + protected static String getFieldSignatureString(Member m, World world) { + StringBuffer buf = new StringBuffer(); + buf.append(makeString(m.getModifiers(world))); + buf.append('-'); + buf.append(m.getName()); + buf.append('-'); + buf.append(makeString(m.getDeclaringType())); + buf.append('-'); + buf.append(makeString(m.getReturnType())); + buf.append('-'); + return buf.toString(); + } + + protected static String makeString(int i) { + return Integer.toString(i, 16); + } + + protected static String makeString(UnresolvedType t) { + // this is the inverse of the odd behavior for Class.forName w/ arrays + if (t.isArray()) { + // this behavior matches the string used by the eclipse compiler for Foo.class literals + return t.getSignature().replace('/', '.'); + } else { + return t.getName(); + } + } + + + + protected static String makeString(UnresolvedType[] types) { + if (types == null) return ""; + StringBuffer buf = new StringBuffer(); + for (int i = 0, len=types.length; i < len; i++) { + buf.append(makeString(types[i])); + buf.append(':'); + } + return buf.toString(); + } + + + + protected static String makeString(String[] names) { + if (names == null) return ""; + StringBuffer buf = new StringBuffer(); + for (int i = 0, len=names.length; i < len; i++) { + buf.append(names[i]); + buf.append(':'); + } + return buf.toString(); + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/SimpleAnnotationValue.java b/org.aspectj.matcher/src/org/aspectj/weaver/SimpleAnnotationValue.java new file mode 100644 index 000000000..41dab703c --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/SimpleAnnotationValue.java @@ -0,0 +1,82 @@ +/* ******************************************************************* + * 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 IBM initial implementation + * ******************************************************************/ + package org.aspectj.weaver; + +public class SimpleAnnotationValue extends AnnotationValue { + + public SimpleAnnotationValue(int kind) { + super(kind); + } + + public SimpleAnnotationValue(int kind, Object value) { + super(kind); + switch (kind) { + case AnnotationValue.PRIMITIVE_BYTE: theByte = ((Byte)value).byteValue(); break; + case AnnotationValue.PRIMITIVE_CHAR: theChar = ((Character)value).charValue(); break; + case AnnotationValue.PRIMITIVE_INT: theInt = ((Integer)value).intValue(); break; + case AnnotationValue.STRING: theString = (String)value; break; + case AnnotationValue.PRIMITIVE_DOUBLE: theDouble = ((Double)value).doubleValue(); break; + case AnnotationValue.PRIMITIVE_FLOAT: theFloat = ((Float)value).floatValue(); break; + case AnnotationValue.PRIMITIVE_LONG: theLong = ((Long)value).longValue(); break; + case AnnotationValue.PRIMITIVE_SHORT: theShort = ((Short)value).shortValue(); break; + case AnnotationValue.PRIMITIVE_BOOLEAN: theBoolean = ((Boolean)value).booleanValue(); break; + default: + throw new BCException("Not implemented for this kind: "+whatKindIsThis(kind)); + } + } + + private byte theByte; + private char theChar; + private int theInt; + private String theString; + private double theDouble; + private float theFloat; + private long theLong; + private short theShort; + private boolean theBoolean; + + public void setValueString(String s) { theString = s; } + public void setValueByte(byte b) { theByte = b; } + public void setValueChar(char c) { theChar = c; } + public void setValueInt(int i) { theInt = i; } + + + public String stringify() { + switch (valueKind) { + case 'B': // byte + return Byte.toString(theByte); + case 'C': // char + return new Character(theChar).toString(); + case 'D': // double + return Double.toString(theDouble); + case 'F': // float + return Float.toString(theFloat); + case 'I': // int + return Integer.toString(theInt); + case 'J': // long + return Long.toString(theLong); + case 'S': // short + return Short.toString(theShort); + case 'Z': // boolean + return new Boolean(theBoolean).toString(); + case 's': // String + return theString; + default: throw new BCException("Do not understand this kind: "+valueKind); + } + } + + public String toString() { + return stringify(); + } + + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/SourceContextImpl.java b/org.aspectj.matcher/src/org/aspectj/weaver/SourceContextImpl.java new file mode 100644 index 000000000..76f0dbd62 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/SourceContextImpl.java @@ -0,0 +1,109 @@ +/* ******************************************************************* + * 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 java.util.Arrays; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.SourceLocation; + +public class SourceContextImpl implements ISourceContext { + +// private AbstractReferenceTypeDelegate delegate; + private int[] lineBreaks; + String sfname; + + public SourceContextImpl(AbstractReferenceTypeDelegate delegate) { +// this.delegate = delegate; + sfname = delegate.getSourcefilename(); + } + + public void configureFromAttribute(String name,int []linebreaks) { +// this.delegate.setSourcefilename(name); + sfname = name; + this.lineBreaks = linebreaks; + } + + public void setSourceFileName(String name) { + sfname = name; + } + + private File getSourceFile() { + return new File(sfname); +// return new File(delegate.getSourcefilename()); + } + + public void tidy() {} + + public int getOffset() { return 0; } + + /* + // AMC - a temporary "fudge" to give as much information as possible about the identity of the + // source file this source location points to. + String internalClassName = getEnclosingClass().getInternalClassName(); + String fileName = getEnclosingClass().getFileName(); + String extension = fileName.substring( fileName.lastIndexOf("."), fileName.length()); + String filePrefix = fileName.substring( 0, fileName.lastIndexOf(".")); + // internal class name is e.g. figures/Point, we don't know whether the file was + // .aj or .java so we put it together with the file extension of the enclosing class + // BUT... sometimes internalClassName is a different class (an aspect), so we only use it if it + // matches the file name. + String mostAccurateFileNameGuess; + if ( internalClassName.endsWith(filePrefix)) { + mostAccurateFileNameGuess = internalClassName + extension; + } else { + mostAccurateFileNameGuess = fileName; + } + return new SourceLocation(new File(mostAccurateFileNameGuess), getSourceLine()); + */ + + + + public ISourceLocation makeSourceLocation(IHasPosition position) { + if (lineBreaks != null) { + int line = Arrays.binarySearch(lineBreaks, position.getStart()); + if (line < 0) line = -line; + return new SourceLocation(getSourceFile(), line); //??? have more info + } else { + return new SourceLocation(getSourceFile(), 0); + } + } + + public ISourceLocation makeSourceLocation(int line, int offset) { + if (line < 0) line = 0; + SourceLocation sl = new SourceLocation(getSourceFile(), line); + if (offset > 0) { + sl.setOffset(offset); + } else { + if (lineBreaks != null) { + int likelyOffset = 0; + if (line > 0 && line < lineBreaks.length) { + //1st char of given line is next char after previous end of line + likelyOffset = lineBreaks[line-1] + 1; + } + sl.setOffset(likelyOffset); + } + } + return sl; + } + + + public final static ISourceContext UNKNOWN_SOURCE_CONTEXT = new ISourceContext() { + public ISourceLocation makeSourceLocation(IHasPosition position) {return null;} + public ISourceLocation makeSourceLocation(int line, int offset) {return null;} + public int getOffset() {return 0;} + public void tidy() {} + }; +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/StandardAnnotation.java b/org.aspectj.matcher/src/org/aspectj/weaver/StandardAnnotation.java new file mode 100644 index 000000000..3dd7bfb82 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/StandardAnnotation.java @@ -0,0 +1,153 @@ +/* ******************************************************************* + * 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; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +/** + * This type represents the weavers abstraction of an annotation - it is not tied to any underlying BCI toolkit. The weaver actualy + * handles these through AnnotationX wrapper objects - until we start transforming the BCEL annotations into this form (expensive) + * or offer a clever visitor mechanism over the BCEL annotation stuff that builds these annotation types directly. + * + * @author AndyClement + */ +public class StandardAnnotation extends AbstractAnnotationAJ { + + private final boolean isRuntimeVisible; + + private List /* of AnnotationNVPair */nvPairs = null; + + public StandardAnnotation(ResolvedType type, boolean isRuntimeVisible) { + super(type); + this.isRuntimeVisible = isRuntimeVisible; + } + + /** + * {@inheritDoc} + */ + public boolean isRuntimeVisible() { + return isRuntimeVisible; + } + + /** + * {@inheritDoc} + */ + public String stringify() { + StringBuffer sb = new StringBuffer(); + sb.append("@").append(type.getClassName()); + if (hasNameValuePairs()) { + sb.append("("); + for (Iterator iter = nvPairs.iterator(); iter.hasNext();) { + AnnotationNameValuePair element = (AnnotationNameValuePair) iter.next(); + sb.append(element.stringify()); + } + sb.append(")"); + } + return sb.toString(); + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append("ANNOTATION [" + getTypeSignature() + "] [" + (isRuntimeVisible ? "runtimeVisible" : "runtimeInvisible") + "] ["); + if (nvPairs != null) { + for (Iterator iter = nvPairs.iterator(); iter.hasNext();) { + AnnotationNameValuePair element = (AnnotationNameValuePair) iter.next(); + sb.append(element.toString()); + if (iter.hasNext()) + sb.append(","); + } + } + sb.append("]"); + return sb.toString(); + } + + /** + * {@inheritDoc} + */ + public boolean hasNamedValue(String n) { + if (nvPairs == null) + return false; + for (int i = 0; i < nvPairs.size(); i++) { + AnnotationNameValuePair pair = (AnnotationNameValuePair) nvPairs.get(i); + if (pair.getName().equals(n)) + return true; + } + return false; + } + + /** + * {@inheritDoc} + */ + public boolean hasNameValuePair(String n, String v) { + if (nvPairs == null) + return false; + for (int i = 0; i < nvPairs.size(); i++) { + AnnotationNameValuePair pair = (AnnotationNameValuePair) nvPairs.get(i); + if (pair.getName().equals(n)) { + if (pair.getValue().stringify().equals(v)) + return true; + } + } + return false; + } + + /** + * {@inheritDoc} + */ + public Set /* */getTargets() { + if (!type.equals(UnresolvedType.AT_TARGET)) { + return Collections.EMPTY_SET; + } + AnnotationNameValuePair nvp = (AnnotationNameValuePair) nvPairs.get(0); + ArrayAnnotationValue aav = (ArrayAnnotationValue) nvp.getValue(); + AnnotationValue[] avs = aav.getValues(); + Set targets = new HashSet(); + for (int i = 0; i < avs.length; i++) { + AnnotationValue value = avs[i]; + targets.add(value.stringify()); + } + return targets; + } + + public List getNameValuePairs() { + return nvPairs; + } + + public boolean hasNameValuePairs() { + return nvPairs != null && nvPairs.size() != 0; + } + + public void addNameValuePair(AnnotationNameValuePair pair) { + if (nvPairs == null) { + nvPairs = new ArrayList(); + } + nvPairs.add(pair); + } + + /** + * {@inheritDoc} + */ + public String getStringFormOfValue(String name) { + if (hasNameValuePairs()) { + for (Iterator iterator = nvPairs.iterator(); iterator.hasNext();) { + AnnotationNameValuePair nvPair = (AnnotationNameValuePair) iterator.next(); + if (nvPair.getName().equals(name)) { + return nvPair.getValue().stringify(); + } + } + } + return null; + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/StaticJoinPointFactory.java b/org.aspectj.matcher/src/org/aspectj/weaver/StaticJoinPointFactory.java new file mode 100644 index 000000000..c7f4f86ff --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/StaticJoinPointFactory.java @@ -0,0 +1,56 @@ +/* ******************************************************************* + * 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; + + +public class StaticJoinPointFactory { +// int usedKeys; +// +// List/*String*/ strings = new ArrayList(); +// Map/*String,Integer*/ keysForStrings = new HashMap(); +// +// public StaticJoinPointFactory() { +// super(); +// } +// +// static char[] encoding = new char[] { +// '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',//10 +// 'a', 'b', 'z', //36 +// 'A', 'B', 'Z', //62 +// '%', '$', //64 +// }; +// +// static int TWO_WORDS = 64*64-1; +// static int WORD_MASK = 63; +// +// public void write(String s, StringBuffer result) { +// int i = getIndex(s); +// encode(i, result); +// } +// +// void encode(int i, StringBuffer result) { +// if (i > TWO_WORDS) { +// throw new RuntimeException("unimplemented"); +// } else { +// result.append( encoding[(i >> 6) & WORD_MASK] ); +// result.append( encoding[i & WORD_MASK] ); +// } +// } +// +// public String read(StringReader reader) { +// int i = reader.read(); +// +// } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/TemporaryTypeMunger.java b/org.aspectj.matcher/src/org/aspectj/weaver/TemporaryTypeMunger.java new file mode 100644 index 000000000..2d31b597a --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/TemporaryTypeMunger.java @@ -0,0 +1,35 @@ +/* ******************************************************************* + * 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; + +import java.util.Map; + +/** + * Some methods need a temporary type munger (because ConcreteTypeMunger is abstract - dont ask...). + * + * TODO ought to remove the need for this or at least sort out the two methods that are in it, they look wierd... + * + * @author AndyClement + */ +public class TemporaryTypeMunger extends ConcreteTypeMunger { + + public TemporaryTypeMunger(ResolvedTypeMunger munger, ResolvedType aspectType) { + super(munger, aspectType); + } + + public ConcreteTypeMunger parameterizeWith(Map parameterizationMap, World world) { + throw new UnsupportedOperationException("Cannot be called on a TemporaryTypeMunger"); + } + + public ConcreteTypeMunger parameterizedFor(ResolvedType targetType) { + throw new UnsupportedOperationException("Cannot be called on a TemporaryTypeMunger"); + } + +} \ No newline at end of file diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/TypeFactory.java b/org.aspectj.matcher/src/org/aspectj/weaver/TypeFactory.java new file mode 100644 index 000000000..2200224ae --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/TypeFactory.java @@ -0,0 +1,293 @@ +/* ******************************************************************* + * 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.util.ArrayList; +import java.util.List; + +/** + * @author colyer + * + */ +public class TypeFactory { + + /** + * Create a parameterized version of a generic type. + * + * @param aGenericType + * @param someTypeParameters note, in the case of an inner type of a parameterized type, this parameter may legitimately be null + * @param inAWorld + * @return + */ + public static ReferenceType createParameterizedType(ResolvedType aBaseType, UnresolvedType[] someTypeParameters, World inAWorld) { + ResolvedType baseType = aBaseType; + if (!aBaseType.isGenericType()) { + // try and find the generic type... + if (someTypeParameters != null && someTypeParameters.length > 0) { + if (!aBaseType.isRawType()) + throw new IllegalStateException("Expecting raw type, not: " + aBaseType); + baseType = baseType.getGenericType(); + if (baseType == null) + throw new IllegalStateException("Raw type does not have generic type set"); + } // else if someTypeParameters is null, then the base type is allowed to be non-generic, it's an inner + } + ResolvedType[] resolvedParameters = inAWorld.resolve(someTypeParameters); + ReferenceType pType = new ReferenceType(baseType, resolvedParameters, inAWorld); + // pType.setSourceContext(aBaseType.getSourceContext()); + return (ReferenceType) pType.resolve(inAWorld); + } + + /** + * Create an *unresolved* parameterized version of a generic type. + */ + public static UnresolvedType createUnresolvedParameterizedType(String sig, String erasuresig, UnresolvedType[] arguments) { + return new UnresolvedType(sig, erasuresig, arguments); + } + + // public static ReferenceType createRawType( + // ResolvedType aBaseType, + // World inAWorld + // ) { + // if (aBaseType.isRawType()) return (ReferenceType) aBaseType; + // if (!aBaseType.isGenericType()) { + // if (!aBaseType.isRawType()) throw new IllegalStateException("Expecting generic type"); + // } + // ReferenceType rType = new ReferenceType(aBaseType,inAWorld); + // //rType.setSourceContext(aBaseType.getSourceContext()); + // return (ReferenceType) rType.resolve(inAWorld); + // } + + /** + * Creates a sensible unresolvedtype from some signature, for example: signature = LIGuard; bound = toString=IGuard + * sig=PIGuard; sigErasure=LIGuard; kind=parameterized + */ + private static UnresolvedType convertSigToType(String aSignature) { + UnresolvedType bound = null; + int startOfParams = aSignature.indexOf('<'); + if (startOfParams == -1) { + bound = UnresolvedType.forSignature(aSignature); + } else { + int endOfParams = aSignature.lastIndexOf('>'); + String signatureErasure = "L" + aSignature.substring(1, startOfParams) + ";"; + UnresolvedType[] typeParams = createTypeParams(aSignature.substring(startOfParams + 1, endOfParams)); + bound = new UnresolvedType("P" + aSignature.substring(1), signatureErasure, typeParams); + } + return bound; + } + + /** + * Used by UnresolvedType.read, creates a type from a full signature. + * + * @param signature + * @return + */ + public static UnresolvedType createTypeFromSignature(String signature) { + if (signature.equals(ResolvedType.MISSING_NAME)) + return ResolvedType.MISSING; + char firstChar = signature.charAt(0); + if (firstChar == 'P') { + // parameterized type, calculate signature erasure and type parameters + // (see pr122458) It is possible for a parameterized type to have *no* type parameters visible in its signature. + // This happens for an inner type of a parameterized type which simply inherits the type parameters + // of its parent. In this case it is parameterized but theres no < in the signature. + int startOfParams = signature.indexOf('<'); + + if (startOfParams == -1) { + // Should be an inner type of a parameterized type - could assert there is a '$' in the signature.... + String signatureErasure = "L" + signature.substring(1); + UnresolvedType[] typeParams = new UnresolvedType[0]; + return new UnresolvedType(signature, signatureErasure, typeParams); + } else { + int endOfParams = locateMatchingEndBracket(signature, startOfParams);// signature.lastIndexOf('>'); + StringBuffer erasureSig = new StringBuffer(signature); + while (startOfParams != -1) { + erasureSig.delete(startOfParams, endOfParams + 1); + startOfParams = locateFirstBracket(erasureSig); + if (startOfParams != -1) + endOfParams = locateMatchingEndBracket(erasureSig, startOfParams); + } + + String signatureErasure = "L" + erasureSig.toString().substring(1); + + // the type parameters of interest are only those that apply to the 'last type' in the signature + // if the signature is 'PMyInterface$MyOtherType;' then there are none... + String lastType = null; + int nestedTypePosition = signature.indexOf("$", endOfParams); // don't look for $ INSIDE the parameters + if (nestedTypePosition != -1) + lastType = signature.substring(nestedTypePosition + 1); + else + lastType = new String(signature); + startOfParams = lastType.indexOf("<"); + endOfParams = locateMatchingEndBracket(lastType, startOfParams); + UnresolvedType[] typeParams = UnresolvedType.NONE; + if (startOfParams != -1) { + typeParams = createTypeParams(lastType.substring(startOfParams + 1, endOfParams)); + } + return new UnresolvedType(signature, signatureErasure, typeParams); + } + // can't replace above with convertSigToType - leads to stackoverflow + } else if (signature.equals("?") || signature.equals("*")) { + return WildcardedUnresolvedType.QUESTIONMARK; + } else if (firstChar == '+') { + // ? extends ... + UnresolvedType upperBound = convertSigToType(signature.substring(1)); + WildcardedUnresolvedType wildcardedUT = new WildcardedUnresolvedType(signature, upperBound, null); + return wildcardedUT; + } else if (firstChar == '-') { + // ? super ... + UnresolvedType lowerBound = convertSigToType(signature.substring(1)); + WildcardedUnresolvedType wildcardedUT = new WildcardedUnresolvedType(signature, null, lowerBound); + return wildcardedUT; + } else if (firstChar == 'T') { + String typeVariableName = signature.substring(1); + if (typeVariableName.endsWith(";")) { + typeVariableName = typeVariableName.substring(0, typeVariableName.length() - 1); + } + return new UnresolvedTypeVariableReferenceType(new TypeVariable(typeVariableName)); + } else if (firstChar == '[') { + int dims = 0; + while (signature.charAt(dims) == '[') + dims++; + UnresolvedType componentType = createTypeFromSignature(signature.substring(dims)); + return new UnresolvedType(signature, signature.substring(0, dims) + componentType.getErasureSignature()); + } else if (signature.length() == 1) { // could be a primitive + switch (firstChar) { + case 'V': + return ResolvedType.VOID; + case 'Z': + return ResolvedType.BOOLEAN; + case 'B': + return ResolvedType.BYTE; + case 'C': + return ResolvedType.CHAR; + case 'D': + return ResolvedType.DOUBLE; + case 'F': + return ResolvedType.FLOAT; + case 'I': + return ResolvedType.INT; + case 'J': + return ResolvedType.LONG; + case 'S': + return ResolvedType.SHORT; + } + } + return new UnresolvedType(signature); + } + + private static int locateMatchingEndBracket(String signature, int startOfParams) { + if (startOfParams == -1) + return -1; + int count = 1; + int idx = startOfParams; + while (count > 0 && idx < signature.length()) { + idx++; + if (signature.charAt(idx) == '<') + count++; + if (signature.charAt(idx) == '>') + count--; + } + return idx; + } + + private static int locateMatchingEndBracket(StringBuffer signature, int startOfParams) { + if (startOfParams == -1) + return -1; + int count = 1; + int idx = startOfParams; + while (count > 0 && idx < signature.length()) { + idx++; + if (signature.charAt(idx) == '<') + count++; + if (signature.charAt(idx) == '>') + count--; + } + return idx; + } + + private static int locateFirstBracket(StringBuffer signature) { + int idx = 0; + while (idx < signature.length()) { + if (signature.charAt(idx) == '<') + return idx; + idx++; + } + return -1; + } + + private static UnresolvedType[] createTypeParams(String typeParameterSpecification) { + String remainingToProcess = typeParameterSpecification; + List types = new ArrayList(); + while (!remainingToProcess.equals("")) { + int endOfSig = 0; + int anglies = 0; + boolean sigFound = false; // OPTIMIZE can this be done better? + for (endOfSig = 0; (endOfSig < remainingToProcess.length()) && !sigFound; endOfSig++) { + char thisChar = remainingToProcess.charAt(endOfSig); + switch (thisChar) { + case '<': + anglies++; + break; + case '>': + anglies--; + break; + case '[': + if (anglies == 0) { + // the next char might be a [ or a primitive type ref (BCDFIJSZ) + int nextChar = endOfSig + 1; + while (remainingToProcess.charAt(nextChar) == '[') { + nextChar++; + } + if ("BCDFIJSZ".indexOf(remainingToProcess.charAt(nextChar)) != -1) { + // it is something like [I or [[S + sigFound = true; + endOfSig = nextChar; + break; + } + } + break; + case ';': + if (anglies == 0) { + sigFound = true; + break; + } + } + } + types.add(createTypeFromSignature(remainingToProcess.substring(0, endOfSig))); + remainingToProcess = remainingToProcess.substring(endOfSig); + } + UnresolvedType[] typeParams = new UnresolvedType[types.size()]; + types.toArray(typeParams); + return typeParams; + } + + // OPTIMIZE improve all this signature processing stuff, use char arrays, etc + + /** + * Create a signature then delegate to the other factory method. Same input/output: baseTypeSignature="LSomeType;" arguments[0]= + * something with sig "Pcom/Foo;" signature created = "PSomeType;>;" + */ + public static UnresolvedType createUnresolvedParameterizedType(String baseTypeSignature, UnresolvedType[] arguments) { + StringBuffer parameterizedSig = new StringBuffer(); + parameterizedSig.append(ResolvedType.PARAMETERIZED_TYPE_IDENTIFIER); + parameterizedSig.append(baseTypeSignature.substring(1, baseTypeSignature.length() - 1)); + if (arguments.length > 0) { + parameterizedSig.append("<"); + for (int i = 0; i < arguments.length; i++) { + parameterizedSig.append(arguments[i].getSignature()); + } + parameterizedSig.append(">"); + } + parameterizedSig.append(";"); + return createUnresolvedParameterizedType(parameterizedSig.toString(), baseTypeSignature, arguments); + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/TypeVariable.java b/org.aspectj.matcher/src/org/aspectj/weaver/TypeVariable.java new file mode 100644 index 000000000..2762fa748 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/TypeVariable.java @@ -0,0 +1,372 @@ +/* ******************************************************************* + * 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.io.DataOutputStream; +import java.io.IOException; + +/** + * Represents a type variable with bounds + */ +public class TypeVariable { + + public static final TypeVariable[] NONE = new TypeVariable[0]; + /** + * whether or not the bounds of this type variable have been + * resolved + */ + private boolean isResolved = false; + + + private boolean beingResolved = false; + + /** + * the name of the type variable as recorded in the generic signature + */ + private String name; + + private int rank; + + // It would be nice to push this field onto the TypeVariableDeclaringElement + // interface (a getKind()) but at the moment we don't always guarantee + // to set the declaring element (eclipse seems to utilise the knowledge of + // what declared the type variable, but we dont yet...) + /** + * What kind of element declared this type variable? + */ + private int declaringElementKind = UNKNOWN; + public static final int UNKNOWN = -1; + public static final int METHOD = 1; + public static final int TYPE = 2; + private TypeVariableDeclaringElement declaringElement; + + /** + * the upper bound of the type variable (default to Object). + * From the extends clause, eg. T extends Number. + */ + private UnresolvedType upperBound = UnresolvedType.OBJECT; + + /** + * any additional upper (interface) bounds. + * from the extends clause, e.g. T extends Number & Comparable + */ + private UnresolvedType[] additionalInterfaceBounds = new UnresolvedType[0]; + + /** + * any lower bound. + * from the super clause, eg T super Foo + */ + private UnresolvedType lowerBound = null; + + public TypeVariable(String aName) { + this.name = aName; + } + + public TypeVariable(String aName, UnresolvedType anUpperBound) { + this(aName); + this.upperBound = anUpperBound; + } + + public TypeVariable(String aName, UnresolvedType anUpperBound, + UnresolvedType[] someAdditionalInterfaceBounds) { + this(aName,anUpperBound); + this.additionalInterfaceBounds = someAdditionalInterfaceBounds; + } + + public TypeVariable(String aName, UnresolvedType anUpperBound, + UnresolvedType[] someAdditionalInterfaceBounds, UnresolvedType aLowerBound) { + this(aName,anUpperBound,someAdditionalInterfaceBounds); + this.lowerBound = aLowerBound; + } + + // First bound is the first 'real' bound, this can be an interface if + // no class bound was specified (it will default to object) + public UnresolvedType getFirstBound() { + if (upperBound.equals(UnresolvedType.OBJECT) && additionalInterfaceBounds!=null && additionalInterfaceBounds.length!=0) { + return additionalInterfaceBounds[0]; + } + return upperBound; + } + + public UnresolvedType getUpperBound() { + return upperBound; + } + + public UnresolvedType[] getAdditionalInterfaceBounds() { + return additionalInterfaceBounds; + } + + public UnresolvedType getLowerBound() { + return lowerBound; + } + + public String getName() { + return name; + } + + /** + * resolve all the bounds of this type variable + */ + public TypeVariable resolve(World inSomeWorld) { + if (beingResolved) { return this; } // avoid spiral of death + beingResolved = true; + if (isResolved) return this; + + TypeVariable resolvedTVar = null; + + if (declaringElement != null) { + // resolve by finding the real type var that we refer to... + if (declaringElementKind == TYPE) { + UnresolvedType declaring = (UnresolvedType) declaringElement; + ReferenceType rd = (ReferenceType) declaring.resolve(inSomeWorld); + TypeVariable[] tVars = rd.getTypeVariables(); + for (int i = 0; i < tVars.length; i++) { + if (tVars[i].getName().equals(getName())) { + resolvedTVar = tVars[i]; + break; + } + } + } else { + // look for type variable on method... + ResolvedMember declaring = (ResolvedMember) declaringElement; + TypeVariable[] tvrts = declaring.getTypeVariables(); + for (int i = 0; i < tvrts.length; i++) { + if (tvrts[i].getName().equals(getName())) resolvedTVar = tvrts[i]; +// if (tvrts[i].isTypeVariableReference()) { +// TypeVariableReferenceType tvrt = (TypeVariableReferenceType) tvrts[i].resolve(inSomeWorld); +// TypeVariable tv = tvrt.getTypeVariable(); +// if (tv.getName().equals(getName())) resolvedTVar = tv; +// } + } + } + + if (resolvedTVar == null) { + // well, this is bad... we didn't find the type variable on the member + // could be a separate compilation issue... + // should issue message, this is a workaround to get us going... + resolvedTVar = this; + } + } else { + resolvedTVar = this; + } + + upperBound = resolvedTVar.upperBound; + lowerBound = resolvedTVar.lowerBound; + additionalInterfaceBounds = resolvedTVar.additionalInterfaceBounds; + + upperBound = upperBound.resolve(inSomeWorld); + if (lowerBound != null) lowerBound = lowerBound.resolve(inSomeWorld); + + if (additionalInterfaceBounds!=null) { + for (int i = 0; i < additionalInterfaceBounds.length; i++) { + additionalInterfaceBounds[i] = additionalInterfaceBounds[i].resolve(inSomeWorld); + } + } + isResolved = true; + beingResolved = false; + return this; + } + + /** + * answer true if the given type satisfies all of the bound constraints of this + * type variable. + * If type variable has not been resolved then throws IllegalStateException + */ + public boolean canBeBoundTo(ResolvedType aCandidateType) { + if (!isResolved) throw new IllegalStateException("Can't answer binding questions prior to resolving"); + + // wildcard can accept any binding + if (aCandidateType.isGenericWildcard()) { // AMC - need a more robust test! + return true; + } + + // otherwise can be bound iff... + // aCandidateType is a subtype of upperBound + if (!isASubtypeOf(upperBound,aCandidateType)) { + return false; + } + // aCandidateType is a subtype of all additionalInterfaceBounds + for (int i = 0; i < additionalInterfaceBounds.length; i++) { + if (!isASubtypeOf(additionalInterfaceBounds[i], aCandidateType)) { + return false; + } + } + // lowerBound is a subtype of aCandidateType + if ((lowerBound != null) && (!isASubtypeOf(aCandidateType,lowerBound))) { + return false; + } + return true; + } + + private boolean isASubtypeOf(UnresolvedType candidateSuperType, UnresolvedType candidateSubType) { + ResolvedType superType = (ResolvedType) candidateSuperType; + ResolvedType subType = (ResolvedType) candidateSubType; + return superType.isAssignableFrom(subType); + } + + // only used when resolving + public void setUpperBound(UnresolvedType aTypeX) { + this.upperBound = aTypeX; + } + + // only used when resolving + public void setLowerBound(UnresolvedType aTypeX) { + this.lowerBound = aTypeX; + } + + // only used when resolving + public void setAdditionalInterfaceBounds(UnresolvedType[] someTypeXs) { + this.additionalInterfaceBounds = someTypeXs; + } + + public String toDebugString() { + return getDisplayName(); + } + + public String getDisplayName() { + StringBuffer ret = new StringBuffer(); + ret.append(name); + if (!getFirstBound().getName().equals("java.lang.Object")) { + ret.append(" extends "); + ret.append(getFirstBound().getName()); + if (additionalInterfaceBounds != null) { + for (int i = 0; i < additionalInterfaceBounds.length; i++) { + if (!getFirstBound().equals(additionalInterfaceBounds[i])) { + ret.append(" & "); + ret.append(additionalInterfaceBounds[i].getName()); + } + } + } + } + if (lowerBound != null) { + ret.append(" super "); + ret.append(lowerBound.getName()); + } + return ret.toString(); + } + + // good enough approximation + public String toString() { + return "TypeVar " + getDisplayName(); + } + + /** + * Return complete signature, e.g. "T extends Number" would return "T:Ljava/lang/Number;" + * note: MAY INCLUDE P types if bounds are parameterized types + */ + public String getSignature() { + StringBuffer sb = new StringBuffer(); + sb.append(name); + sb.append(":"); + sb.append(upperBound.getSignature()); + if (additionalInterfaceBounds!=null && additionalInterfaceBounds.length!=0) { + sb.append(":"); + for (int i = 0; i < additionalInterfaceBounds.length; i++) { + UnresolvedType iBound = additionalInterfaceBounds[i]; + sb.append(iBound.getSignature()); + } + } + return sb.toString(); + } + + /** + * @return signature for inclusion in an attribute, there must be no 'P' in it signatures + */ + public String getSignatureForAttribute() { + StringBuffer sb = new StringBuffer(); + sb.append(name); + sb.append(":"); + sb.append(((ResolvedType)upperBound).getSignatureForAttribute()); + if (additionalInterfaceBounds!=null && additionalInterfaceBounds.length!=0) { + sb.append(":"); + for (int i = 0; i < additionalInterfaceBounds.length; i++) { + ResolvedType iBound = (ResolvedType)additionalInterfaceBounds[i]; + sb.append(iBound.getSignatureForAttribute()); + } + } + return sb.toString(); + } + + + public void setRank(int rank) { + this.rank=rank; + } + + public int getRank() { + return rank; + } + + public void setDeclaringElement(TypeVariableDeclaringElement element) { + this.declaringElement = element; + if (element instanceof UnresolvedType) { + this.declaringElementKind = TYPE; + } else { + this.declaringElementKind = METHOD; + } + } + + public TypeVariableDeclaringElement getDeclaringElement() { + return declaringElement; + } + + public void setDeclaringElementKind(int kind) { + this.declaringElementKind = kind; + } + + public int getDeclaringElementKind() { +// if (declaringElementKind==UNKNOWN) throw new RuntimeException("Dont know declarer of this tvar : "+this); + return declaringElementKind; + } + + public void write(DataOutputStream s) throws IOException { + // name, upperbound, additionalInterfaceBounds, lowerbound + s.writeUTF(name); + upperBound.write(s); + if (additionalInterfaceBounds==null || additionalInterfaceBounds.length==0) { + s.writeInt(0); + } else { + s.writeInt(additionalInterfaceBounds.length); + for (int i = 0; i < additionalInterfaceBounds.length; i++) { + UnresolvedType ibound = additionalInterfaceBounds[i]; + ibound.write(s); + } + } + } + + public static TypeVariable read(VersionedDataInputStream s) throws IOException { + + //if (s.getMajorVersion()>=AjAttribute.WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ150) { + + String name = s.readUTF(); + UnresolvedType ubound = UnresolvedType.read(s); + int iboundcount = s.readInt(); + UnresolvedType[] ibounds = UnresolvedType.NONE; + if (iboundcount>0) { + ibounds = new UnresolvedType[iboundcount]; + for (int i=0; i would have signature: <1:Ljava/lang/Number>Lorg.xyz.Foo;Ljava/lang/Object; + * + * A parameterized type is a distinct type in the world with its own signature following the grammar: + * + * TypeParamsOpt ClassSig; + * + * but with the L for the class sig replaced by "P". For example List has signature + * + * Pjava/util/List; + * + * and List in the following class : class Foo { List lt; } + * + * has signature: <1:>Pjava/util/List; + * + * A typex that represents a type variable has its own unique signature, following the grammar for a FormalTypeParameter in the JVM + * spec. + * + * A generic typex has its true signature and also an erasure signature. Both of these are keys pointing to the same UnresolvedType + * in the world. For example List has signature: + * + * <1:>Ljava/util/List;Ljava/lang/Object; + * + * and the erasure signature + * + * Ljava/util/List; + * + * Generics wildcards introduce their own special signatures for type parameters. The wildcard ? has signature * The wildcard ? + * extends Foo has signature +LFoo; The wildcard ? super Foo has signature -LFoo; + */ +public class UnresolvedType implements Traceable, TypeVariableDeclaringElement { + + // common types referred to by the weaver + public static final UnresolvedType[] NONE = new UnresolvedType[0]; + public static final UnresolvedType OBJECT = forSignature("Ljava/lang/Object;"); + public static final UnresolvedType OBJECTARRAY = forSignature("[Ljava/lang/Object;"); + public static final UnresolvedType CLONEABLE = forSignature("Ljava/lang/Cloneable;"); + public static final UnresolvedType SERIALIZABLE = forSignature("Ljava/io/Serializable;"); + public static final UnresolvedType THROWABLE = forSignature("Ljava/lang/Throwable;"); + public static final UnresolvedType RUNTIME_EXCEPTION = forSignature("Ljava/lang/RuntimeException;"); + public static final UnresolvedType ERROR = forSignature("Ljava/lang/Error;"); + public static final UnresolvedType AT_INHERITED = forSignature("Ljava/lang/annotation/Inherited;"); + public static final UnresolvedType AT_RETENTION = forSignature("Ljava/lang/annotation/Retention;"); + public static final UnresolvedType ENUM = forSignature("Ljava/lang/Enum;"); + public static final UnresolvedType ANNOTATION = forSignature("Ljava/lang/annotation/Annotation;"); + public static final UnresolvedType JAVA_LANG_CLASS = forSignature("Ljava/lang/Class;"); + public static final UnresolvedType JAVA_LANG_CLASS_ARRAY = forSignature("[Ljava/lang/Class;"); + public static final UnresolvedType JAVA_LANG_STRING = forSignature("Ljava/lang/String;"); + public static final UnresolvedType JAVA_LANG_EXCEPTION = forSignature("Ljava/lang/Exception;"); + public static final UnresolvedType JAVA_LANG_REFLECT_METHOD = forSignature("Ljava/lang/reflect/Method;"); + public static final UnresolvedType JAVA_LANG_ANNOTATION = UnresolvedType.forSignature("Ljava.lang.annotation.Annotation;"); + public static final UnresolvedType SUPPRESS_AJ_WARNINGS = forSignature("Lorg/aspectj/lang/annotation/SuppressAjWarnings;"); + public static final UnresolvedType AT_TARGET = forSignature("Ljava/lang/annotation/Target;"); + public static final UnresolvedType SOMETHING = new UnresolvedType("?"); + public static final UnresolvedType[] ARRAY_WITH_JUST_OBJECT = new UnresolvedType[] { OBJECT }; + public static final UnresolvedType JOINPOINT_STATICPART = forSignature("Lorg/aspectj/lang/JoinPoint$StaticPart;"); + public static final UnresolvedType JOINPOINT_ENCLOSINGSTATICPART = forSignature("Lorg/aspectj/lang/JoinPoint$EnclosingStaticPart;"); + + // this doesn't belong here and will get moved to ResolvedType later in the refactoring + public static final String MISSING_NAME = "@missing@"; + + // OPTIMIZE I dont think you can ask something unresolved what kind of type it is, how can it always know? Push down into + // resolvedtype + // that will force references to resolvedtypes to be correct rather than relying on unresolvedtypes to answer questions + protected TypeKind typeKind = TypeKind.SIMPLE; // what kind of type am I? + + /** + * THE SIGNATURE - see the comments above for how this is defined + */ + protected String signature; + + /** + * The erasure of the signature. Contains only the Java signature of the type with all supertype, superinterface, type variable, + * and parameter information removed. + */ + protected String signatureErasure; + + /** + * Iff isParameterized(), then these are the type parameters + */ + protected UnresolvedType[] typeParameters; + + /** + * Iff isGeneric(), then these are the type variables declared on the type Iff isParameterized(), then these are the type + * variables bound as parameters in the type + */ + // OPTIMIZE should be no state in here that will damage whether equals() is correct... + protected TypeVariable[] typeVariables; + + public boolean isPrimitiveType() { + return typeKind == TypeKind.PRIMITIVE; + } + + public boolean isSimpleType() { + return typeKind == TypeKind.SIMPLE; + } + + public boolean isRawType() { + return typeKind == TypeKind.RAW; + } + + public boolean isGenericType() { + return typeKind == TypeKind.GENERIC; + } + + public boolean isParameterizedType() { + return typeKind == TypeKind.PARAMETERIZED; + } + + public boolean isTypeVariableReference() { + return typeKind == TypeKind.TYPE_VARIABLE; + } + + public boolean isGenericWildcard() { + return typeKind == TypeKind.WILDCARD; + } + + public TypeKind getTypekind() { + return typeKind; + } + + // for any reference type, we can get some extra information... + public final boolean isArray() { + return signature.length() > 0 && signature.charAt(0) == '['; + } + + /** + * Equality is checked based on the underlying signature. {@link ResolvedType} objects' equals is by reference. + */ + public boolean equals(Object other) { + if (!(other instanceof UnresolvedType)) + return false; + return signature.equals(((UnresolvedType) other).signature); + } + + /** + * Equality is checked based on the underlying signature, so the hash code of a particular type is the hash code of its + * signature string. + */ + public final int hashCode() { + return signature.hashCode(); + } + + protected UnresolvedType(String signature) { + this.signature = signature; + this.signatureErasure = signature; + } + + protected UnresolvedType(String signature, String signatureErasure) { + this.signature = signature; + this.signatureErasure = signatureErasure; + } + + // called from TypeFactory + public UnresolvedType(String signature, String signatureErasure, UnresolvedType[] typeParams) { + this.signature = signature; + this.signatureErasure = signatureErasure; + this.typeParameters = typeParams; + if (typeParams != null) + this.typeKind = TypeKind.PARAMETERIZED; + } + + // ---- Things we can do without a world + + /** + * This is the size of this type as used in JVM. + */ + public int getSize() { + return 1; + } + + public static UnresolvedType makeArray(UnresolvedType base, int dims) { + StringBuffer sig = new StringBuffer(); + for (int i = 0; i < dims; i++) + sig.append("["); + sig.append(base.getSignature()); + return UnresolvedType.forSignature(sig.toString()); + } + + /** + * NOTE: Use forSignature() if you can, it'll be cheaper ! Constructs a UnresolvedType for a java language type name. For + * example: + * + *
+ * + *
+	 *   UnresolvedType.forName("java.lang.Thread[]")
+	 *   UnresolvedType.forName("int")
+	 * 
+ * + *
+ * + * Types may equivalently be produced by this or by {@link #forSignature(String)}. + * + *
+ * + *
+	 *   UnresolvedType.forName("java.lang.Thread[]").equals(Type.forSignature("[Ljava/lang/Thread;")
+	 *   UnresolvedType.forName("int").equals(Type.forSignature("I"))
+	 * 
+ * + *
+ * + * @param name the java language type name in question. + * @return a type object representing that java language type. + */ + // OPTIMIZE change users of this to use forSignature, especially for simple cases + public static UnresolvedType forName(String name) { + return forSignature(nameToSignature(name)); + } + + /** + * Constructs a UnresolvedType for each java language type name in an incoming array. + * + * @param names an array of java language type names. + * @return an array of UnresolvedType objects. + * @see #forName(String) + */ + public static UnresolvedType[] forNames(String[] names) { + UnresolvedType[] ret = new UnresolvedType[names.length]; + for (int i = 0, len = names.length; i < len; i++) { + ret[i] = UnresolvedType.forName(names[i]); + } + return ret; + } + + public static UnresolvedType forGenericType(String name, TypeVariable[] tvbs, String genericSig) { + // TODO asc generics needs a declared sig + String sig = nameToSignature(name); + UnresolvedType ret = UnresolvedType.forSignature(sig); + ret.typeKind = TypeKind.GENERIC; + ret.typeVariables = tvbs; + ret.signatureErasure = sig; + return ret; + } + + public static UnresolvedType forGenericTypeSignature(String sig, String declaredGenericSig) { + UnresolvedType ret = UnresolvedType.forSignature(sig); + ret.typeKind = TypeKind.GENERIC; + + ClassSignature csig = new GenericSignatureParser().parseAsClassSignature(declaredGenericSig); + + GenericSignature.FormalTypeParameter[] ftps = csig.formalTypeParameters; + ret.typeVariables = new TypeVariable[ftps.length]; + for (int i = 0; i < ftps.length; i++) { + GenericSignature.FormalTypeParameter parameter = ftps[i]; + if (parameter.classBound instanceof GenericSignature.ClassTypeSignature) { + GenericSignature.ClassTypeSignature cts = (GenericSignature.ClassTypeSignature) parameter.classBound; + ret.typeVariables[i] = new TypeVariable(ftps[i].identifier, UnresolvedType.forSignature(cts.outerType.identifier + + ";")); + } else if (parameter.classBound instanceof GenericSignature.TypeVariableSignature) { + GenericSignature.TypeVariableSignature tvs = (GenericSignature.TypeVariableSignature) parameter.classBound; + UnresolvedTypeVariableReferenceType utvrt = new UnresolvedTypeVariableReferenceType(new TypeVariable( + tvs.typeVariableName)); + ret.typeVariables[i] = new TypeVariable(ftps[i].identifier, utvrt); + } else { + throw new BCException( + "UnresolvedType.forGenericTypeSignature(): Do not know how to process type variable bound of type '" + + parameter.classBound.getClass() + "'. Full signature is '" + sig + "'"); + } + } + ret.signatureErasure = sig; + ret.signature = ret.signatureErasure; + return ret; + } + + public static UnresolvedType forGenericTypeVariables(String sig, TypeVariable[] tVars) { + UnresolvedType ret = UnresolvedType.forSignature(sig); + ret.typeKind = TypeKind.GENERIC; + ret.typeVariables = tVars; + ret.signatureErasure = sig; + ret.signature = ret.signatureErasure; + return ret; + } + + public static UnresolvedType forRawTypeName(String name) { + UnresolvedType ret = UnresolvedType.forName(name); + ret.typeKind = TypeKind.RAW; + return ret; + } + + /** + * Creates a new type array with a fresh type appended to the end. + * + * @param types the left hand side of the new array + * @param end the right hand side of the new array + */ + public static UnresolvedType[] add(UnresolvedType[] types, UnresolvedType end) { + int len = types.length; + UnresolvedType[] ret = new UnresolvedType[len + 1]; + System.arraycopy(types, 0, ret, 0, len); + ret[len] = end; + return ret; + } + + /** + * Creates a new type array with a fresh type inserted at the beginning. + * + * + * @param start the left hand side of the new array + * @param types the right hand side of the new array + */ + public static UnresolvedType[] insert(UnresolvedType start, UnresolvedType[] types) { + int len = types.length; + UnresolvedType[] ret = new UnresolvedType[len + 1]; + ret[0] = start; + System.arraycopy(types, 0, ret, 1, len); + return ret; + } + + /** + * Constructs a Type for a JVM bytecode signature string. For example: + * + *
+ * + *
+	 *   UnresolvedType.forSignature("[Ljava/lang/Thread;")
+	 *   UnresolvedType.forSignature("I");
+	 * 
+ * + *
+ * + * Types may equivalently be produced by this or by {@link #forName(String)}. + * + *
+ * + *
+	 *   UnresolvedType.forName("java.lang.Thread[]").equals(Type.forSignature("[Ljava/lang/Thread;")
+	 *   UnresolvedType.forName("int").equals(Type.forSignature("I"))
+	 * 
+ * + *
+ * + * @param signature the JVM bytecode signature string for the desired type. + * @return a type object represnting that JVM bytecode signature. + */ + public static UnresolvedType forSignature(String signature) { + switch (signature.charAt(0)) { + case 'B': + return ResolvedType.BYTE; + case 'C': + return ResolvedType.CHAR; + case 'D': + return ResolvedType.DOUBLE; + case 'F': + return ResolvedType.FLOAT; + case 'I': + return ResolvedType.INT; + case 'J': + return ResolvedType.LONG; + case 'L': + return TypeFactory.createTypeFromSignature(signature); + case 'P': + return TypeFactory.createTypeFromSignature(signature); + case 'S': + return ResolvedType.SHORT; + case 'V': + return ResolvedType.VOID; + case 'Z': + return ResolvedType.BOOLEAN; + case '[': + return TypeFactory.createTypeFromSignature(signature); + case '+': + return TypeFactory.createTypeFromSignature(signature); + case '-': + return TypeFactory.createTypeFromSignature(signature); + case '?': + return TypeFactory.createTypeFromSignature(signature); + case 'T': + return TypeFactory.createTypeFromSignature(signature); + default: + throw new BCException("Bad type signature " + signature); + } + } + + /** + * Constructs a UnresolvedType for each JVM bytecode type signature in an incoming array. + * + * @param names an array of JVM bytecode type signatures + * @return an array of UnresolvedType objects. + * @see #forSignature(String) + */ + public static UnresolvedType[] forSignatures(String[] sigs) { + UnresolvedType[] ret = new UnresolvedType[sigs.length]; + for (int i = 0, len = sigs.length; i < len; i++) { + ret[i] = UnresolvedType.forSignature(sigs[i]); + } + return ret; + } + + /** + * Returns the name of this type in java language form (e.g. java.lang.Thread or boolean[]). This produces a more aesthetically + * pleasing string than {@link java.lang.Class#getName()}. + * + * @return the java language name of this type. + */ + public String getName() { + return signatureToName(signature); + } + + public String getSimpleName() { + String name = getRawName(); + int lastDot = name.lastIndexOf('.'); + if (lastDot != -1) { + name = name.substring(lastDot + 1); + } + if (isParameterizedType()) { + StringBuffer sb = new StringBuffer(name); + sb.append("<"); + for (int i = 0; i < (typeParameters.length - 1); i++) { + sb.append(typeParameters[i].getSimpleName()); + sb.append(","); + } + sb.append(typeParameters[typeParameters.length - 1].getSimpleName()); + sb.append(">"); + name = sb.toString(); + } + return name; + } + + public String getRawName() { + return signatureToName((signatureErasure == null ? signature : signatureErasure)); + } + + public String getBaseName() { + String name = getName(); + if (isParameterizedType() || isGenericType()) { + if (typeParameters == null) + return name; + else + return name.substring(0, name.indexOf("<")); + } else { + return name; + } + } + + public String getSimpleBaseName() { + String name = getBaseName(); + int lastDot = name.lastIndexOf('.'); + if (lastDot != -1) { + name = name.substring(lastDot + 1); + } + return name; + } + + /** + * Returns an array of strings representing the java langauge names of an array of types. + * + * @param types an array of UnresolvedType objects + * @return an array of Strings fo the java language names of types. + * @see #getName() + */ + public static String[] getNames(UnresolvedType[] types) { + String[] ret = new String[types.length]; + for (int i = 0, len = types.length; i < len; i++) { + ret[i] = types[i].getName(); + } + return ret; + } + + /** + * Returns the name of this type in JVM signature form. For all UnresolvedType t: + * + *
+ * + *
+	 * UnresolvedType.forSignature(t.getSignature()).equals(t)
+	 * 
+ * + *
+ * + * and for all String s where s is a lexically valid JVM type signature string: + * + *
+ * + *
+	 * UnresolvedType.forSignature(s).getSignature().equals(s)
+	 * 
+ * + *
+ * + * @return the java JVM signature string for this type. + */ + public String getSignature() { + return signature; + } + + /** + * For parameterized types, return the signature for the raw type + */ + public String getErasureSignature() { + if (signatureErasure == null) + return signature; + return signatureErasure; + } + + private boolean needsModifiableDelegate = false; + + public boolean needsModifiableDelegate() { + return needsModifiableDelegate; + } + + public void setNeedsModifiableDelegate(boolean b) { + this.needsModifiableDelegate = b; + } + + public UnresolvedType getRawType() { + return UnresolvedType.forSignature(getErasureSignature()); + } + + /** + * Returns a UnresolvedType object representing the effective outermost enclosing type for a name type. For all other types, + * this will return the type itself. + * + * The only guarantee is given in JLS 13.1 where code generated according to those rules will have type names that can be split + * apart in this way. + * + * @return the outermost enclosing UnresolvedType object or this. + */ + public UnresolvedType getOutermostType() { + if (isArray() || isPrimitiveType()) + return this; + String sig = getErasureSignature(); + int dollar = sig.indexOf('$'); + if (dollar != -1) { + return UnresolvedType.forSignature(sig.substring(0, dollar) + ';'); + } else { + return this; + } + } + + /** + * Returns a UnresolvedType object representing the component type of this array, or null if this type does not represent an + * array type. + * + * @return the component UnresolvedType object, or null. + */ + public UnresolvedType getComponentType() { + if (isArray()) { + return forSignature(signature.substring(1)); + } else { + return null; + } + } + + /** + * Returns a java language string representation of this type. + */ + public String toString() { + return getName(); // + " - " + getKind(); + } + + public String toDebugString() { + return getName(); + } + + // ---- requires worlds + + /** + * Returns a resolved version of this type according to a particular world. + * + * @param world thie {@link World} within which to resolve. + * @return a resolved type representing this type in the appropriate world. + */ + public ResolvedType resolve(World world) { + return world.resolve(this); + } + + // ---- helpers + + private static String signatureToName(String signature) { + switch (signature.charAt(0)) { + case 'B': + return "byte"; + case 'C': + return "char"; + case 'D': + return "double"; + case 'F': + return "float"; + case 'I': + return "int"; + case 'J': + return "long"; + case 'L': + String name = signature.substring(1, signature.length() - 1).replace('/', '.'); + return name; + case 'T': + StringBuffer nameBuff2 = new StringBuffer(); + int colon = signature.indexOf(";"); + String tvarName = signature.substring(1, colon); + nameBuff2.append(tvarName); + return nameBuff2.toString(); + case 'P': // it's one of our parameterized type sigs + StringBuffer nameBuff = new StringBuffer(); + // signature for parameterized types is e.g. + // List -> Ljava/util/List; + // Map> -> Ljava/util/Map;>; + int paramNestLevel = 0; + for (int i = 1; i < signature.length(); i++) { + char c = signature.charAt(i); + switch (c) { + case '/': + nameBuff.append('.'); + break; + case '<': + nameBuff.append("<"); + paramNestLevel++; + StringBuffer innerBuff = new StringBuffer(); + while (paramNestLevel > 0) { + c = signature.charAt(++i); + if (c == '<') + paramNestLevel++; + if (c == '>') + paramNestLevel--; + if (paramNestLevel > 0) + innerBuff.append(c); + if (c == ';' && paramNestLevel == 1) { + nameBuff.append(signatureToName(innerBuff.toString())); + if (signature.charAt(i + 1) != '>') + nameBuff.append(','); + innerBuff = new StringBuffer(); + } + } + nameBuff.append(">"); + break; + case ';': + break; + default: + nameBuff.append(c); + } + } + return nameBuff.toString(); + case 'S': + return "short"; + case 'V': + return "void"; + case 'Z': + return "boolean"; + case '[': + return signatureToName(signature.substring(1, signature.length())) + "[]"; + // case '<': + // // its a generic! + // if (signature.charAt(1)=='>') return signatureToName(signature.substring(2)); + case '+': + return "? extends " + signatureToName(signature.substring(1, signature.length())); + case '-': + return "? super " + signatureToName(signature.substring(1, signature.length())); + case '*': + return "?"; + default: + throw new BCException("Bad type signature: " + signature); + } + } + + private static String nameToSignature(String name) { + if (name.equals("byte")) + return "B"; + if (name.equals("char")) + return "C"; + if (name.equals("double")) + return "D"; + if (name.equals("float")) + return "F"; + if (name.equals("int")) + return "I"; + if (name.equals("long")) + return "J"; + if (name.equals("short")) + return "S"; + if (name.equals("boolean")) + return "Z"; + if (name.equals("void")) + return "V"; + if (name.equals("?")) + return name; + if (name.endsWith("[]")) + return "[" + nameToSignature(name.substring(0, name.length() - 2)); + if (name.length() != 0) { + // lots more tests could be made here... + + // check if someone is calling us with something that is a signature already + if (name.charAt(0) == '[') { + throw new BCException("Do not call nameToSignature with something that looks like a signature (descriptor): '" + + name + "'"); + } + + if (name.indexOf("<") == -1) { + // not parameterised + return "L" + name.replace('.', '/') + ";"; + } else { + StringBuffer nameBuff = new StringBuffer(); + int nestLevel = 0; + nameBuff.append("P"); + for (int i = 0; i < name.length(); i++) { + char c = name.charAt(i); + switch (c) { + case '.': + nameBuff.append('/'); + break; + case '<': + nameBuff.append("<"); + nestLevel++; + StringBuffer innerBuff = new StringBuffer(); + while (nestLevel > 0) { + c = name.charAt(++i); + if (c == '<') + nestLevel++; + if (c == '>') + nestLevel--; + if (c == ',' && nestLevel == 1) { + nameBuff.append(nameToSignature(innerBuff.toString())); + innerBuff = new StringBuffer(); + } else { + if (nestLevel > 0) + innerBuff.append(c); + } + } + nameBuff.append(nameToSignature(innerBuff.toString())); + nameBuff.append('>'); + break; + case '>': + throw new IllegalStateException("Should by matched by <"); + case ',': + throw new IllegalStateException("Should only happen inside <...>"); + default: + nameBuff.append(c); + } + } + nameBuff.append(";"); + return nameBuff.toString(); + } + } else + throw new BCException("Bad type name: " + name); + } + + public void write(DataOutputStream s) throws IOException { + s.writeUTF(getSignature()); + } + + public static UnresolvedType read(DataInputStream s) throws IOException { + String sig = s.readUTF(); + if (sig.equals(MISSING_NAME)) { + return ResolvedType.MISSING; + } else { + UnresolvedType ret = UnresolvedType.forSignature(sig); + return ret; + } + } + + public static void writeArray(UnresolvedType[] types, DataOutputStream s) throws IOException { + int len = types.length; + s.writeShort(len); + for (int i = 0; i < len; i++) { + types[i].write(s); + } + } + + public static UnresolvedType[] readArray(DataInputStream s) throws IOException { + int len = s.readShort(); + if (len == 0) + return UnresolvedType.NONE; + UnresolvedType[] types = new UnresolvedType[len]; + for (int i = 0; i < len; i++) { + types[i] = UnresolvedType.read(s); + } + return types; + } + + public String getNameAsIdentifier() { + return getName().replace('.', '_'); + } + + public String getPackageNameAsIdentifier() { + String name = getName(); + int index = name.lastIndexOf('.'); + if (index == -1) { + return ""; + } else { + return name.substring(0, index).replace('.', '_'); + } + } + + public String getPackageName() { + String name = getName(); + if (name.indexOf("<") != -1) { + name = name.substring(0, name.indexOf("<")); + } + int index = name.lastIndexOf('.'); + if (index == -1) { + return ""; + } else { + return name.substring(0, index); + } + } + + public UnresolvedType[] getTypeParameters() { + return typeParameters == null ? UnresolvedType.NONE : typeParameters; + } + + /** + * Doesn't include the package + */ + public String getClassName() { + String name = getName(); + int index = name.lastIndexOf('.'); + if (index == -1) { + return name; + } else { + return name.substring(index + 1); + } + } + + public TypeVariable[] getTypeVariables() { + return typeVariables; + } + + public static class TypeKind { + // Note: It is not sufficient to say that a parameterized type with no type parameters in fact + // represents a raw type - a parameterized type with no type parameters can represent + // an inner type of a parameterized type that specifies no type parameters of its own. + public final static TypeKind PRIMITIVE = new TypeKind("primitive"); + public final static TypeKind SIMPLE = new TypeKind("simple"); // a type with NO type parameters/vars + public final static TypeKind RAW = new TypeKind("raw"); // the erasure of a generic type + public final static TypeKind GENERIC = new TypeKind("generic"); // a generic type + public final static TypeKind PARAMETERIZED = new TypeKind("parameterized"); // a parameterized type + public final static TypeKind TYPE_VARIABLE = new TypeKind("type_variable"); // a type variable + public final static TypeKind WILDCARD = new TypeKind("wildcard"); // a generic wildcard type + + public String toString() { + return type; + } + + private TypeKind(String type) { + this.type = type; + } + + private final String type; + } + + public TypeVariable getTypeVariableNamed(String name) { + TypeVariable[] vars = getTypeVariables(); + if (vars == null || vars.length == 0) + return null; + for (int i = 0; i < vars.length; i++) { + TypeVariable aVar = vars[i]; + if (aVar.getName().equals(name)) + return aVar; + } + return null; + } + + public String toTraceString() { + return getClass().getName() + "[" + getName() + "]"; + } + + /** + * Return a version of this parameterized type in which any type parameters that are type variable references are replaced by + * their matching type variable binding. + */ + // OPTIMIZE methods like this just allow callers to be lazy and not ensure they are working with the right (resolved) subtype + public UnresolvedType parameterize(Map typeBindings) { + throw new UnsupportedOperationException("unable to parameterize unresolved type: " + signature); + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/UnresolvedTypeVariableReferenceType.java b/org.aspectj.matcher/src/org/aspectj/weaver/UnresolvedTypeVariableReferenceType.java new file mode 100644 index 000000000..d6486b532 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/UnresolvedTypeVariableReferenceType.java @@ -0,0 +1,102 @@ +/* ******************************************************************* + * 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.io.DataOutputStream; +import java.io.IOException; + +/** + * @author colyer + * Represents a type variable encountered in the Eclipse Source world, + * which when resolved will turn into a TypeVariableReferenceType + */ +public class UnresolvedTypeVariableReferenceType extends UnresolvedType implements TypeVariableReference { + + private TypeVariable typeVariable; + + // constructor used as place-holder when dealing with circular refs such as Enum + public UnresolvedTypeVariableReferenceType() { + super("Ljava/lang/Object;"); + } + + public UnresolvedTypeVariableReferenceType(TypeVariable aTypeVariable) { + super(aTypeVariable.getFirstBound().getSignature()); + this.typeVariable = aTypeVariable; + } + + // only used when resolving circular refs... + public void setTypeVariable(TypeVariable aTypeVariable) { + this.signature = "T" + aTypeVariable.getName() + ";"; //aTypeVariable.getUpperBound().getSignature(); + this.typeVariable = aTypeVariable; + this.typeKind=TypeKind.TYPE_VARIABLE; + } + + public ResolvedType resolve(World world) { + TypeVariableDeclaringElement typeVariableScope = world.getTypeVariableLookupScope(); + TypeVariable resolvedTypeVariable = null; + TypeVariableReferenceType tvrt = null; + if (typeVariableScope == null) { + // throw new BCException("There is no scope in which to lookup type variables!"); + // FIXME asc correct thing to do is go bang, but to limp along, lets cope with the scope missing + resolvedTypeVariable = typeVariable.resolve(world); + tvrt = new TypeVariableReferenceType(resolvedTypeVariable,world); + } else { + boolean foundOK = false; + resolvedTypeVariable = typeVariableScope.getTypeVariableNamed(typeVariable.getName()); + // FIXME asc remove this when the shared type var stuff is sorted + if (resolvedTypeVariable == null) { + resolvedTypeVariable = typeVariable.resolve(world); + } else { + foundOK = true; + } + tvrt = new TypeVariableReferenceType(resolvedTypeVariable,world); + tvrt.fixedUp = foundOK; + } + + return tvrt; + } + + + public boolean isTypeVariableReference() { + return true; + } + + public TypeVariable getTypeVariable() { + return typeVariable; + } + +// public String getName() { +// if (typeVariable == null) return ""; +// return typeVariable.getDisplayName(); +// } + + public String toString() { + if (typeVariable == null) { + return ""; + } else { + return "T" + typeVariable.getName() + ";"; + } + } + + public String toDebugString() { + return typeVariable.getName(); + } + + public void write(DataOutputStream s) throws IOException { + super.write(s); + } + + public String getErasureSignature() { + return typeVariable.getFirstBound().getSignature(); + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/Utils.java b/org.aspectj.matcher/src/org/aspectj/weaver/Utils.java new file mode 100644 index 000000000..8053af0c0 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/Utils.java @@ -0,0 +1,43 @@ +/* ******************************************************************* + * 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 + * + * ******************************************************************/ +package org.aspectj.weaver; + +/** + * + * @author Andy Clement + */ +public class Utils { + + /** + * Check if the annotations contain a SuppressAjWarnings annotation and if that annotation specifies that the given lint message + * (identified by its key) should be ignored. + * + */ + public static boolean isSuppressing(AnnotationAJ[] anns, String lintkey) { + if (anns == null) { + return false; + } + // Go through the annotation types on the advice + for (int i = 0; i < anns.length; i++) { + if (UnresolvedType.SUPPRESS_AJ_WARNINGS.getSignature().equals(anns[i].getTypeSignature())) { + // Two possibilities: + // 1. there are no values specified (i.e. @SuppressAjWarnings) + // 2. there are values specified (i.e. @SuppressAjWarnings("A") or @SuppressAjWarnings({"A","B"}) + String value = anns[i].getStringFormOfValue("value"); + // Slightly lazy, just doing a string indexof + if (value == null || value.indexOf(lintkey) != -1) { + return true; + } + } + } + return false; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/VersionedDataInputStream.java b/org.aspectj.matcher/src/org/aspectj/weaver/VersionedDataInputStream.java new file mode 100644 index 000000000..f31723b67 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/VersionedDataInputStream.java @@ -0,0 +1,32 @@ +/* ******************************************************************* + * 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; + +import java.io.DataInputStream; +import java.io.InputStream; + +import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; + +/** + * Lightweight subclass of DataInputStream that knows what version of the weaver was used to construct the data in it. + */ +public class VersionedDataInputStream extends DataInputStream { + private WeaverVersionInfo version = new WeaverVersionInfo();// assume we are the latest unless something tells us otherwise... + public VersionedDataInputStream(InputStream is) { super(is); } + + public int getMajorVersion() { return version.getMajorVersion(); } + public int getMinorVersion() { return version.getMinorVersion(); } + public long getBuildstamp() { return version.getBuildstamp(); } + + public void setVersion(WeaverVersionInfo version) { this.version = version; } +} \ No newline at end of file diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/WeakClassLoaderReference.java b/org.aspectj.matcher/src/org/aspectj/weaver/WeakClassLoaderReference.java new file mode 100644 index 000000000..bcf650286 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/WeakClassLoaderReference.java @@ -0,0 +1,63 @@ +/* ******************************************************************* + * 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; + +import java.lang.ref.WeakReference; + +/** + * 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 WeakClassLoaderReference { + + protected final int hashcode; + + private final WeakReference loaderRef; + + public WeakClassLoaderReference(ClassLoader loader) { + loaderRef = new WeakReference(loader); + hashcode = loader.hashCode() * 37; + } + + public ClassLoader getClassLoader() { + ClassLoader instance = (ClassLoader) loaderRef.get(); + // Assert instance!=null + return instance; + } + + public boolean equals(Object obj) { + if (!(obj instanceof WeakClassLoaderReference)) + return false; + WeakClassLoaderReference other = (WeakClassLoaderReference) obj; + return (other.hashcode == hashcode); + } + + public int hashCode() { + return hashcode; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/WeaverMessages.java b/org.aspectj.matcher/src/org/aspectj/weaver/WeaverMessages.java new file mode 100644 index 000000000..5e86794a4 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/WeaverMessages.java @@ -0,0 +1,210 @@ +/******************************************************************************* + * 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.text.MessageFormat; +import java.util.ResourceBundle; + + +public class WeaverMessages { + + private static ResourceBundle bundle = ResourceBundle.getBundle("org.aspectj.weaver.weaver-messages"); + + public static final String ARGS_IN_DECLARE = "argsInDeclare"; + public static final String CFLOW_IN_DECLARE = "cflowInDeclare"; + public static final String IF_IN_DECLARE = "ifInDeclare"; + public static final String THIS_OR_TARGET_IN_DECLARE = "thisOrTargetInDeclare"; + public static final String ABSTRACT_POINTCUT = "abstractPointcut"; + public static final String POINCUT_NOT_CONCRETE = "abstractPointcutNotMadeConcrete"; + public static final String POINTCUT_NOT_VISIBLE = "pointcutNotVisible"; + public static final String CONFLICTING_INHERITED_POINTCUTS = "conflictingInheritedPointcuts"; + public static final String CIRCULAR_POINTCUT = "circularPointcutDeclaration"; + public static final String CANT_FIND_POINTCUT = "cantFindPointcut"; + public static final String EXACT_TYPE_PATTERN_REQD = "exactTypePatternRequired"; + public static final String CANT_BIND_TYPE = "cantBindType"; + public static final String WILDCARD_NOT_ALLOWED = "wildcardTypePatternNotAllowed"; + public static final String FIELDS_CANT_HAVE_VOID_TYPE = "fieldCantBeVoid"; + public static final String NO_NEWARRAY_JOINPOINTS_BY_DEFAULT = "noNewArrayJoinpointsByDefault"; + public static final String UNSUPPORTED_POINTCUT_PRIMITIVE = "unsupportedPointcutPrimitive"; + public static final String MISSING_TYPE_PREVENTS_MATCH = "missingTypePreventsMatch"; + + public static final String DECP_OBJECT = "decpObject"; + public static final String CANT_EXTEND_SELF="cantExtendSelf"; + public static final String INTERFACE_CANT_EXTEND_CLASS="interfaceExtendClass"; + public static final String DECP_HIERARCHY_ERROR = "decpHierarchy"; + + public static final String MULTIPLE_MATCHES_IN_PRECEDENCE = "multipleMatchesInPrecedence"; + public static final String TWO_STARS_IN_PRECEDENCE = "circularityInPrecedenceStar"; + public static final String CLASSES_IN_PRECEDENCE = "nonAspectTypesInPrecedence"; + public static final String TWO_PATTERN_MATCHES_IN_PRECEDENCE = "circularityInPrecedenceTwo"; + + public static final String NOT_THROWABLE = "notThrowable"; + + public static final String ITD_CONS_ON_ASPECT = "itdConsOnAspect"; + public static final String ITD_RETURN_TYPE_MISMATCH = "returnTypeMismatch"; + public static final String ITD_PARAM_TYPE_MISMATCH = "paramTypeMismatch"; + public static final String ITD_VISIBILITY_REDUCTION = "visibilityReduction"; + public static final String ITD_DOESNT_THROW = "doesntThrow"; + public static final String ITD_OVERRIDDEN_STATIC = "overriddenStatic"; + public static final String ITD_OVERIDDING_STATIC = "overridingStatic"; + public static final String ITD_CONFLICT = "itdConflict"; + public static final String ITD_MEMBER_CONFLICT = "itdMemberConflict"; + public static final String ITD_NON_EXPOSED_IMPLEMENTOR = "itdNonExposedImplementor"; + public static final String ITD_ABSTRACT_MUST_BE_PUBLIC_ON_INTERFACE = "itdAbstractMustBePublicOnInterface"; + public static final String CANT_OVERRIDE_FINAL_MEMBER = "cantOverrideFinalMember"; + + public static final String NON_VOID_RETURN = "nonVoidReturn"; + public static final String INCOMPATIBLE_RETURN_TYPE="incompatibleReturnType"; + public static final String CANT_THROW_CHECKED = "cantThrowChecked"; + public static final String CIRCULAR_DEPENDENCY = "circularDependency"; + + public static final String MISSING_PER_CLAUSE = "missingPerClause"; + public static final String WRONG_PER_CLAUSE = "wrongPerClause"; + + public static final String ALREADY_WOVEN = "alreadyWoven"; + public static final String REWEAVABLE_MODE = "reweavableMode"; + public static final String PROCESSING_REWEAVABLE = "processingReweavable"; + public static final String MISSING_REWEAVABLE_TYPE = "missingReweavableType"; + public static final String VERIFIED_REWEAVABLE_TYPE = "verifiedReweavableType"; + public static final String ASPECT_NEEDED = "aspectNeeded"; + public static final String REWEAVABLE_ASPECT_NOT_REGISTERED = "reweavableAspectNotRegistered"; + + public static final String CANT_FIND_TYPE = "cantFindType"; + public static final String CANT_FIND_CORE_TYPE = "cantFindCoreType"; + public static final String CANT_FIND_TYPE_WITHINPCD = "cantFindTypeWithinpcd"; + public static final String CANT_FIND_TYPE_DURING_AROUND_WEAVE = "cftDuringAroundWeave"; + public static final String CANT_FIND_TYPE_DURING_AROUND_WEAVE_PREINIT = "cftDuringAroundWeavePreinit"; + public static final String CANT_FIND_TYPE_EXCEPTION_TYPE = "cftExceptionType"; + public static final String CANT_FIND_TYPE_ARG_TYPE = "cftArgType"; + public static final String CANT_FIND_PARENT_TYPE = "cantFindParentType"; + public static final String CANT_FIND_PARENT_TYPE_NO_SUB = "cantFindParentTypeNoSub"; + public static final String CANT_FIND_TYPE_FIELDS = "cantFindTypeFields"; + public static final String CANT_FIND_TYPE_SUPERCLASS = "cantFindTypeSuperclass"; + public static final String CANT_FIND_TYPE_INTERFACES = "cantFindTypeInterfaces"; + public static final String CANT_FIND_TYPE_METHODS = "cantFindTypeMethods"; + public static final String CANT_FIND_TYPE_POINTCUTS = "cantFindTypePointcuts"; + public static final String CANT_FIND_TYPE_MODIFIERS = "cantFindTypeModifiers"; + public static final String CANT_FIND_TYPE_ANNOTATION = "cantFindTypeAnnotation"; + public static final String CANT_FIND_TYPE_ASSIGNABLE = "cantFindTypeAssignable"; + public static final String CANT_FIND_TYPE_COERCEABLE = "cantFindTypeCoerceable"; + public static final String CANT_FIND_TYPE_JOINPOINT = "cantFindTypeJoinPoint"; + public static final String CANT_FIND_TYPE_INTERFACE_METHODS = "cantFindTypeInterfaceMethods"; + + public static final String DECP_BINARY_LIMITATION = "decpBinaryLimitation"; + public static final String OVERWRITE_JSR45 = "overwriteJSR45"; + public static final String IF_IN_PERCLAUSE = "ifInPerClause"; + public static final String IF_LEXICALLY_IN_CFLOW = "ifLexicallyInCflow"; + public static final String ONLY_BEFORE_ON_HANDLER = "onlyBeforeOnHandler"; + public static final String NO_AROUND_ON_SYNCHRONIZATION = "noAroundOnSynchronization"; + public static final String AROUND_ON_PREINIT = "aroundOnPreInit"; + public static final String AROUND_ON_INIT = "aroundOnInit"; + public static final String AROUND_ON_INTERFACE_STATICINIT = "aroundOnInterfaceStaticInit"; + + public static final String PROBLEM_GENERATING_METHOD = "problemGeneratingMethod"; + public static final String CLASS_TOO_BIG = "classTooBig"; + + public static final String ZIPFILE_ENTRY_MISSING = "zipfileEntryMissing"; + public static final String ZIPFILE_ENTRY_INVALID = "zipfileEntryInvalid"; + public static final String DIRECTORY_ENTRY_MISSING = "directoryEntryMissing"; + public static final String OUTJAR_IN_INPUT_PATH = "outjarInInputPath"; + + + public static final String XLINT_LOAD_ERROR = "problemLoadingXLint"; + public static final String XLINTDEFAULT_LOAD_ERROR = "unableToLoadXLintDefault"; + public static final String XLINTDEFAULT_LOAD_PROBLEM = "errorLoadingXLintDefault"; + public static final String XLINT_KEY_ERROR = "invalidXLintKey"; + public static final String XLINT_VALUE_ERROR = "invalidXLintMessageKind"; + + public static final String UNBOUND_FORMAL = "unboundFormalInPC"; + public static final String AMBIGUOUS_BINDING = "ambiguousBindingInPC"; + public static final String AMBIGUOUS_BINDING_IN_OR = "ambiguousBindingInOrPC"; + public static final String NEGATION_DOESNT_ALLOW_BINDING = "negationDoesntAllowBinding"; + + // Java5 messages + public static final String ITDC_ON_ENUM_NOT_ALLOWED = "itdcOnEnumNotAllowed"; + public static final String ITDM_ON_ENUM_NOT_ALLOWED = "itdmOnEnumNotAllowed"; + public static final String ITDF_ON_ENUM_NOT_ALLOWED = "itdfOnEnumNotAllowed"; + public static final String CANT_DECP_ON_ENUM_TO_IMPL_INTERFACE = "cantDecpOnEnumToImplInterface"; + public static final String CANT_DECP_ON_ENUM_TO_EXTEND_CLASS = "cantDecpOnEnumToExtendClass"; + public static final String CANT_DECP_TO_MAKE_ENUM_SUPERTYPE = "cantDecpToMakeEnumSupertype"; + public static final String ITDC_ON_ANNOTATION_NOT_ALLOWED = "itdcOnAnnotationNotAllowed"; + public static final String ITDM_ON_ANNOTATION_NOT_ALLOWED = "itdmOnAnnotationNotAllowed"; + public static final String ITDF_ON_ANNOTATION_NOT_ALLOWED = "itdfOnAnnotationNotAllowed"; + public static final String CANT_DECP_ON_ANNOTATION_TO_IMPL_INTERFACE = "cantDecpOnAnnotationToImplInterface"; + public static final String CANT_DECP_ON_ANNOTATION_TO_EXTEND_CLASS = "cantDecpOnAnnotationToExtendClass"; + public static final String CANT_DECP_TO_MAKE_ANNOTATION_SUPERTYPE = "cantDecpToMakeAnnotationSupertype"; + public static final String REFERENCE_TO_NON_ANNOTATION_TYPE = "referenceToNonAnnotationType"; + public static final String BINDING_NON_RUNTIME_RETENTION_ANNOTATION = "bindingNonRuntimeRetentionAnnotation"; + + public static final String INCORRECT_TARGET_FOR_DECLARE_ANNOTATION = "incorrectTargetForDeclareAnnotation"; + public static final String NO_MATCH_BECAUSE_SOURCE_RETENTION = "noMatchBecauseSourceRetention"; + + // Annotation Value messages + public static final String INVALID_ANNOTATION_VALUE = "invalidAnnotationValue"; + public static final String UNKNOWN_ANNOTATION_VALUE = "unknownAnnotationValue"; + + // < Java5 messages + public static final String ATANNOTATION_ONLY_SUPPORTED_AT_JAVA5_LEVEL = "atannotationNeedsJava5"; + public static final String ATWITHIN_ONLY_SUPPORTED_AT_JAVA5_LEVEL = "atwithinNeedsJava5"; + public static final String ATWITHINCODE_ONLY_SUPPORTED_AT_JAVA5_LEVEL = "atwithincodeNeedsJava5"; + public static final String ATTHIS_ONLY_SUPPORTED_AT_JAVA5_LEVEL = "atthisNeedsJava5"; + public static final String ATTARGET_ONLY_SUPPORTED_AT_JAVA5_LEVEL = "attargetNeedsJava5"; + public static final String ATARGS_ONLY_SUPPORTED_AT_JAVA5_LEVEL = "atargsNeedsJava5"; + public static final String DECLARE_ATTYPE_ONLY_SUPPORTED_AT_JAVA5_LEVEL = "declareAtTypeNeedsJava5"; + public static final String DECLARE_ATMETHOD_ONLY_SUPPORTED_AT_JAVA5_LEVEL = "declareAtMethodNeedsJava5"; + public static final String DECLARE_ATFIELD_ONLY_SUPPORTED_AT_JAVA5_LEVEL = "declareAtFieldNeedsJava5"; + public static final String DECLARE_ATCONS_ONLY_SUPPORTED_AT_JAVA5_LEVEL = "declareAtConsNeedsJava5"; + public static final String ANNOTATIONS_NEED_JAVA5 = "annotationsRequireJava5"; + + // Generics + public static final String CANT_DECP_MULTIPLE_PARAMETERIZATIONS="cantDecpMultipleParameterizations"; + public static final String HANDLER_PCD_DOESNT_SUPPORT_PARAMETERS="noParameterizedTypePatternInHandler"; + public static final String INCORRECT_NUMBER_OF_TYPE_ARGUMENTS = "incorrectNumberOfTypeArguments"; + public static final String VIOLATES_TYPE_VARIABLE_BOUNDS = "violatesTypeVariableBounds"; + public static final String NO_STATIC_INIT_JPS_FOR_PARAMETERIZED_TYPES = "noStaticInitJPsForParameterizedTypes"; + public static final String NOT_A_GENERIC_TYPE="notAGenericType"; + public static final String WITHIN_PCD_DOESNT_SUPPORT_PARAMETERS="noParameterizedTypePatternInWithin"; + public static final String THIS_AND_TARGET_DONT_SUPPORT_PARAMETERS="noParameterizedTypesInThisAndTarget"; + public static final String GET_AND_SET_DONT_SUPPORT_DEC_TYPE_PARAMETERS="noParameterizedTypesInGetAndSet"; + public static final String NO_INIT_JPS_FOR_PARAMETERIZED_TYPES = "noInitJPsForParameterizedTypes"; + public static final String NO_GENERIC_THROWABLES = "noGenericThrowables"; + public static final String WITHINCODE_DOESNT_SUPPORT_PARAMETERIZED_DECLARING_TYPES="noParameterizedDeclaringTypesWithinCode"; + public static final String EXECUTION_DOESNT_SUPPORT_PARAMETERIZED_DECLARING_TYPES="noParameterizedDeclaringTypesInExecution"; + public static final String CALL_DOESNT_SUPPORT_PARAMETERIZED_DECLARING_TYPES="noParameterizedDeclaringTypesInCall"; + public static final String CANT_REFERENCE_POINTCUT_IN_RAW_TYPE="noRawTypePointcutReferences"; + + public static final String HAS_MEMBER_NOT_ENABLED="hasMemberNotEnabled"; + + // @AspectJ + public static final String RETURNING_FORMAL_NOT_DECLARED_IN_ADVICE = "returningFormalNotDeclaredInAdvice"; + public static final String THROWN_FORMAL_NOT_DECLARED_IN_ADVICE = "thrownFormalNotDeclaredInAdvice"; + + public static String format(String key) { + return bundle.getString(key); + } + + public static String format(String key, Object insert) { + return MessageFormat.format(bundle.getString(key),new Object[] {insert}); + } + + public static String format(String key, Object insert1, Object insert2) { + return MessageFormat.format(bundle.getString(key),new Object[] {insert1,insert2}); + } + + public static String format(String key, Object insert1, Object insert2, Object insert3) { + return MessageFormat.format(bundle.getString(key),new Object[] {insert1, insert2, insert3}); + } + + public static String format(String key, Object insert1, Object insert2, Object insert3, Object insert4) { + return MessageFormat.format(bundle.getString(key),new Object[] {insert1, insert2, insert3, insert4}); + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/WeaverStateInfo.java b/org.aspectj.matcher/src/org/aspectj/weaver/WeaverStateInfo.java new file mode 100644 index 000000000..c1cca4567 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/WeaverStateInfo.java @@ -0,0 +1,476 @@ +/* ******************************************************************* + * 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.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import org.aspectj.bridge.IMessage; + +/** + * WeaverStateInfo represents how a type was processed. It is used by the weaver to determine how a type was previously treated and + * whether reweaving is allowed. The format in the data stream is: + * + * Byte: Kind. UNTOUCHED|WOVEN|EXTENDED - If extended it can have two extra bits set 'REWEAVABLE' and 'REWEAVABLE_COMPRESSION_BIT' + * Short: typeMungerCount - how many type mungers have affected this type : The type mungers + * themselves If we are reweavable then we also have: Short: Number of aspects that touched this type in some way when it was + * previously woven The fully qualified name of each type Int: Length of class file data (i.e. the unwovenclassfile) + * Byte[]: The class file data, compressed if REWEAVABLE_COMPRESSION_BIT set. + */ + +public class WeaverStateInfo { + private List/* Entry */typeMungers; + private boolean oldStyle; + + private boolean reweavable; + private boolean reweavableCompressedMode; // If true, unwovenClassFile is uncompressed on read + private boolean reweavableDiffMode; // if true, unwovenClassFile is written and read as a diff + + private Set /* String */aspectsAffectingType; // These must exist in the world for reweaving to be valid + private byte[] unwovenClassFile; // Original 'untouched' class file + private static boolean reweavableDefault = true; // ajh02: changed from false; + private static boolean reweavableCompressedModeDefault = false; + private static boolean reweavableDiffModeDefault = true; + + // when serializing the WeaverStateInfo we come to adding the reweavable data, + // we'd like to add a diff of the unwovenClassFile and the wovenClassFile, + // but we don't have the wovenClassFile yet as we're still in the process of making it. + // so we put this key there instead as a stub. + // Then when the wovenClassFile has been made, replaceKeyWithDiff is called. + private static byte[] key = { -51, 34, 105, 56, -34, 65, 45, 78, -26, 125, 114, 97, 98, 1, -1, -42 }; + private boolean unwovenClassFileIsADiff = false; + + private WeaverStateInfo() { + // this(new ArrayList(), false,reweavableDefault,reweavableCompressedModeDefault,reweavableDiffModeDefault); + } + + public WeaverStateInfo(boolean reweavable) { + this(new ArrayList(), false, reweavable, reweavableCompressedModeDefault, reweavableDiffModeDefault); + } + + private WeaverStateInfo(List typeMungers, boolean oldStyle, boolean reweavableMode, boolean reweavableCompressedMode, + boolean reweavableDiffMode) { + this.typeMungers = typeMungers; + this.oldStyle = oldStyle; + this.reweavable = reweavableMode; + this.reweavableCompressedMode = reweavableCompressedMode; + this.reweavableDiffMode = reweavableMode ? reweavableDiffMode : false; + this.aspectsAffectingType = new HashSet(); + this.unwovenClassFile = null; + } + + public static void setReweavableModeDefaults(boolean mode, boolean compress, boolean diff) { + reweavableDefault = mode; + reweavableCompressedModeDefault = compress; + reweavableDiffModeDefault = diff; + } + + private static final int UNTOUCHED = 0, WOVEN = 2, EXTENDED = 3; + + // Use 'bits' for these capabilities - only valid in EXTENDED mode + private static final byte REWEAVABLE_BIT = 1 << 4; + private static final byte REWEAVABLE_COMPRESSION_BIT = 1 << 5; + private static final byte REWEAVABLE_DIFF_BIT = 1 << 6; + + /** See comments on write() */ + public static final WeaverStateInfo read(VersionedDataInputStream s, ISourceContext context) throws IOException { + byte b = s.readByte(); + + boolean isReweavable = ((b & REWEAVABLE_BIT) != 0); + if (isReweavable) { + b = (byte) (b - REWEAVABLE_BIT); + } + + boolean isReweavableCompressed = ((b & REWEAVABLE_COMPRESSION_BIT) != 0); + if (isReweavableCompressed) { + b = (byte) (b - REWEAVABLE_COMPRESSION_BIT); + } + + boolean isReweavableDiff = ((b & REWEAVABLE_DIFF_BIT) != 0); + if (isReweavableDiff) { + b = (byte) (b - REWEAVABLE_DIFF_BIT); + } + + switch (b) { + case UNTOUCHED: + throw new RuntimeException("unexpected UNWOVEN"); + case WOVEN: + return new WeaverStateInfo(Collections.EMPTY_LIST, true, isReweavable, isReweavableCompressed, isReweavableDiff); + case EXTENDED: + int n = s.readShort(); + List l = new ArrayList(); + for (int i = 0; i < n; i++) { + UnresolvedType aspectType = UnresolvedType.read(s); + ResolvedTypeMunger typeMunger = ResolvedTypeMunger.read(s, context); + l.add(new Entry(aspectType, typeMunger)); + } + WeaverStateInfo wsi = new WeaverStateInfo(l, false, isReweavable, isReweavableCompressed, isReweavableDiff); + readAnyReweavableData(wsi, s); + return wsi; + } + throw new RuntimeException("bad WeaverState.Kind: " + b + ". File was :" + + (context == null ? "unknown" : context.makeSourceLocation(0, 0).toString())); + } + + private static class Entry { + public UnresolvedType aspectType; + public ResolvedTypeMunger typeMunger; + + public Entry(UnresolvedType aspectType, ResolvedTypeMunger typeMunger) { + this.aspectType = aspectType; + this.typeMunger = typeMunger; + } + + public String toString() { + return "<" + aspectType + ", " + typeMunger + ">"; + } + } + + /** + * Serialize the WeaverStateInfo. Various bits are set within the 'kind' flag to indicate the structure of the attribute. In + * reweavable diff mode a 'marker' is inserted at the start of the attribute to indicate where the final calculated diff should + * be inserted. When the key is replaced with the diff, the 'kind' byte moves to the front of the attribute - thats why in the + * read logic you'll see it expecting the kind as the first byte. + */ + public void write(DataOutputStream s) throws IOException { + if (oldStyle || reweavableCompressedMode) { + throw new RuntimeException("shouldn't be writing this"); + } + + byte weaverStateInfoKind = EXTENDED; + if (reweavable) + weaverStateInfoKind |= REWEAVABLE_BIT; + + if (reweavableDiffMode) { + s.write(key); // put key in so we can replace it with the diff later + weaverStateInfoKind |= REWEAVABLE_DIFF_BIT; + } + + s.writeByte(weaverStateInfoKind); + + int n = typeMungers.size(); + s.writeShort(n); + for (int i = 0; i < n; i++) { + Entry e = (Entry) typeMungers.get(i); + e.aspectType.write(s); + e.typeMunger.write(s); + } + writeAnyReweavableData(this, s); + } + + public void addConcreteMunger(ConcreteTypeMunger munger) { + typeMungers.add(new Entry(munger.getAspectType(), munger.getMunger())); + } + + public String toString() { + return "WeaverStateInfo(" + typeMungers + ", " + oldStyle + ")"; + } + + public List getTypeMungers(ResolvedType onType) { + World world = onType.getWorld(); + List ret = new ArrayList(); + for (Iterator i = typeMungers.iterator(); i.hasNext();) { + Entry entry = (Entry) i.next(); + ResolvedType aspectType = world.resolve(entry.aspectType, true); + if (aspectType.isMissing()) { + world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.ASPECT_NEEDED, entry.aspectType, onType), + onType.getSourceLocation(), null); + continue; + } + + ret.add(new TemporaryTypeMunger(entry.typeMunger, aspectType)); + } + return ret; + } + + public boolean isOldStyle() { + return oldStyle; + } + + public byte[] getUnwovenClassFileData(byte wovenClassFile[]) { + if (unwovenClassFileIsADiff) { + unwovenClassFile = applyDiff(wovenClassFile, unwovenClassFile); + unwovenClassFileIsADiff = false; + } + return unwovenClassFile; + } + + public void setUnwovenClassFileData(byte[] data) { + unwovenClassFile = data; + } + + public boolean isReweavable() { + return reweavable; + } + + public void setReweavable(boolean rw) { + reweavable = rw; + } + + public void addAspectsAffectingType(Collection /* String */aspects) { + aspectsAffectingType.addAll(aspects); + } + + public void addAspectAffectingType(String aspectType) { + aspectsAffectingType.add(aspectType); + } + + public Set /* String */getAspectsAffectingType() { + return this.aspectsAffectingType; + } + + private static void readAnyReweavableData(WeaverStateInfo wsi, DataInputStream s) throws IOException { + if (wsi.isReweavable()) { + // Load list of aspects that need to exist in the world for reweaving to be 'legal' + int numberAspectsAffectingType = s.readShort(); + for (int i = 0; i < numberAspectsAffectingType; i++) { + wsi.addAspectAffectingType(s.readUTF()); + } + + int unwovenClassFileSize = s.readInt(); + byte[] classData = null; + // the unwovenClassFile may have been compressed: + if (wsi.reweavableCompressedMode) { + classData = new byte[unwovenClassFileSize]; + ZipInputStream zis = new ZipInputStream(s); + ZipEntry zen = zis.getNextEntry(); + int current = 0; + int bytesToGo = unwovenClassFileSize; + while (bytesToGo > 0) { + int amount = zis.read(classData, current, bytesToGo); + current += amount; + bytesToGo -= amount; + } + zis.closeEntry(); + if (bytesToGo != 0) + throw new IOException("ERROR whilst reading compressed reweavable data, expected " + unwovenClassFileSize + + " bytes, only found " + current); + } else { + classData = new byte[unwovenClassFileSize]; + int bytesread = s.read(classData); + if (bytesread != unwovenClassFileSize) + throw new IOException("ERROR whilst reading reweavable data, expected " + unwovenClassFileSize + + " bytes, only found " + bytesread); + } + + // if it was diffMode we'll have to remember to apply the diff if someone + // asks for the unwovenClassFile + wsi.unwovenClassFileIsADiff = wsi.reweavableDiffMode; + wsi.setUnwovenClassFileData(classData); + } + } + + /** + * Here is the cleverness for reweavable diff mode. The class file on disk contains, inside the weaverstateinfo attribute, a + * diff that can be applied to 'itself' to recover the original class - which can then be rewoven. + */ + public byte[] replaceKeyWithDiff(byte wovenClassFile[]) { + // we couldn't have made the diff earlier + // as we didn't have the wovenClassFile + // so we left a key there as a marker to come back to + + if (reweavableDiffMode) { + ByteArrayOutputStream arrayStream = new ByteArrayOutputStream(); + DataOutputStream s = new DataOutputStream(arrayStream); + + int endOfKey = findEndOfKey(wovenClassFile); + int startOfKey = endOfKey - key.length; + // the length of the wsi attribute is written infront of it in the classFile, + // swapping the diff for the key will probably change the length of the wsi, + // so we'll have to fiddle with the four 'int length' bytes + int oldLengthLocation = startOfKey - 4; + int oldLength = readInt(wovenClassFile, oldLengthLocation); + wovenClassFile = deleteInArray(wovenClassFile, startOfKey, endOfKey); // delete the key + + byte[] wovenClassFileUpToWSI = new byte[oldLengthLocation]; + System.arraycopy(wovenClassFile, 0, wovenClassFileUpToWSI, 0, oldLengthLocation); + + byte[] diff = generateDiff(wovenClassFileUpToWSI, unwovenClassFile); + try { // put the length of the diff infront of the diff + s.writeInt(diff.length); + s.write(diff); + } catch (IOException e) { + } + diff = arrayStream.toByteArray(); + // we have to swap the oldLength for the new one, + // and add the diff, using the oldLength to work out where it should go :) + + int newLength = oldLength - key.length + diff.length; + byte newLengthBytes[] = serializeInt(newLength); + + // swap in the serialized newLength for the oldOne: + wovenClassFile[oldLengthLocation] = newLengthBytes[0]; + wovenClassFile[oldLengthLocation + 1] = newLengthBytes[1]; + wovenClassFile[oldLengthLocation + 2] = newLengthBytes[2]; + wovenClassFile[oldLengthLocation + 3] = newLengthBytes[3]; + + // add the diff + wovenClassFile = insertArray(diff, wovenClassFile, oldLengthLocation + 4 + oldLength - key.length); + } + return wovenClassFile; + } + + private static final int findEndOfKey(byte[] wovenClassFile) { + // looks through the classfile backwards (as the attributes are all near the end) + for (int i = wovenClassFile.length - 1; i > 0; i--) + if (endOfKeyHere(wovenClassFile, i)) { + return i + 1; + } + throw new RuntimeException("key not found in wovenClassFile"); // should never happen + } + + private static final boolean endOfKeyHere(byte lookIn[], int i) { + for (int j = 0; j < key.length; j++) + if (key[key.length - 1 - j] != lookIn[i - j]) { + return false; + } + return true; + } + + private static final byte[] insertArray(byte toInsert[], byte original[], int offset) { + byte result[] = new byte[original.length + toInsert.length]; + System.arraycopy(original, 0, result, 0, offset); + System.arraycopy(toInsert, 0, result, offset, toInsert.length); + System.arraycopy(original, offset, result, offset + toInsert.length, original.length - offset); + return result; + } + + private static final int readInt(byte[] a, int offset) { + ByteArrayInputStream b = new ByteArrayInputStream(a, offset, 4); + DataInputStream d = new DataInputStream(b); + int length = -1; + try { + length = d.readInt(); + } catch (IOException e) { + throw (new RuntimeException("readInt called with a bad array or offset")); // should never happen + } + return length; + } + + private static final byte[] deleteInArray(byte a[], int start, int end) { + int lengthToDelete = end - start; + byte result[] = new byte[a.length - lengthToDelete]; // make a new array + System.arraycopy(a, 0, result, 0, start); // copy in the bit before the deleted bit + System.arraycopy(a, end, result, start, a.length - end); // copy in the bit after the deleted bit + return result; + } + + // ajh02: a quick note about the diff format... + // + // classfiles consist of: + // 8 bytes: magic number and minor and major versions, + // 2 bytes: its constant pool count + // n bytes: the rest of the class file + // + // weaving a classfile never changes the classfile's first 8 bytes, + // and after the constant pool count there's usually a run of bytes that weaving didn't change + // hereafter referred to as the run + // + // so the diff consists of: + // 2 bytes: its constant pool count + // 4 bytes: length of the run + // n bytes: the rest of the unwovenClassFile + + byte[] generateDiff(byte[] wovenClassFile, byte[] unWovenClassFile) { + + // find how long the run is + int lookingAt = 10; + int shorterLength = (wovenClassFile.length < unWovenClassFile.length) ? wovenClassFile.length : unWovenClassFile.length; + while (lookingAt < shorterLength && (wovenClassFile[lookingAt] == unWovenClassFile[lookingAt])) { + lookingAt++; + } + int lengthInCommon = lookingAt - 10; + byte[] diff = new byte[unWovenClassFile.length - 4 - lengthInCommon]; + + // first 2 bytes of the diff are the constant pool count + diff[0] = unWovenClassFile[8]; + diff[1] = unWovenClassFile[9]; + + // then 4 bytes saying how long the run is + byte[] lengthInCommonBytes = serializeInt(lengthInCommon); + diff[2] = lengthInCommonBytes[0]; + diff[3] = lengthInCommonBytes[1]; + diff[4] = lengthInCommonBytes[2]; + diff[5] = lengthInCommonBytes[3]; + + // then we just dump the rest of the unWovenClassFile verbatim + System.arraycopy(unWovenClassFile, 10 + lengthInCommon, diff, 6, diff.length - 6); + + return diff; + } + + byte[] applyDiff(byte[] wovenClassFile, byte[] diff) { + + int lengthInCommon = readInt(diff, 2); + byte[] unWovenClassFile = new byte[4 + diff.length + lengthInCommon]; + + // copy the first 8 bytes from the wovenClassFile + System.arraycopy(wovenClassFile, 0, unWovenClassFile, 0, 8); + + // copy the constant pool count from the diff + unWovenClassFile[8] = diff[0]; + unWovenClassFile[9] = diff[1]; + + // copy the run from the wovenClassFile + System.arraycopy(wovenClassFile, 10, unWovenClassFile, 10, lengthInCommon); + + // copy the stuff after the run from the diff + System.arraycopy(diff, 6, unWovenClassFile, 10 + lengthInCommon, diff.length - 6); + + return unWovenClassFile; + } + + private byte[] serializeInt(int i) { + ByteArrayOutputStream bos = new ByteArrayOutputStream(4); + DataOutputStream dos = new DataOutputStream(bos); + try { + dos.writeInt(i); + } catch (IOException e) { + } + return bos.toByteArray(); + } + + private static void writeAnyReweavableData(WeaverStateInfo wsi, DataOutputStream s) throws IOException { + if (wsi.isReweavable()) { + // Write out list of aspects that must exist next time we try and weave this class + s.writeShort(wsi.aspectsAffectingType.size()); + if (wsi.aspectsAffectingType.size() > 0) { + for (Iterator iter = wsi.aspectsAffectingType.iterator(); iter.hasNext();) { + String type = (String) iter.next(); + s.writeUTF(type); + } + } + byte[] data = wsi.unwovenClassFile; + + // if we're not in diffMode, write the unwovenClassFile now, + // otherwise we'll insert it as a diff later + if (!wsi.reweavableDiffMode) { + s.writeInt(data.length); + s.write(wsi.unwovenClassFile); + } + } + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/WildcardedUnresolvedType.java b/org.aspectj.matcher/src/org/aspectj/weaver/WildcardedUnresolvedType.java new file mode 100644 index 000000000..5888bc889 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/WildcardedUnresolvedType.java @@ -0,0 +1,69 @@ +/* ******************************************************************* + * 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; + +/** + * Represents a wildcarded bound for a generic type, this can be unbounded '?' or bounded via extends '? extends Foo' or + * super '? super Foo'. The signature for a ? is in fact "*" and the erasure signature is the upper bound which defaults + * to java.lang.Object if nothing is specified. On resolution, this becomes a BoundedReferenceType + * + * @author Andy Clement + */ +public class WildcardedUnresolvedType extends UnresolvedType { + + // TODO does not cope with extra bounds '? extends A & B & C' + + public static final int UNBOUND = 0; + + public static final int EXTENDS = 1; + + public static final int SUPER = 2; + + public static final WildcardedUnresolvedType QUESTIONMARK = new WildcardedUnresolvedType("*", UnresolvedType.OBJECT, null); + + private int boundKind = UNBOUND; // UNBOUND, EXTENDS, SUPER + + private UnresolvedType lowerBound; + + private UnresolvedType upperBound; + + public WildcardedUnresolvedType(String signature, UnresolvedType upperBound, UnresolvedType lowerBound) { + super(signature, (upperBound == null ? UnresolvedType.OBJECT.signature : upperBound.signatureErasure)); + this.typeKind = TypeKind.WILDCARD; + this.upperBound = upperBound; + this.lowerBound = lowerBound; + if (signature.charAt(0) == '-') boundKind = SUPER; + if (signature.charAt(0) == '+') boundKind = EXTENDS; + } + + public UnresolvedType getUpperBound() { + return upperBound; + } + + public UnresolvedType getLowerBound() { + return lowerBound; + } + + public boolean isExtends() { + return boundKind == EXTENDS; + } + + public boolean isSuper() { + return boundKind == SUPER; + } + + public boolean isUnbound() { + return boundKind == UNBOUND; + } + + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/World.java b/org.aspectj.matcher/src/org/aspectj/weaver/World.java new file mode 100644 index 000000000..7cba4d4b8 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/World.java @@ -0,0 +1,1286 @@ +/* ******************************************************************* + * 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, Andy Clement, overhaul for generics + * ******************************************************************/ + +package org.aspectj.weaver; + +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.WeakHashMap; + +import org.aspectj.bridge.IMessageHandler; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.Message; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.bridge.IMessage.Kind; +import org.aspectj.bridge.context.PinpointingMessageHandler; +import org.aspectj.util.IStructureModel; +import org.aspectj.weaver.UnresolvedType.TypeKind; +import org.aspectj.weaver.patterns.DeclarePrecedence; +import org.aspectj.weaver.patterns.Pointcut; +import org.aspectj.weaver.tools.PointcutDesignatorHandler; +import org.aspectj.weaver.tools.Trace; +import org.aspectj.weaver.tools.TraceFactory; + +/** + * A World is a collection of known types and crosscutting members. + */ +public abstract class World implements Dump.INode { + /** handler for any messages produced during resolution etc. */ + private IMessageHandler messageHandler = IMessageHandler.SYSTEM_ERR; + + /** + * handler for cross-reference information produced during the weaving process + */ + private ICrossReferenceHandler xrefHandler = null; + + /** + * Currently 'active' scope in which to lookup (resolve) typevariable references + */ + private TypeVariableDeclaringElement typeVariableLookupScope; + + /** The heart of the world, a map from type signatures to resolved types */ + protected TypeMap typeMap = new TypeMap(this); // Signature to ResolvedType + + /** New pointcut designators this world supports */ + private Set pointcutDesignators; + + // see pr145963 + /** Should we create the hierarchy for binary classes and aspects */ + public static boolean createInjarHierarchy = true; + + /** Calculator for working out aspect precedence */ + private final AspectPrecedenceCalculator precedenceCalculator; + + /** All of the type and shadow mungers known to us */ + private final CrosscuttingMembersSet crosscuttingMembersSet = new CrosscuttingMembersSet(this); + + /** The structure model for the compilation */ + private IStructureModel model = null; + + /** for processing Xlint messages */ + private Lint lint = new Lint(this); + + /** XnoInline option setting passed down to weaver */ + private boolean XnoInline; + + /** XlazyTjp option setting passed down to weaver */ + private boolean XlazyTjp; + + /** XhasMember option setting passed down to weaver */ + private boolean XhasMember = false; + + /** + * Xpinpoint controls whether we put out developer info showing the source of messages + */ + private boolean Xpinpoint = false; + + /** When behaving in a Java 5 way autoboxing is considered */ + private boolean behaveInJava5Way = false; + + /** Determines if this world could be used for multiple compiles */ + private boolean incrementalCompileCouldFollow = false; + + /** The level of the aspectjrt.jar the code we generate needs to run on */ + private String targetAspectjRuntimeLevel = Constants.RUNTIME_LEVEL_DEFAULT; + + /** Flags for the new joinpoints that are 'optional' */ + private boolean optionalJoinpoint_ArrayConstruction = false; // Command line + // flag: + // "-Xjoinpoints:arrayconstruction" + private boolean optionalJoinpoint_Synchronization = false; // Command line + // flag: + // "-Xjoinpoints:synchronization" + + private boolean addSerialVerUID = false; + + private Properties extraConfiguration = null; + private boolean checkedAdvancedConfiguration = false; + private boolean synchronizationPointcutsInUse = false; + // Xset'table options + private boolean runMinimalMemory = false; + private boolean shouldPipelineCompilation = true; + private boolean shouldGenerateStackMaps = false; + protected boolean bcelRepositoryCaching = xsetBCEL_REPOSITORY_CACHING_DEFAULT.equalsIgnoreCase("true"); + private boolean fastMethodPacking = false; + private boolean completeBinaryTypes = false; + public boolean forDEBUG_structuralChangesCode = false; + public boolean forDEBUG_bridgingCode = false; + + private static Trace trace = TraceFactory.getTraceFactory().getTrace(World.class); + + private long errorThreshold; + private long warningThreshold; + + /** + * A list of RuntimeExceptions containing full stack information for every type we couldn't find. + */ + private List dumpState_cantFindTypeExceptions = null; + + /** + * Play God. On the first day, God created the primitive types and put them in the type map. + */ + protected World() { + super(); + if (trace.isTraceEnabled()) + trace.enter("", this); + Dump.registerNode(this.getClass(), this); + typeMap.put("B", ResolvedType.BYTE); + typeMap.put("S", ResolvedType.SHORT); + typeMap.put("I", ResolvedType.INT); + typeMap.put("J", ResolvedType.LONG); + typeMap.put("F", ResolvedType.FLOAT); + typeMap.put("D", ResolvedType.DOUBLE); + typeMap.put("C", ResolvedType.CHAR); + typeMap.put("Z", ResolvedType.BOOLEAN); + typeMap.put("V", ResolvedType.VOID); + precedenceCalculator = new AspectPrecedenceCalculator(this); + if (trace.isTraceEnabled()) + trace.exit(""); + } + + /** + * Dump processing when a fatal error occurs + */ + public void accept(Dump.IVisitor visitor) { + // visitor.visitObject("Extra configuration:"); + // visitor.visitList(extraConfiguration.); + visitor.visitObject("Shadow mungers:"); + visitor.visitList(crosscuttingMembersSet.getShadowMungers()); + visitor.visitObject("Type mungers:"); + visitor.visitList(crosscuttingMembersSet.getTypeMungers()); + visitor.visitObject("Late Type mungers:"); + visitor.visitList(crosscuttingMembersSet.getLateTypeMungers()); + if (dumpState_cantFindTypeExceptions != null) { + visitor.visitObject("Cant find type problems:"); + visitor.visitList(dumpState_cantFindTypeExceptions); + dumpState_cantFindTypeExceptions = null; + } + } + + // ========================================================================== + // === + // T Y P E R E S O L U T I O N + // ========================================================================== + // === + + /** + * Resolve a type that we require to be present in the world + */ + public ResolvedType resolve(UnresolvedType ty) { + return resolve(ty, false); + } + + /** + * Attempt to resolve a type - the source location gives you some context in which resolution is taking place. In the case of an + * error where we can't find the type - we can then at least report why (source location) we were trying to resolve it. + */ + public ResolvedType resolve(UnresolvedType ty, ISourceLocation isl) { + ResolvedType ret = resolve(ty, true); + if (ResolvedType.isMissing(ty)) { + // IMessage msg = null; + getLint().cantFindType.signal(WeaverMessages.format(WeaverMessages.CANT_FIND_TYPE, ty.getName()), isl); + // if (isl!=null) { + // msg = MessageUtil.error(WeaverMessages.format(WeaverMessages. + // CANT_FIND_TYPE,ty.getName()),isl); + // } else { + // msg = MessageUtil.error(WeaverMessages.format(WeaverMessages. + // CANT_FIND_TYPE,ty.getName())); + // } + // messageHandler.handleMessage(msg); + } + return ret; + } + + /** + * Convenience method for resolving an array of unresolved types in one hit. Useful for e.g. resolving type parameters in + * signatures. + */ + public ResolvedType[] resolve(UnresolvedType[] types) { + if (types == null) + return new ResolvedType[0]; + + ResolvedType[] ret = new ResolvedType[types.length]; + for (int i = 0; i < types.length; i++) { + ret[i] = resolve(types[i]); + } + return ret; + } + + /** + * Resolve a type. This the hub of type resolution. The resolved type is added to the type map by signature. + */ + public ResolvedType resolve(UnresolvedType ty, boolean allowMissing) { + + // special resolution processing for already resolved types. + if (ty instanceof ResolvedType) { + ResolvedType rty = (ResolvedType) ty; + rty = resolve(rty); + return rty; + } + + // dispatch back to the type variable reference to resolve its + // constituent parts + // don't do this for other unresolved types otherwise you'll end up in a + // loop + if (ty.isTypeVariableReference()) { + return ty.resolve(this); + } + + // if we've already got a resolved type for the signature, just return + // it + // after updating the world + String signature = ty.getSignature(); + ResolvedType ret = typeMap.get(signature); + if (ret != null) { + ret.world = this; // Set the world for the RTX + return ret; + } else if (signature.equals("?") || signature.equals("*")) { + // might be a problem here, not sure '?' should make it to here as a + // signature, the + // proper signature for wildcard '?' is '*' + // fault in generic wildcard, can't be done earlier because of init + // issues + // TODO ought to be shared single instance representing this + ResolvedType something = new BoundedReferenceType("*", "Ljava/lang/Object", this); + typeMap.put("?", something); + return something; + } + + // no existing resolved type, create one + if (ty.isArray()) { + ResolvedType componentType = resolve(ty.getComponentType(), allowMissing); + // String brackets = + // signature.substring(0,signature.lastIndexOf("[")+1); + ret = new ArrayReferenceType(signature, "[" + componentType.getErasureSignature(), this, componentType); + } else { + ret = resolveToReferenceType(ty, allowMissing); + if (!allowMissing && ret.isMissing()) { + ret = handleRequiredMissingTypeDuringResolution(ty); + } + if (completeBinaryTypes) { + completeBinaryType(ret); + } + } + + // Pulling in the type may have already put the right entry in the map + if (typeMap.get(signature) == null && !ret.isMissing()) { + typeMap.put(signature, ret); + } + return ret; + } + + /** + * Called when a type is resolved - enables its type hierarchy to be finished off before we proceed + */ + protected void completeBinaryType(ResolvedType ret) { + } + + /** + * Return true if the classloader relating to this world is definetly the one that will define the specified class. Return false + * otherwise or we don't know for certain. + */ + public boolean isLocallyDefined(String classname) { + return false; + } + + /** + * We tried to resolve a type and couldn't find it... + */ + private ResolvedType handleRequiredMissingTypeDuringResolution(UnresolvedType ty) { + // defer the message until someone asks a question of the type that we + // can't answer + // just from the signature. + // MessageUtil.error(messageHandler, + // WeaverMessages.format(WeaverMessages.CANT_FIND_TYPE,ty.getName())); + if (dumpState_cantFindTypeExceptions == null) { + dumpState_cantFindTypeExceptions = new ArrayList(); + } + if (dumpState_cantFindTypeExceptions.size() < 100) { // limit growth + dumpState_cantFindTypeExceptions.add(new RuntimeException("Can't find type " + ty.getName())); + } + return new MissingResolvedTypeWithKnownSignature(ty.getSignature(), this); + } + + /** + * Some TypeFactory operations create resolved types directly, but these won't be in the typeMap - this resolution process puts + * them there. Resolved types are also told their world which is needed for the special autoboxing resolved types. + */ + public ResolvedType resolve(ResolvedType ty) { + if (ty.isTypeVariableReference()) + return ty; // until type variables have proper sigs... + ResolvedType resolved = typeMap.get(ty.getSignature()); + if (resolved == null) { + typeMap.put(ty.getSignature(), ty); + resolved = ty; + } + resolved.world = this; + return resolved; + } + + /** + * Convenience method for finding a type by name and resolving it in one step. + */ + public ResolvedType resolve(String name) { + // trace.enter("resolve", this, new Object[] {name}); + ResolvedType ret = resolve(UnresolvedType.forName(name)); + // trace.exit("resolve", ret); + return ret; + } + + public ResolvedType resolve(String name, boolean allowMissing) { + return resolve(UnresolvedType.forName(name), allowMissing); + } + + /** + * Resolve to a ReferenceType - simple, raw, parameterized, or generic. Raw, parameterized, and generic versions of a type share + * a delegate. + */ + private final ResolvedType resolveToReferenceType(UnresolvedType ty, boolean allowMissing) { + if (ty.isParameterizedType()) { + // ======= parameterized types ================ + ResolvedType rt = resolveGenericTypeFor(ty, allowMissing); + if (rt.isMissing()) + return rt; + ReferenceType genericType = (ReferenceType) rt; + ReferenceType parameterizedType = TypeFactory.createParameterizedType(genericType, ty.typeParameters, this); + return parameterizedType; + + } else if (ty.isGenericType()) { + // ======= generic types ====================== + ReferenceType genericType = (ReferenceType) resolveGenericTypeFor(ty, false); + return genericType; + + } else if (ty.isGenericWildcard()) { + // ======= generic wildcard types ============= + return resolveGenericWildcardFor((WildcardedUnresolvedType) ty); + } else { + // ======= simple and raw types =============== + String erasedSignature = ty.getErasureSignature(); + ReferenceType simpleOrRawType = new ReferenceType(erasedSignature, this); + if (ty.needsModifiableDelegate()) + simpleOrRawType.setNeedsModifiableDelegate(true); + ReferenceTypeDelegate delegate = resolveDelegate(simpleOrRawType); + // 117854 + // if (delegate == null) return ResolvedType.MISSING; + if (delegate == null) + return new MissingResolvedTypeWithKnownSignature(ty.getSignature(), erasedSignature, this);// ResolvedType + // . + // MISSING + // ; + + if (delegate.isGeneric() && behaveInJava5Way) { + // ======== raw type =========== + simpleOrRawType.typeKind = TypeKind.RAW; + ReferenceType genericType = makeGenericTypeFrom(delegate, simpleOrRawType); + // name = + // ReferenceType.fromTypeX(UnresolvedType.forRawTypeNames( + // ty.getName()),this); + simpleOrRawType.setDelegate(delegate); + genericType.setDelegate(delegate); + simpleOrRawType.setGenericType(genericType); + return simpleOrRawType; + + } else { + // ======== simple type ========= + simpleOrRawType.setDelegate(delegate); + return simpleOrRawType; + } + } + } + + /** + * Attempt to resolve a type that should be a generic type. + */ + public ResolvedType resolveGenericTypeFor(UnresolvedType anUnresolvedType, boolean allowMissing) { + // Look up the raw type by signature + String rawSignature = anUnresolvedType.getRawType().getSignature(); + ResolvedType rawType = typeMap.get(rawSignature); + if (rawType == null) { + rawType = resolve(UnresolvedType.forSignature(rawSignature), allowMissing); + typeMap.put(rawSignature, rawType); + } + if (rawType.isMissing()) + return rawType; + + // Does the raw type know its generic form? (It will if we created the + // raw type from a source type, it won't if its been created just + // through + // being referenced, e.g. java.util.List + ResolvedType genericType = rawType.getGenericType(); + + // There is a special case to consider here (testGenericsBang_pr95993 + // highlights it) + // You may have an unresolvedType for a parameterized type but it + // is backed by a simple type rather than a generic type. This occurs + // for + // inner types of generic types that inherit their enclosing types + // type variables. + if (rawType.isSimpleType() && (anUnresolvedType.typeParameters == null || anUnresolvedType.typeParameters.length == 0)) { + rawType.world = this; + return rawType; + } + + if (genericType != null) { + genericType.world = this; + return genericType; + } else { + // Fault in the generic that underpins the raw type ;) + ReferenceTypeDelegate delegate = resolveDelegate((ReferenceType) rawType); + ReferenceType genericRefType = makeGenericTypeFrom(delegate, ((ReferenceType) rawType)); + ((ReferenceType) rawType).setGenericType(genericRefType); + genericRefType.setDelegate(delegate); + ((ReferenceType) rawType).setDelegate(delegate); + return genericRefType; + } + } + + private ReferenceType makeGenericTypeFrom(ReferenceTypeDelegate delegate, ReferenceType rawType) { + String genericSig = delegate.getDeclaredGenericSignature(); + if (genericSig != null) { + return new ReferenceType(UnresolvedType.forGenericTypeSignature(rawType.getSignature(), delegate + .getDeclaredGenericSignature()), this); + } else { + return new ReferenceType(UnresolvedType.forGenericTypeVariables(rawType.getSignature(), delegate.getTypeVariables()), + this); + } + } + + /** + * Go from an unresolved generic wildcard (represented by UnresolvedType) to a resolved version (BoundedReferenceType). + */ + private ReferenceType resolveGenericWildcardFor(WildcardedUnresolvedType aType) { + BoundedReferenceType ret = null; + // FIXME asc doesnt take account of additional interface bounds (e.g. ? + // super R & Serializable - can you do that?) + if (aType.isExtends()) { + ReferenceType upperBound = (ReferenceType) resolve(aType.getUpperBound()); + ret = new BoundedReferenceType(upperBound, true, this); + } else if (aType.isSuper()) { + ReferenceType lowerBound = (ReferenceType) resolve(aType.getLowerBound()); + ret = new BoundedReferenceType(lowerBound, false, this); + } else { + // must be ? on its own! + ret = new BoundedReferenceType("*", "Ljava/lang/Object", this); + } + return ret; + } + + /** + * Find the ReferenceTypeDelegate behind this reference type so that it can fulfill its contract. + */ + protected abstract ReferenceTypeDelegate resolveDelegate(ReferenceType ty); + + /** + * Special resolution for "core" types like OBJECT. These are resolved just like any other type, but if they are not found it is + * more serious and we issue an error message immediately. + */ + // OPTIMIZE streamline path for core types? They are just simple types, + // could look straight in the typemap? + public ResolvedType getCoreType(UnresolvedType tx) { + ResolvedType coreTy = resolve(tx, true); + if (coreTy.isMissing()) { + MessageUtil.error(messageHandler, WeaverMessages.format(WeaverMessages.CANT_FIND_CORE_TYPE, tx.getName())); + } + return coreTy; + } + + /** + * Lookup a type by signature, if not found then build one and put it in the map. + */ + public ReferenceType lookupOrCreateName(UnresolvedType ty) { + String signature = ty.getSignature(); + ReferenceType ret = lookupBySignature(signature); + if (ret == null) { + ret = ReferenceType.fromTypeX(ty, this); + typeMap.put(signature, ret); + } + return ret; + } + + /** + * Lookup a reference type in the world by its signature. Returns null if not found. + */ + public ReferenceType lookupBySignature(String signature) { + return (ReferenceType) typeMap.get(signature); + } + + // ========================================================================== + // === + // T Y P E R E S O L U T I O N -- E N D + // ========================================================================== + // === + + /** + * Member resolution is achieved by resolving the declaring type and then looking up the member in the resolved declaring type. + */ + public ResolvedMember resolve(Member member) { + ResolvedType declaring = member.getDeclaringType().resolve(this); + if (declaring.isRawType()) + declaring = declaring.getGenericType(); + ResolvedMember ret; + if (member.getKind() == Member.FIELD) { + ret = declaring.lookupField(member); + } else { + ret = declaring.lookupMethod(member); + } + + if (ret != null) + return ret; + + return declaring.lookupSyntheticMember(member); + } + + public abstract IWeavingSupport getWeavingSupport(); + + /** + * Create an advice shadow munger from the given advice attribute + */ + // public abstract Advice createAdviceMunger(AjAttribute.AdviceAttribute + // attribute, Pointcut pointcut, Member signature); + /** + * Create an advice shadow munger for the given advice kind + */ + public final Advice createAdviceMunger(AdviceKind kind, Pointcut p, Member signature, int extraParameterFlags, + IHasSourceLocation loc) { + AjAttribute.AdviceAttribute attribute = new AjAttribute.AdviceAttribute(kind, p, extraParameterFlags, loc.getStart(), loc + .getEnd(), loc.getSourceContext()); + return getWeavingSupport().createAdviceMunger(attribute, p, signature); + } + + /** + * Same signature as org.aspectj.util.PartialOrder.PartialComparable.compareTo + */ + public int compareByPrecedence(ResolvedType aspect1, ResolvedType aspect2) { + return precedenceCalculator.compareByPrecedence(aspect1, aspect2); + } + + public Integer getPrecedenceIfAny(ResolvedType aspect1, ResolvedType aspect2) { + return precedenceCalculator.getPrecedenceIfAny(aspect1, aspect2); + } + + /** + * compares by precedence with the additional rule that a super-aspect is sorted before its sub-aspects + */ + public int compareByPrecedenceAndHierarchy(ResolvedType aspect1, ResolvedType aspect2) { + return precedenceCalculator.compareByPrecedenceAndHierarchy(aspect1, aspect2); + } + + // simple property getter and setters + // =========================================================== + + /** + * Nobody should hold onto a copy of this message handler, or setMessageHandler won't work right. + */ + public IMessageHandler getMessageHandler() { + return messageHandler; + } + + public void setMessageHandler(IMessageHandler messageHandler) { + if (this.isInPinpointMode()) { + this.messageHandler = new PinpointingMessageHandler(messageHandler); + } else { + this.messageHandler = messageHandler; + } + } + + /** + * convenenience method for creating and issuing messages via the message handler - if you supply two locations you will get two + * messages. + */ + public void showMessage(Kind kind, String message, ISourceLocation loc1, ISourceLocation loc2) { + if (loc1 != null) { + messageHandler.handleMessage(new Message(message, kind, null, loc1)); + if (loc2 != null) { + messageHandler.handleMessage(new Message(message, kind, null, loc2)); + } + } else { + messageHandler.handleMessage(new Message(message, kind, null, loc2)); + } + } + + public void setCrossReferenceHandler(ICrossReferenceHandler xrefHandler) { + this.xrefHandler = xrefHandler; + } + + /** + * Get the cross-reference handler for the world, may be null. + */ + public ICrossReferenceHandler getCrossReferenceHandler() { + return xrefHandler; + } + + public void setTypeVariableLookupScope(TypeVariableDeclaringElement scope) { + typeVariableLookupScope = scope; + } + + public TypeVariableDeclaringElement getTypeVariableLookupScope() { + return typeVariableLookupScope; + } + + public List getDeclareParents() { + return crosscuttingMembersSet.getDeclareParents(); + } + + public List getDeclareAnnotationOnTypes() { + return crosscuttingMembersSet.getDeclareAnnotationOnTypes(); + } + + public List getDeclareAnnotationOnFields() { + return crosscuttingMembersSet.getDeclareAnnotationOnFields(); + } + + public List getDeclareAnnotationOnMethods() { + return crosscuttingMembersSet.getDeclareAnnotationOnMethods(); + } + + public List getDeclareSoft() { + return crosscuttingMembersSet.getDeclareSofts(); + } + + public CrosscuttingMembersSet getCrosscuttingMembersSet() { + return crosscuttingMembersSet; + } + + public IStructureModel getModel() { + return model; + } + + public void setModel(IStructureModel model) { + this.model = model; + } + + public Lint getLint() { + return lint; + } + + public void setLint(Lint lint) { + this.lint = lint; + } + + public boolean isXnoInline() { + return XnoInline; + } + + public void setXnoInline(boolean xnoInline) { + XnoInline = xnoInline; + } + + public boolean isXlazyTjp() { + return XlazyTjp; + } + + public void setXlazyTjp(boolean b) { + XlazyTjp = b; + } + + public boolean isHasMemberSupportEnabled() { + return XhasMember; + } + + public void setXHasMemberSupportEnabled(boolean b) { + XhasMember = b; + } + + public boolean isInPinpointMode() { + return Xpinpoint; + } + + public void setPinpointMode(boolean b) { + Xpinpoint = b; + } + + public void setBehaveInJava5Way(boolean b) { + behaveInJava5Way = b; + } + + /** + * Set the error and warning threashold which can be taken from CompilerOptions (see bug 129282) + * + * @param errorThreshold + * @param warningThreshold + */ + public void setErrorAndWarningThreshold(long errorThreshold, long warningThreshold) { + this.errorThreshold = errorThreshold; + this.warningThreshold = warningThreshold; + } + + /** + * @return true if ignoring the UnusedDeclaredThrownException and false if this compiler option is set to error or warning + */ + public boolean isIgnoringUnusedDeclaredThrownException() { + // the 0x800000 is CompilerOptions.UnusedDeclaredThrownException + // which is ASTNode.bit24 + if ((errorThreshold & 0x800000) != 0 || (warningThreshold & 0x800000) != 0) + return false; + return true; + } + + public void performExtraConfiguration(String config) { + if (config == null) + return; + // Bunch of name value pairs to split + extraConfiguration = new Properties(); + int pos = -1; + while ((pos = config.indexOf(",")) != -1) { + String nvpair = config.substring(0, pos); + int pos2 = nvpair.indexOf("="); + if (pos2 != -1) { + String n = nvpair.substring(0, pos2); + String v = nvpair.substring(pos2 + 1); + extraConfiguration.setProperty(n, v); + } + config = config.substring(pos + 1); + } + if (config.length() > 0) { + int pos2 = config.indexOf("="); + if (pos2 != -1) { + String n = config.substring(0, pos2); + String v = config.substring(pos2 + 1); + extraConfiguration.setProperty(n, v); + } + } + ensureAdvancedConfigurationProcessed(); + } + + /** + * may return null + */ + public Properties getExtraConfiguration() { + return extraConfiguration; + } + + public final static String xsetWEAVE_JAVA_PACKAGES = "weaveJavaPackages"; // default + // false + // - + // controls + // LTW + public final static String xsetWEAVE_JAVAX_PACKAGES = "weaveJavaxPackages"; // default + // false + // - + // controls + // LTW + public final static String xsetCAPTURE_ALL_CONTEXT = "captureAllContext"; // default + // false + public final static String xsetRUN_MINIMAL_MEMORY = "runMinimalMemory"; // default + // true + public final static String xsetDEBUG_STRUCTURAL_CHANGES_CODE = "debugStructuralChangesCode"; // default + // false + public final static String xsetDEBUG_BRIDGING = "debugBridging"; // default + // false + public final static String xsetBCEL_REPOSITORY_CACHING = "bcelRepositoryCaching"; + public final static String xsetPIPELINE_COMPILATION = "pipelineCompilation"; + public final static String xsetGENERATE_STACKMAPS = "generateStackMaps"; + public final static String xsetPIPELINE_COMPILATION_DEFAULT = "true"; + public final static String xsetCOMPLETE_BINARY_TYPES = "completeBinaryTypes"; + public final static String xsetCOMPLETE_BINARY_TYPES_DEFAULT = "false"; + public final static String xsetBCEL_REPOSITORY_CACHING_DEFAULT = "true"; + public final static String xsetFAST_PACK_METHODS = "fastPackMethods"; // default + + // TRUE + + public boolean isInJava5Mode() { + return behaveInJava5Way; + } + + public void setTargetAspectjRuntimeLevel(String s) { + targetAspectjRuntimeLevel = s; + } + + public void setOptionalJoinpoints(String jps) { + if (jps == null) + return; + if (jps.indexOf("arrayconstruction") != -1) + optionalJoinpoint_ArrayConstruction = true; + if (jps.indexOf("synchronization") != -1) + optionalJoinpoint_Synchronization = true; + } + + public boolean isJoinpointArrayConstructionEnabled() { + return optionalJoinpoint_ArrayConstruction; + } + + public boolean isJoinpointSynchronizationEnabled() { + return optionalJoinpoint_Synchronization; + } + + public String getTargetAspectjRuntimeLevel() { + return targetAspectjRuntimeLevel; + } + + // OPTIMIZE are users falling foul of not supplying -1.5 and so targetting + // the old runtime? + public boolean isTargettingAspectJRuntime12() { + boolean b = false; // pr116679 + if (!isInJava5Mode()) + b = true; + else + b = getTargetAspectjRuntimeLevel().equals(org.aspectj.weaver.Constants.RUNTIME_LEVEL_12); + // System.err.println("Asked if targetting runtime 1.2 , returning: "+b); + return b; + } + + /* + * Map of types in the world, can have 'references' to expendable ones which can be garbage collected to recover memory. An + * expendable type is a reference type that is not exposed to the weaver (ie just pulled in for type resolution purposes). + */ + protected static class TypeMap { + + private static boolean debug = false; + + // Strategy for entries in the expendable map + public final static int DONT_USE_REFS = 0; // Hang around forever + public final static int USE_WEAK_REFS = 1; // Collected asap + public final static int USE_SOFT_REFS = 2; // Collected when short on + // memory + + // SECRETAPI - Can switch to a policy of choice ;) + public static int policy = USE_SOFT_REFS; + + // Map of types that never get thrown away + private final Map /* String -> ResolvedType */tMap = new HashMap(); + + // Map of types that may be ejected from the cache if we need space + private final Map expendableMap = Collections.synchronizedMap(new WeakHashMap()); + + private final World w; + + // profiling tools... + private boolean memoryProfiling = false; + private int maxExpendableMapSize = -1; + private int collectedTypes = 0; + private final ReferenceQueue rq = new ReferenceQueue(); + + private static Trace trace = TraceFactory.getTraceFactory().getTrace(World.TypeMap.class); + + TypeMap(World w) { + if (trace.isTraceEnabled()) + trace.enter("", this, w); + this.w = w; + memoryProfiling = false;// !w.getMessageHandler().isIgnoring(Message. + // INFO); + if (trace.isTraceEnabled()) + trace.exit(""); + } + + /** + * Add a new type into the map, the key is the type signature. Some types do *not* go in the map, these are ones involving + * *member* type variables. The reason is that when all you have is the signature which gives you a type variable name, you + * cannot guarantee you are using the type variable in the same way as someone previously working with a similarly named + * type variable. So, these do not go into the map: - TypeVariableReferenceType. - ParameterizedType where a member type + * variable is involved. - BoundedReferenceType when one of the bounds is a type variable. + * + * definition: "member type variables" - a tvar declared on a generic method/ctor as opposed to those you see declared on a + * generic type. + */ + public ResolvedType put(String key, ResolvedType type) { + if (type.isParameterizedType() && type.isParameterizedWithTypeVariable()) { + if (debug) + System.err + .println("Not putting a parameterized type that utilises member declared type variables into the typemap: key=" + + key + " type=" + type); + return type; + } + if (type.isTypeVariableReference()) { + if (debug) + System.err.println("Not putting a type variable reference type into the typemap: key=" + key + " type=" + type); + return type; + } + // this test should be improved - only avoid putting them in if one + // of the + // bounds is a member type variable + if (type instanceof BoundedReferenceType) { + if (debug) + System.err.println("Not putting a bounded reference type into the typemap: key=" + key + " type=" + type); + return type; + } + if (type instanceof MissingResolvedTypeWithKnownSignature) { + if (debug) + System.err.println("Not putting a missing type into the typemap: key=" + key + " type=" + type); + return type; + } + + if ((type instanceof ReferenceType) && (((ReferenceType) type).getDelegate() == null) && w.isExpendable(type)) { + if (debug) + System.err.println("Not putting expendable ref type with null delegate into typemap: key=" + key + " type=" + + type); + return type; + } + + if (w.isExpendable(type)) { + // Dont use reference queue for tracking if not profiling... + if (policy == USE_WEAK_REFS) { + if (memoryProfiling) + expendableMap.put(key, new WeakReference(type, rq)); + else + expendableMap.put(key, new WeakReference(type)); + } else if (policy == USE_SOFT_REFS) { + if (memoryProfiling) + expendableMap.put(key, new SoftReference(type, rq)); + else + expendableMap.put(key, new SoftReference(type)); + } else { + expendableMap.put(key, type); + } + if (memoryProfiling && expendableMap.size() > maxExpendableMapSize) { + maxExpendableMapSize = expendableMap.size(); + } + return type; + } else { + return (ResolvedType) tMap.put(key, type); + } + } + + public void report() { + if (!memoryProfiling) + return; + checkq(); + w.getMessageHandler().handleMessage( + MessageUtil.info("MEMORY: world expendable type map reached maximum size of #" + maxExpendableMapSize + + " entries")); + w.getMessageHandler().handleMessage( + MessageUtil.info("MEMORY: types collected through garbage collection #" + collectedTypes + " entries")); + } + + public void checkq() { + if (!memoryProfiling) + return; + while (rq.poll() != null) + collectedTypes++; + } + + /** + * Lookup a type by its signature, always look in the real map before the expendable map + */ + public ResolvedType get(String key) { + checkq(); + ResolvedType ret = (ResolvedType) tMap.get(key); + if (ret == null) { + if (policy == USE_WEAK_REFS) { + WeakReference ref = (WeakReference) expendableMap.get(key); + if (ref != null) { + ret = (ResolvedType) ref.get(); + } + } else if (policy == USE_SOFT_REFS) { + SoftReference ref = (SoftReference) expendableMap.get(key); + if (ref != null) { + ret = (ResolvedType) ref.get(); + } + } else { + return (ResolvedType) expendableMap.get(key); + } + } + return ret; + } + + /** Remove a type from the map */ + public ResolvedType remove(String key) { + ResolvedType ret = (ResolvedType) tMap.remove(key); + if (ret == null) { + if (policy == USE_WEAK_REFS) { + WeakReference wref = (WeakReference) expendableMap.remove(key); + if (wref != null) + ret = (ResolvedType) wref.get(); + } else if (policy == USE_SOFT_REFS) { + SoftReference wref = (SoftReference) expendableMap.remove(key); + if (wref != null) + ret = (ResolvedType) wref.get(); + } else { + ret = (ResolvedType) expendableMap.remove(key); + } + } + return ret; + } + + // public ResolvedType[] getAllTypes() { + // List/* ResolvedType */results = new ArrayList(); + // + // collectTypes(expendableMap, results); + // collectTypes(tMap, results); + // return (ResolvedType[]) results.toArray(new + // ResolvedType[results.size()]); + // } + // + // private void collectTypes(Map map, List/* ResolvedType */results) { + // for (Iterator iterator = map.keySet().iterator(); + // iterator.hasNext();) { + // String key = (String) iterator.next(); + // ResolvedType type = get(key); + // if (type != null) + // results.add(type); + // else + // System.err.println("null!:" + key); + // } + // } + + } + + /** + * Reference types we don't intend to weave may be ejected from the cache if we need the space. + */ + protected boolean isExpendable(ResolvedType type) { + return (!type.equals(UnresolvedType.OBJECT) && (!type.isExposedToWeaver()) && (!type.isPrimitiveType())); + } + + /** + * This class is used to compute and store precedence relationships between aspects. + */ + private static class AspectPrecedenceCalculator { + + private final World world; + private final Map cachedResults; + + public AspectPrecedenceCalculator(World forSomeWorld) { + world = forSomeWorld; + cachedResults = new HashMap(); + } + + /** + * Ask every declare precedence in the world to order the two aspects. If more than one declare precedence gives an + * ordering, and the orderings conflict, then that's an error. + */ + public int compareByPrecedence(ResolvedType firstAspect, ResolvedType secondAspect) { + PrecedenceCacheKey key = new PrecedenceCacheKey(firstAspect, secondAspect); + if (cachedResults.containsKey(key)) { + return ((Integer) cachedResults.get(key)).intValue(); + } else { + int order = 0; + DeclarePrecedence orderer = null; // Records the declare + // precedence statement that + // gives the first ordering + for (Iterator i = world.getCrosscuttingMembersSet().getDeclareDominates().iterator(); i.hasNext();) { + DeclarePrecedence d = (DeclarePrecedence) i.next(); + int thisOrder = d.compare(firstAspect, secondAspect); + if (thisOrder != 0) { + if (orderer == null) + orderer = d; + if (order != 0 && order != thisOrder) { + ISourceLocation[] isls = new ISourceLocation[2]; + isls[0] = orderer.getSourceLocation(); + isls[1] = d.getSourceLocation(); + Message m = new Message("conflicting declare precedence orderings for aspects: " + + firstAspect.getName() + " and " + secondAspect.getName(), null, true, isls); + world.getMessageHandler().handleMessage(m); + } else { + order = thisOrder; + } + } + } + cachedResults.put(key, new Integer(order)); + return order; + } + } + + public Integer getPrecedenceIfAny(ResolvedType aspect1, ResolvedType aspect2) { + return (Integer) cachedResults.get(new PrecedenceCacheKey(aspect1, aspect2)); + } + + public int compareByPrecedenceAndHierarchy(ResolvedType firstAspect, ResolvedType secondAspect) { + if (firstAspect.equals(secondAspect)) + return 0; + + int ret = compareByPrecedence(firstAspect, secondAspect); + if (ret != 0) + return ret; + + if (firstAspect.isAssignableFrom(secondAspect)) + return -1; + else if (secondAspect.isAssignableFrom(firstAspect)) + return +1; + + return 0; + } + + private static class PrecedenceCacheKey { + public ResolvedType aspect1; + public ResolvedType aspect2; + + public PrecedenceCacheKey(ResolvedType a1, ResolvedType a2) { + aspect1 = a1; + aspect2 = a2; + } + + public boolean equals(Object obj) { + if (!(obj instanceof PrecedenceCacheKey)) + return false; + PrecedenceCacheKey other = (PrecedenceCacheKey) obj; + return (aspect1 == other.aspect1 && aspect2 == other.aspect2); + } + + public int hashCode() { + return aspect1.hashCode() + aspect2.hashCode(); + } + } + } + + public void validateType(UnresolvedType type) { + } + + // --- with java5 we can get into a recursive mess if we aren't careful when + // resolving types (*cough* java.lang.Enum) --- + + // --- this first map is for java15 delegates which may try and recursively + // access the same type variables. + // --- I would rather stash this against a reference type - but we don't + // guarantee referencetypes are unique for + // so we can't :( + private final Map workInProgress1 = new HashMap(); + + public TypeVariable[] getTypeVariablesCurrentlyBeingProcessed(Class baseClass) { + return (TypeVariable[]) workInProgress1.get(baseClass); + } + + public void recordTypeVariablesCurrentlyBeingProcessed(Class baseClass, TypeVariable[] typeVariables) { + workInProgress1.put(baseClass, typeVariables); + } + + public void forgetTypeVariablesCurrentlyBeingProcessed(Class baseClass) { + workInProgress1.remove(baseClass); + } + + public void setAddSerialVerUID(boolean b) { + addSerialVerUID = b; + } + + public boolean isAddSerialVerUID() { + return addSerialVerUID; + } + + /** be careful calling this - pr152257 */ + public void flush() { + typeMap.expendableMap.clear(); + } + + public void ensureAdvancedConfigurationProcessed() { + // Check *once* whether the user has switched asm support off + if (!checkedAdvancedConfiguration) { + Properties p = getExtraConfiguration(); + if (p != null) { + + String s = p.getProperty(xsetBCEL_REPOSITORY_CACHING, xsetBCEL_REPOSITORY_CACHING_DEFAULT); + bcelRepositoryCaching = s.equalsIgnoreCase("true"); + if (!bcelRepositoryCaching) { + getMessageHandler().handleMessage( + MessageUtil + .info("[bcelRepositoryCaching=false] AspectJ will not use a bcel cache for class information")); + } + + s = p.getProperty(xsetFAST_PACK_METHODS, "true"); + fastMethodPacking = s.equalsIgnoreCase("true"); + + s = p.getProperty(xsetPIPELINE_COMPILATION, xsetPIPELINE_COMPILATION_DEFAULT); + shouldPipelineCompilation = s.equalsIgnoreCase("true"); + + s = p.getProperty(xsetGENERATE_STACKMAPS, "false"); + shouldGenerateStackMaps = s.equalsIgnoreCase("true"); + + s = p.getProperty(xsetCOMPLETE_BINARY_TYPES, xsetCOMPLETE_BINARY_TYPES_DEFAULT); + completeBinaryTypes = s.equalsIgnoreCase("true"); + if (completeBinaryTypes) { + getMessageHandler().handleMessage( + MessageUtil.info("[completeBinaryTypes=true] Completion of binary types activated")); + } + + s = p.getProperty(xsetRUN_MINIMAL_MEMORY, "false"); + runMinimalMemory = s.equalsIgnoreCase("true"); + // if (runMinimalMemory) + // getMessageHandler().handleMessage(MessageUtil.info( + // "[runMinimalMemory=true] Optimizing bcel processing (and cost of performance) to use less memory" + // )); + + s = p.getProperty(xsetDEBUG_STRUCTURAL_CHANGES_CODE, "false"); + forDEBUG_structuralChangesCode = s.equalsIgnoreCase("true"); + + s = p.getProperty(xsetDEBUG_BRIDGING, "false"); + forDEBUG_bridgingCode = s.equalsIgnoreCase("true"); + + } + checkedAdvancedConfiguration = true; + } + } + + public boolean isRunMinimalMemory() { + ensureAdvancedConfigurationProcessed(); + return runMinimalMemory; + } + + public boolean shouldFastPackMethods() { + ensureAdvancedConfigurationProcessed(); + return fastMethodPacking; + } + + public boolean shouldPipelineCompilation() { + ensureAdvancedConfigurationProcessed(); + return shouldPipelineCompilation; + } + + public boolean shouldGenerateStackMaps() { + ensureAdvancedConfigurationProcessed(); + return shouldGenerateStackMaps; + } + + public void setIncrementalCompileCouldFollow(boolean b) { + incrementalCompileCouldFollow = b; + } + + public boolean couldIncrementalCompileFollow() { + return incrementalCompileCouldFollow; + } + + public void setSynchronizationPointcutsInUse() { + if (trace.isTraceEnabled()) + trace.enter("setSynchronizationPointcutsInUse", this); + synchronizationPointcutsInUse = true; + if (trace.isTraceEnabled()) + trace.exit("setSynchronizationPointcutsInUse"); + } + + public boolean areSynchronizationPointcutsInUse() { + return synchronizationPointcutsInUse; + } + + /** + * Register a new pointcut designator handler with the world - this can be used by any pointcut parsers attached to the world. + * + * @param designatorHandler handler for the new pointcut + */ + public void registerPointcutHandler(PointcutDesignatorHandler designatorHandler) { + if (pointcutDesignators == null) + pointcutDesignators = new HashSet(); + pointcutDesignators.add(designatorHandler); + } + + public Set getRegisteredPointcutHandlers() { + if (pointcutDesignators == null) + return Collections.EMPTY_SET; + return pointcutDesignators; + } + + public void reportMatch(ShadowMunger munger, Shadow shadow) { + + } + + public void reportCheckerMatch(Checker checker, Shadow shadow) { + } + +} \ No newline at end of file diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/XlintDefault.properties b/org.aspectj.matcher/src/org/aspectj/weaver/XlintDefault.properties new file mode 100644 index 000000000..f88f24e7a --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/XlintDefault.properties @@ -0,0 +1,47 @@ +invalidAbsoluteTypeName = warning +invalidWildcardTypeName = ignore + +unresolvableMember = warning + +typeNotExposedToWeaver = warning + +shadowNotInStructure = ignore + +unmatchedSuperTypeInCall = warning + +canNotImplementLazyTjp = ignore +multipleAdviceStoppingLazyTjp=ignore +noGuardForLazyTjp=ignore + +uncheckedAdviceConversion = warning + +needsSerialVersionUIDField = ignore +brokeSerialVersionCompatibility = ignore + +noInterfaceCtorJoinpoint = warning + +noJoinpointsForBridgeMethods = warning +cantMatchArrayTypeOnVarargs = ignore +enumAsTargetForDecpIgnored = warning +annotationAsTargetForDecpIgnored = warning +adviceDidNotMatch = warning +invalidTargetForAnnotation = warning +elementAlreadyAnnotated = warning +runtimeExceptionNotSoftened = warning +uncheckedArgument = warning +noExplicitConstructorCall = warning + +aspectExcludedByConfiguration = ignore + +unmatchedTargetKind = warning + +cantFindType = error +cantFindTypeAffectingJPMatch = warning + +unorderedAdviceAtShadow=ignore +swallowedExceptionInCatchBlock=ignore +calculatingSerialVersionUID=ignore +advisingSynchronizedMethods=warning +mustWeaveXmlDefinedAspects=warning + +missingAspectForReweaving=error \ No newline at end of file diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/ast/ASTNode.java b/org.aspectj.matcher/src/org/aspectj/weaver/ast/ASTNode.java new file mode 100644 index 000000000..7e8b08347 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/ast/ASTNode.java @@ -0,0 +1,22 @@ +/* ******************************************************************* + * 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.ast; + +public abstract class ASTNode { + + public ASTNode() { + super(); + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/ast/And.java b/org.aspectj.matcher/src/org/aspectj/weaver/ast/And.java new file mode 100644 index 000000000..8679d48d0 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/ast/And.java @@ -0,0 +1,51 @@ +/* ******************************************************************* + * 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.ast; + + +public class And extends Test { + Test left, right; + + public And(Test left, Test right) { + super(); + this.left = left; + this.right = right; + } + + public void accept(ITestVisitor v) { + v.visit(this); + } + + public String toString() { + return "(" + left + " && " + right + ")"; + } + + public boolean equals(Object other) { + if (other instanceof And) { + And o = (And) other; + return o.left.equals(left) && o.right.equals(right); + } else { + return false; + } + } + + public Test getLeft() { + return left; + } + + public Test getRight() { + return right; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/ast/Call.java b/org.aspectj.matcher/src/org/aspectj/weaver/ast/Call.java new file mode 100644 index 000000000..1a1b52abe --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/ast/Call.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.ast; + +import org.aspectj.weaver.Member; + +public class Call extends Test { + // assert m.return value is boolean + private final Member method; + private final Expr[] args; + + public Call(Member m, Expr[] args) { + super(); + this.method = m; + this.args = args; + } + + public void accept(ITestVisitor v) { + v.visit(this); + } + + public Expr[] getArgs() { + return args; + } + + public Member getMethod() { + return method; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/ast/CallExpr.java b/org.aspectj.matcher/src/org/aspectj/weaver/ast/CallExpr.java new file mode 100644 index 000000000..e8191a42e --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/ast/CallExpr.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.ast; + +import org.aspectj.weaver.Member; +import org.aspectj.weaver.ResolvedType; + +public class CallExpr extends Expr { + // assert m.return value is boolean + private final Member method; + private final Expr[] args; + private final ResolvedType returnType; // yes, stored in method as well, but that one isn't resolved + + public CallExpr(Member m, Expr[] args, ResolvedType returnType) { + super(); + this.method = m; + this.args = args; + this.returnType = returnType; + } + + public void accept(IExprVisitor v) { + v.visit(this); + } + + public Expr[] getArgs() { + return args; + } + + public Member getMethod() { + return method; + } + + public ResolvedType getType() { + return returnType; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/ast/Expr.java b/org.aspectj.matcher/src/org/aspectj/weaver/ast/Expr.java new file mode 100644 index 000000000..1b22c8f42 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/ast/Expr.java @@ -0,0 +1,34 @@ +/* ******************************************************************* + * 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.ast; + +import org.aspectj.weaver.Member; +import org.aspectj.weaver.ResolvedType; + +public abstract class Expr extends ASTNode { + + public Expr() { + super(); + } + + public static final Expr[] NONE = new Expr[0]; + + public abstract void accept(IExprVisitor v); + + public abstract ResolvedType getType(); + + public static CallExpr makeCallExpr(Member member, Expr[] exprs, ResolvedType returnType) { + return new CallExpr(member, exprs, returnType); + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/ast/FieldGet.java b/org.aspectj.matcher/src/org/aspectj/weaver/ast/FieldGet.java new file mode 100644 index 000000000..2e145e3c2 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/ast/FieldGet.java @@ -0,0 +1,44 @@ +/* ******************************************************************* + * 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.ast; + +import org.aspectj.weaver.Member; +import org.aspectj.weaver.ResolvedType; + +public class FieldGet extends Expr { + Member field; + ResolvedType resolvedType; + + public FieldGet(Member field, ResolvedType resolvedType) { + super(); + this.field = field; + this.resolvedType = resolvedType; + } + + public ResolvedType getType() { + return resolvedType; + } + + public String toString() { + return "(FieldGet " + field + ")"; + } + + public void accept(IExprVisitor v) { + v.visit(this); + } + + public Member getField() { + return field; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/ast/FieldGetCall.java b/org.aspectj.matcher/src/org/aspectj/weaver/ast/FieldGetCall.java new file mode 100644 index 000000000..64aaf4b1a --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/ast/FieldGetCall.java @@ -0,0 +1,47 @@ +/* ******************************************************************* + * 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.ast; + +import org.aspectj.weaver.Member; + +public class FieldGetCall extends Test { + // assert m.return value is boolean + private final Member field; + private final Member method; + private final Expr[] args; + + public FieldGetCall(Member f, Member m, Expr[] args) { + super(); + this.field = f; + this.method = m; + this.args = args; + } + + public void accept(ITestVisitor v) { + v.visit(this); + } + + public Expr[] getArgs() { + return args; + } + + public Member getMethod() { + return method; + } + + public Member getField() { + return field; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/ast/HasAnnotation.java b/org.aspectj.matcher/src/org/aspectj/weaver/ast/HasAnnotation.java new file mode 100644 index 000000000..885e9d083 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/ast/HasAnnotation.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * 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.ast; + +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; + +public class HasAnnotation extends Test { + + private Var v; + private ResolvedType annType; + + public HasAnnotation(Var v, ResolvedType annType) { + super(); + this.v = v; + this.annType = annType; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.ast.Test#accept(org.aspectj.weaver.ast.ITestVisitor) + */ + public void accept(ITestVisitor v) { + v.visit(this); + } + + public String toString() { + return "(" + v + " has annotation @" + annType + ")"; + } + + public boolean equals(Object other) { + if (other instanceof HasAnnotation) { + HasAnnotation o = (HasAnnotation) other; + return o.v.equals(v) && o.annType.equals(annType); + } else { + return false; + } + } + + public int hashCode() { + return v.hashCode()*37+annType.hashCode(); + } + + public Var getVar() { + return v; + } + + public UnresolvedType getAnnotationType() { + return annType; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/ast/IExprVisitor.java b/org.aspectj.matcher/src/org/aspectj/weaver/ast/IExprVisitor.java new file mode 100644 index 000000000..89e0b3d74 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/ast/IExprVisitor.java @@ -0,0 +1,23 @@ +/* ******************************************************************* + * 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.ast; + +public interface IExprVisitor { + + void visit(Var i); + + void visit(FieldGet fieldGet); + + void visit(CallExpr callExpr); + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/ast/ITestVisitor.java b/org.aspectj.matcher/src/org/aspectj/weaver/ast/ITestVisitor.java new file mode 100644 index 000000000..fc99272af --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/ast/ITestVisitor.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.ast; + +import org.aspectj.weaver.internal.tools.MatchingContextBasedTest; + + +public interface ITestVisitor { + + void visit(And e); + void visit(Instanceof i); + void visit(Not not); + void visit(Or or); + void visit(Literal literal); + void visit(Call call); + void visit(FieldGetCall fieldGetCall); + void visit(HasAnnotation hasAnnotation); + void visit(MatchingContextBasedTest matchingContextTest); + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/ast/Instanceof.java b/org.aspectj.matcher/src/org/aspectj/weaver/ast/Instanceof.java new file mode 100644 index 000000000..feec6a2be --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/ast/Instanceof.java @@ -0,0 +1,56 @@ +/* ******************************************************************* + * 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.ast; + +import org.aspectj.weaver.UnresolvedType; + +public class Instanceof extends Test { + Var var; + UnresolvedType type; + + public Instanceof(Var left, UnresolvedType right) { + super(); + this.var = left; + this.type = right; + } + + public void accept(ITestVisitor v) { + v.visit(this); + } + + public String toString() { + return "(" + var + " instanceof " + type + ")"; + } + + public boolean equals(Object other) { + if (other instanceof Instanceof) { + Instanceof o = (Instanceof) other; + return o.var.equals(var) && o.type.equals(type); + } else { + return false; + } + } + + public int hashCode() { + return var.hashCode()*37+type.hashCode(); + } + + public Var getVar() { + return var; + } + + public UnresolvedType getType() { + return type; + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/ast/Literal.java b/org.aspectj.matcher/src/org/aspectj/weaver/ast/Literal.java new file mode 100644 index 000000000..bb7968eb5 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/ast/Literal.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.ast; + + +public final class Literal extends Test { + + boolean noTest; + boolean val; + + private Literal(boolean val, boolean noTest) { + super(); + this.val = val; + this.noTest = noTest; + } + + public void accept(ITestVisitor v) { + v.visit(this); + } + + public static final Literal TRUE = new Literal(true, false); + public static final Literal FALSE = new Literal(false, false); +// public static final Literal NO_TEST = new Literal(false, true); + + public String toString() { + return noTest ? "NO_TEST" : val ? "TRUE" : "FALSE"; + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/ast/Not.java b/org.aspectj.matcher/src/org/aspectj/weaver/ast/Not.java new file mode 100644 index 000000000..366a193a4 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/ast/Not.java @@ -0,0 +1,49 @@ +/* ******************************************************************* + * 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.ast; + + +public class Not extends Test { + Test test; + + public Not(Test test) { + super(); + this.test = test; + } + + public void accept(ITestVisitor v) { + v.visit(this); + } + + public Test getBody() { + return test; + } + + public String toString() { + return "!" + test; + } + + public boolean equals(Object other) { + if (other instanceof Not) { + Not o = (Not) other; + return o.test.equals(test); + } else { + return false; + } + } + + public int hashCode() { + return test.hashCode(); + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/ast/Or.java b/org.aspectj.matcher/src/org/aspectj/weaver/ast/Or.java new file mode 100644 index 000000000..b3143df73 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/ast/Or.java @@ -0,0 +1,56 @@ +/* ******************************************************************* + * 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.ast; + +public class Or extends Test { + Test left, right; + + public Or(Test left, Test right) { + super(); + this.left = left; + this.right = right; + } + + public void accept(ITestVisitor v) { + v.visit(this); + } + + public String toString() { + return "(" + left + " || " + right + ")"; + } + + public boolean equals(Object other) { + if (other instanceof Or) { + Or o = (Or) other; + return o.left.equals(left) && o.right.equals(right); + } else { + return false; + } + } + + public int hashCode() { + int result = 19; + result = 37*result + left.hashCode(); + result = 37*result + right.hashCode(); + return result; + } + + public Test getLeft() { + return left; + } + + public Test getRight() { + return right; + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/ast/Test.java b/org.aspectj.matcher/src/org/aspectj/weaver/ast/Test.java new file mode 100644 index 000000000..8da7694e6 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/ast/Test.java @@ -0,0 +1,95 @@ +/* ******************************************************************* + * 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.ast; + +import org.aspectj.weaver.Member; +import org.aspectj.weaver.ResolvedType; + +public abstract class Test extends ASTNode { + + public Test() { + super(); + } + + public abstract void accept(ITestVisitor v); + + public static Test makeAnd(Test a, Test b) { +// if (a == Literal.NO_TEST) return b; +// if (b == Literal.NO_TEST) return a; + if (a == Literal.TRUE) { + if (b == Literal.TRUE) { + return a; + } else { + return b; + } + } else if (b == Literal.TRUE) { + return a; + } else if (a == Literal.FALSE || b == Literal.FALSE) { + return Literal.FALSE; + } else { + return new And(a, b); + } + } + + public static Test makeOr(Test a, Test b) { +// if (a == Literal.NO_TEST) return a; +// if (b == Literal.NO_TEST) return b; + if (a == Literal.FALSE) { + return b; + } else if (b == Literal.FALSE) { + return a; + } else if (a == Literal.TRUE || b == Literal.TRUE) { + return Literal.TRUE; + } else { + return new Or(a, b); + } + } + + public static Test makeNot(Test a) { + if (a instanceof Not) { + return ((Not) a).getBody(); + } else if (a == Literal.TRUE) { + return Literal.FALSE; + } else if (a == Literal.FALSE) { + return Literal.TRUE; +// } else if (a == Literal.NO_TEST) { +// return a; + } else { + return new Not(a); + } + } + + // uses our special rules that anything matches object + public static Test makeInstanceof(Var v, ResolvedType ty) { + if (ty.equals(ResolvedType.OBJECT)) return Literal.TRUE; + + Test e; + if (ty.isAssignableFrom(v.getType())) e = Literal.TRUE; + else if (! ty.isCoerceableFrom(v.getType())) e = Literal.FALSE; + else e = new Instanceof(v, ty); + return e; + } + + public static Test makeHasAnnotation(Var v, ResolvedType annTy) { + return new HasAnnotation(v,annTy); + } + + public static Test makeCall(Member m, Expr[] args) { + return new Call(m, args); + } + public static Test makeFieldGetCall(Member f, Member m, Expr[] args) { + return new FieldGetCall(f, m, args); + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/ast/Var.java b/org.aspectj.matcher/src/org/aspectj/weaver/ast/Var.java new file mode 100644 index 000000000..230cfeac6 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/ast/Var.java @@ -0,0 +1,47 @@ +/* ******************************************************************* + * 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.ast; + +import org.aspectj.weaver.ResolvedType; + +public class Var extends Expr { + ResolvedType variableType; + + public Var(ResolvedType variableType) { + super(); + this.variableType = variableType; + } + + public ResolvedType getType() { + return variableType; + } + + public String toString() { + return "(Var " + variableType + ")"; + } + + public void accept(IExprVisitor v) { + v.visit(this); + } + + /** + * For an annotation this will return a variable that can access a specific field of the annotation (of the specified type) TODO + * what kind of behaviour happens for two annotation fields of the same type? + * + * @param formalType + * @return + */ + public Var getAccessorForValue(ResolvedType formalType) { + throw new IllegalStateException("Only makes sense for annotation variables"); + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/internal/tools/MatchingContextBasedTest.java b/org.aspectj.matcher/src/org/aspectj/weaver/internal/tools/MatchingContextBasedTest.java new file mode 100644 index 000000000..b5a78e0e6 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/internal/tools/MatchingContextBasedTest.java @@ -0,0 +1,43 @@ +/* ******************************************************************* + * 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.internal.tools; + +import org.aspectj.weaver.ast.ITestVisitor; +import org.aspectj.weaver.ast.Test; +import org.aspectj.weaver.tools.MatchingContext; +import org.aspectj.weaver.tools.ContextBasedMatcher; + +/** + * Test that uses MatchingContext to match (or not) + * + */ +public class MatchingContextBasedTest extends Test { + + private final ContextBasedMatcher matcher; + + public MatchingContextBasedTest(ContextBasedMatcher pc) { + this.matcher = pc; + } + + + /* (non-Javadoc) + * @see org.aspectj.weaver.ast.Test#accept(org.aspectj.weaver.ast.ITestVisitor) + */ + public void accept(ITestVisitor v) { + v.visit(this); + } + + public boolean matches(MatchingContext context) { + return this.matcher.matchesDynamically(context); + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/internal/tools/PointcutDesignatorHandlerBasedPointcut.java b/org.aspectj.matcher/src/org/aspectj/weaver/internal/tools/PointcutDesignatorHandlerBasedPointcut.java new file mode 100644 index 000000000..04b1554eb --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/internal/tools/PointcutDesignatorHandlerBasedPointcut.java @@ -0,0 +1,157 @@ +/* ******************************************************************* + * 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.internal.tools; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Test; +import org.aspectj.weaver.patterns.Bindings; +import org.aspectj.weaver.patterns.ExposedState; +import org.aspectj.weaver.patterns.FastMatchInfo; +import org.aspectj.weaver.patterns.IScope; +import org.aspectj.weaver.patterns.PatternNodeVisitor; +import org.aspectj.weaver.patterns.Pointcut; +import org.aspectj.weaver.reflect.ReflectionFastMatchInfo; +import org.aspectj.weaver.reflect.ReflectionShadow; +import org.aspectj.weaver.reflect.ReflectionWorld; +import org.aspectj.weaver.tools.ContextBasedMatcher; +import org.aspectj.weaver.tools.MatchingContext; + +/** + * Implementation of Pointcut that is backed by a user-extension + * pointcut designator handler. + * + */ +public class PointcutDesignatorHandlerBasedPointcut extends Pointcut{ + + private final ContextBasedMatcher matcher; + private final ReflectionWorld world; + + public PointcutDesignatorHandlerBasedPointcut( + ContextBasedMatcher expr, + ReflectionWorld world) { + this.matcher = expr; + this.world = world; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.Pointcut#getPointcutKind() + */ + public byte getPointcutKind() { + return Pointcut.USER_EXTENSION; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.Pointcut#fastMatch(org.aspectj.weaver.patterns.FastMatchInfo) + */ + public FuzzyBoolean fastMatch(FastMatchInfo info) { + if (info instanceof ReflectionFastMatchInfo) { + try { + return FuzzyBoolean.fromBoolean( + this.matcher.couldMatchJoinPointsInType( + Class.forName(info.getType().getName(),false,world.getClassLoader()), + ((ReflectionFastMatchInfo)info).getMatchingContext()) + ); + } catch (ClassNotFoundException cnfEx) { + return FuzzyBoolean.MAYBE; + } + } + throw new IllegalStateException("Can only match user-extension pcds against Reflection FastMatchInfo objects"); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.Pointcut#couldMatchKinds() + */ + public int couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS_BITS; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.Pointcut#matchInternal(org.aspectj.weaver.Shadow) + */ + protected FuzzyBoolean matchInternal(Shadow shadow) { + if (shadow instanceof ReflectionShadow) { + MatchingContext context = ((ReflectionShadow)shadow).getMatchingContext(); + org.aspectj.weaver.tools.FuzzyBoolean match = + this.matcher.matchesStatically(context); + if (match == org.aspectj.weaver.tools.FuzzyBoolean.MAYBE) { + return FuzzyBoolean.MAYBE; + } else if (match == org.aspectj.weaver.tools.FuzzyBoolean.YES) { + return FuzzyBoolean.YES; + } else if (match == org.aspectj.weaver.tools.FuzzyBoolean.NO) { + return FuzzyBoolean.NO; + } + } + throw new IllegalStateException("Can only match user-extension pcds against Reflection shadows (not BCEL)"); + } + + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.Pointcut#resolveBindings(org.aspectj.weaver.patterns.IScope, org.aspectj.weaver.patterns.Bindings) + */ + protected void resolveBindings(IScope scope, Bindings bindings) { + // no-op + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.Pointcut#concretize1(org.aspectj.weaver.ResolvedType, org.aspectj.weaver.ResolvedType, org.aspectj.weaver.IntMap) + */ + protected Pointcut concretize1(ResolvedType inAspect, + ResolvedType declaringType, IntMap bindings) { + return this; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.Pointcut#findResidueInternal(org.aspectj.weaver.Shadow, org.aspectj.weaver.patterns.ExposedState) + */ + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + if (!this.matcher.mayNeedDynamicTest()) { + return Literal.TRUE; + } else { + // could be more efficient here! + matchInternal(shadow); + return new MatchingContextBasedTest(this.matcher); + } + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.Pointcut#parameterizeWith(java.util.Map) + */ + public Pointcut parameterizeWith(Map typeVariableMap,World w) { + return this; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.PatternNode#write(java.io.DataOutputStream) + */ + public void write(DataOutputStream s) throws IOException { + throw new UnsupportedOperationException("can't write custom pointcut designator expressions to stream"); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.PatternNode#accept(org.aspectj.weaver.patterns.PatternNodeVisitor, java.lang.Object) + */ + public Object accept(PatternNodeVisitor visitor, Object data) { + //visitor.visit(this); + // no-op? + return data; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/internal/tools/PointcutExpressionImpl.java b/org.aspectj.matcher/src/org/aspectj/weaver/internal/tools/PointcutExpressionImpl.java new file mode 100644 index 000000000..29f8e027c --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/internal/tools/PointcutExpressionImpl.java @@ -0,0 +1,343 @@ +/* ******************************************************************* + * 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.internal.tools; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; + +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Test; +import org.aspectj.weaver.patterns.AbstractPatternNodeVisitor; +import org.aspectj.weaver.patterns.AnnotationPointcut; +import org.aspectj.weaver.patterns.ArgsAnnotationPointcut; +import org.aspectj.weaver.patterns.ArgsPointcut; +import org.aspectj.weaver.patterns.CflowPointcut; +import org.aspectj.weaver.patterns.ExposedState; +import org.aspectj.weaver.patterns.IfPointcut; +import org.aspectj.weaver.patterns.NotAnnotationTypePattern; +import org.aspectj.weaver.patterns.NotPointcut; +import org.aspectj.weaver.patterns.Pointcut; +import org.aspectj.weaver.patterns.ThisOrTargetAnnotationPointcut; +import org.aspectj.weaver.patterns.ThisOrTargetPointcut; +import org.aspectj.weaver.patterns.WithinAnnotationPointcut; +import org.aspectj.weaver.patterns.WithinCodeAnnotationPointcut; +import org.aspectj.weaver.reflect.ReflectionFastMatchInfo; +import org.aspectj.weaver.reflect.ReflectionShadow; +import org.aspectj.weaver.reflect.ShadowMatchImpl; +import org.aspectj.weaver.tools.DefaultMatchingContext; +import org.aspectj.weaver.tools.MatchingContext; +import org.aspectj.weaver.tools.PointcutExpression; +import org.aspectj.weaver.tools.PointcutParameter; +import org.aspectj.weaver.tools.ShadowMatch; + +/** + * Map from weaver.tools interface to internal Pointcut implementation... + */ +public class PointcutExpressionImpl implements PointcutExpression { + + private World world; + private Pointcut pointcut; + private String expression; + private PointcutParameter[] parameters; + private MatchingContext matchContext = new DefaultMatchingContext(); + + public PointcutExpressionImpl(Pointcut pointcut, String expression, PointcutParameter[] params, World inWorld) { + this.pointcut = pointcut; + this.expression = expression; + this.world = inWorld; + this.parameters = params; + if (this.parameters == null) this.parameters = new PointcutParameter[0]; + } + + public Pointcut getUnderlyingPointcut() { + return this.pointcut; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.tools.PointcutExpression#setMatchingContext(org.aspectj.weaver.tools.MatchingContext) + */ + public void setMatchingContext(MatchingContext aMatchContext) { + this.matchContext = aMatchContext; + } + + public boolean couldMatchJoinPointsInType(Class aClass) { + ResolvedType matchType = world.resolve(aClass.getName()); + ReflectionFastMatchInfo info = new ReflectionFastMatchInfo(matchType,null,this.matchContext); + return pointcut.fastMatch(info).maybeTrue(); + } + + public boolean mayNeedDynamicTest() { + HasPossibleDynamicContentVisitor visitor = new HasPossibleDynamicContentVisitor(); + pointcut.traverse(visitor, null); + return visitor.hasDynamicContent(); + } + + private ExposedState getExposedState() { + return new ExposedState(parameters.length); + } + + public ShadowMatch matchesMethodExecution(Method aMethod) { + return matchesExecution(aMethod); + } + + public ShadowMatch matchesConstructorExecution(Constructor aConstructor) { + return matchesExecution(aConstructor); + } + + private ShadowMatch matchesExecution(Member aMember) { + Shadow s = ReflectionShadow.makeExecutionShadow(world, aMember,this.matchContext); + ShadowMatchImpl sm = getShadowMatch(s); + sm.setSubject(aMember); + sm.setWithinCode(null); + sm.setWithinType(aMember.getDeclaringClass()); + return sm; + } + + public ShadowMatch matchesStaticInitialization(Class aClass) { + Shadow s = ReflectionShadow.makeStaticInitializationShadow(world, aClass,this.matchContext); + ShadowMatchImpl sm = getShadowMatch(s); + sm.setSubject(null); + sm.setWithinCode(null); + sm.setWithinType(aClass); + return sm; + } + + public ShadowMatch matchesAdviceExecution(Method aMethod) { + Shadow s = ReflectionShadow.makeAdviceExecutionShadow(world, aMethod,this.matchContext); + ShadowMatchImpl sm = getShadowMatch(s); + sm.setSubject(aMethod); + sm.setWithinCode(null); + sm.setWithinType(aMethod.getDeclaringClass()); + return sm; + } + + public ShadowMatch matchesInitialization(Constructor aConstructor) { + Shadow s = ReflectionShadow.makeInitializationShadow(world, aConstructor,this.matchContext); + ShadowMatchImpl sm = getShadowMatch(s); + sm.setSubject(aConstructor); + sm.setWithinCode(null); + sm.setWithinType(aConstructor.getDeclaringClass()); + return sm; + } + + public ShadowMatch matchesPreInitialization(Constructor aConstructor) { + Shadow s = ReflectionShadow.makePreInitializationShadow(world, aConstructor,this.matchContext); + ShadowMatchImpl sm = getShadowMatch(s); + sm.setSubject(aConstructor); + sm.setWithinCode(null); + sm.setWithinType(aConstructor.getDeclaringClass()); + return sm; + } + + public ShadowMatch matchesMethodCall(Method aMethod, Member withinCode) { + Shadow s = ReflectionShadow.makeCallShadow(world, aMethod, withinCode,this.matchContext); + ShadowMatchImpl sm = getShadowMatch(s); + sm.setSubject(aMethod); + sm.setWithinCode(withinCode); + sm.setWithinType(withinCode.getDeclaringClass()); + return sm; + } + + public ShadowMatch matchesMethodCall(Method aMethod, Class callerType) { + Shadow s = ReflectionShadow.makeCallShadow(world, aMethod, callerType,this.matchContext); + ShadowMatchImpl sm = getShadowMatch(s); + sm.setSubject(aMethod); + sm.setWithinCode(null); + sm.setWithinType(callerType); + return sm; + } + + public ShadowMatch matchesConstructorCall(Constructor aConstructor, Class callerType) { + Shadow s = ReflectionShadow.makeCallShadow(world, aConstructor, callerType,this.matchContext); + ShadowMatchImpl sm = getShadowMatch(s); + sm.setSubject(aConstructor); + sm.setWithinCode(null); + sm.setWithinType(callerType); + return sm; + } + + public ShadowMatch matchesConstructorCall(Constructor aConstructor, Member withinCode) { + Shadow s = ReflectionShadow.makeCallShadow(world, aConstructor,withinCode,this.matchContext); + ShadowMatchImpl sm = getShadowMatch(s); + sm.setSubject(aConstructor); + sm.setWithinCode(withinCode); + sm.setWithinType(withinCode.getDeclaringClass()); + return sm; + } + + public ShadowMatch matchesHandler(Class exceptionType, Class handlingType) { + Shadow s = ReflectionShadow.makeHandlerShadow(world,exceptionType,handlingType,this.matchContext); + ShadowMatchImpl sm = getShadowMatch(s); + sm.setSubject(null); + sm.setWithinCode(null); + sm.setWithinType(handlingType); + return sm; + } + + public ShadowMatch matchesHandler(Class exceptionType, Member withinCode) { + Shadow s = ReflectionShadow.makeHandlerShadow(world,exceptionType,withinCode,this.matchContext); + ShadowMatchImpl sm = getShadowMatch(s); + sm.setSubject(null); + sm.setWithinCode(withinCode); + sm.setWithinType(withinCode.getDeclaringClass()); + return sm; + } + + public ShadowMatch matchesFieldGet(Field aField, Class withinType) { + Shadow s = ReflectionShadow.makeFieldGetShadow(world, aField, withinType,this.matchContext); + ShadowMatchImpl sm = getShadowMatch(s); + sm.setSubject(aField); + sm.setWithinCode(null); + sm.setWithinType(withinType); + return sm; + } + + public ShadowMatch matchesFieldGet(Field aField, Member withinCode) { + Shadow s = ReflectionShadow.makeFieldGetShadow(world, aField, withinCode,this.matchContext); + ShadowMatchImpl sm = getShadowMatch(s); + sm.setSubject(aField); + sm.setWithinCode(withinCode); + sm.setWithinType(withinCode.getDeclaringClass()); + return sm; + } + + public ShadowMatch matchesFieldSet(Field aField, Class withinType) { + Shadow s = ReflectionShadow.makeFieldSetShadow(world, aField, withinType,this.matchContext); + ShadowMatchImpl sm = getShadowMatch(s); + sm.setSubject(aField); + sm.setWithinCode(null); + sm.setWithinType(withinType); + return sm; + } + + public ShadowMatch matchesFieldSet(Field aField, Member withinCode) { + Shadow s = ReflectionShadow.makeFieldSetShadow(world, aField, withinCode,this.matchContext); + ShadowMatchImpl sm = getShadowMatch(s); + sm.setSubject(aField); + sm.setWithinCode(withinCode); + sm.setWithinType(withinCode.getDeclaringClass()); + return sm; + } + + private ShadowMatchImpl getShadowMatch(Shadow forShadow) { + org.aspectj.util.FuzzyBoolean match = pointcut.match(forShadow); + Test residueTest = Literal.TRUE; + ExposedState state = getExposedState(); + if (match.maybeTrue()) { + residueTest = pointcut.findResidue(forShadow, state); + } + ShadowMatchImpl sm = new ShadowMatchImpl(match,residueTest,state,parameters); + sm.setMatchingContext(this.matchContext); + return sm; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.tools.PointcutExpression#getPointcutExpression() + */ + public String getPointcutExpression() { + return expression; + } + + private static class HasPossibleDynamicContentVisitor extends AbstractPatternNodeVisitor { + private boolean hasDynamicContent = false; + + public boolean hasDynamicContent() { return hasDynamicContent; } + + public Object visit(WithinAnnotationPointcut node, Object data) { + hasDynamicContent = true; + return null; + } + + public Object visit(WithinCodeAnnotationPointcut node, Object data) { + hasDynamicContent = true; + return null; + } + + public Object visit(AnnotationPointcut node, Object data) { + hasDynamicContent = true; + return null; + } + + public Object visit(ArgsAnnotationPointcut node, Object data) { + hasDynamicContent = true; + return null; + } + + public Object visit(ArgsPointcut node, Object data) { + hasDynamicContent = true; + return null; + } + + public Object visit(CflowPointcut node, Object data) { + hasDynamicContent = true; + return null; + } + + public Object visit(IfPointcut node, Object data) { + hasDynamicContent = true; + return null; + } + + public Object visit(NotAnnotationTypePattern node, Object data) { + return node.getNegatedPattern().accept(this, data); + } + + public Object visit(NotPointcut node, Object data) { + return node.getNegatedPointcut().accept(this, data); + } + + public Object visit(ThisOrTargetAnnotationPointcut node, Object data) { + hasDynamicContent = true; + return null; + } + + public Object visit(ThisOrTargetPointcut node, Object data) { + hasDynamicContent = true; + return null; + } + + } + + public static class Handler implements Member { + + private Class decClass; + private Class exType; + + public Handler(Class decClass, Class exType) { + this.decClass = decClass; + this.exType = exType; + } + + public int getModifiers() { + return 0; + } + + public Class getDeclaringClass() { + return decClass; + } + + public String getName() { + return null; + } + + public Class getHandledExceptionType() { + return exType; + } + + public boolean isSynthetic() { + return false; + } + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/internal/tools/TypePatternMatcherImpl.java b/org.aspectj.matcher/src/org/aspectj/weaver/internal/tools/TypePatternMatcherImpl.java new file mode 100644 index 000000000..8ce4bceea --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/internal/tools/TypePatternMatcherImpl.java @@ -0,0 +1,34 @@ +/* ******************************************************************* + * 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.internal.tools; + +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.World; +import org.aspectj.weaver.patterns.TypePattern; +import org.aspectj.weaver.reflect.ReflectionBasedReferenceTypeDelegateFactory; +import org.aspectj.weaver.tools.TypePatternMatcher; + +public class TypePatternMatcherImpl implements TypePatternMatcher { + + private final TypePattern pattern; + private final World world; + + public TypePatternMatcherImpl(TypePattern pattern, World world) { + this.pattern = pattern; + this.world = world; + } + + public boolean matches(Class aClass) { + ResolvedType rt = + ReflectionBasedReferenceTypeDelegateFactory.resolveTypeInWorld(aClass,world); + return pattern.matchesStatically(rt); + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/AbstractPatternNodeVisitor.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/AbstractPatternNodeVisitor.java new file mode 100644 index 000000000..14234438c --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/AbstractPatternNodeVisitor.java @@ -0,0 +1,467 @@ +/* ******************************************************************* + * 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.patterns; + +import org.aspectj.weaver.patterns.Pointcut.MatchesNothingPointcut; + +/** + * @author colyer + * + */ +public abstract class AbstractPatternNodeVisitor implements PatternNodeVisitor { + + public Object visit(AnyTypePattern node, Object data) { + return node; + } + + public Object visit(NoTypePattern node, Object data) { + return node; + } + + public Object visit(EllipsisTypePattern node, Object data) { + return node; + } + + public Object visit(AnyWithAnnotationTypePattern node, Object data) { + return node; + } + + public Object visit(AnyAnnotationTypePattern node, Object data) { + return node; + } + + public Object visit(EllipsisAnnotationTypePattern node, Object data) { + return node; + } + + public Object visit(AndAnnotationTypePattern node, Object data) { + return node; + } + + public Object visit(AndPointcut node, Object data) { + return node; + } + + public Object visit(AndTypePattern node, Object data) { + return node; + } + + public Object visit(AnnotationPatternList node, Object data) { + return node; + } + + public Object visit(AnnotationPointcut node, Object data) { + return node; + } + + public Object visit(ArgsAnnotationPointcut node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.ArgsPointcut, java.lang.Object) + */ + public Object visit(ArgsPointcut node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.BindingAnnotationTypePattern, + * java.lang.Object) + */ + public Object visit(BindingAnnotationTypePattern node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.BindingTypePattern, java.lang.Object) + */ + public Object visit(BindingTypePattern node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.CflowPointcut, java.lang.Object) + */ + public Object visit(CflowPointcut node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.ConcreteCflowPointcut, java.lang.Object) + */ + public Object visit(ConcreteCflowPointcut node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.DeclareAnnotation, java.lang.Object) + */ + public Object visit(DeclareAnnotation node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.DeclareErrorOrWarning, java.lang.Object) + */ + public Object visit(DeclareErrorOrWarning node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.DeclareParents, java.lang.Object) + */ + public Object visit(DeclareParents node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.DeclarePrecedence, java.lang.Object) + */ + public Object visit(DeclarePrecedence node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.DeclareSoft, java.lang.Object) + */ + public Object visit(DeclareSoft node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.ExactAnnotationTypePattern, + * java.lang.Object) + */ + public Object visit(ExactAnnotationTypePattern node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.ExactTypePattern, java.lang.Object) + */ + public Object visit(ExactTypePattern node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.HandlerPointcut, java.lang.Object) + */ + public Object visit(HandlerPointcut node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.IfPointcut, java.lang.Object) + */ + public Object visit(IfPointcut node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.KindedPointcut, java.lang.Object) + */ + public Object visit(KindedPointcut node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.ModifiersPattern, java.lang.Object) + */ + public Object visit(ModifiersPattern node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.NamePattern, java.lang.Object) + */ + public Object visit(NamePattern node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.NotAnnotationTypePattern, + * java.lang.Object) + */ + public Object visit(NotAnnotationTypePattern node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.NotPointcut, java.lang.Object) + */ + public Object visit(NotPointcut node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.NotTypePattern, java.lang.Object) + */ + public Object visit(NotTypePattern node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.OrAnnotationTypePattern, java.lang.Object) + */ + public Object visit(OrAnnotationTypePattern node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.OrPointcut, java.lang.Object) + */ + public Object visit(OrPointcut node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.OrTypePattern, java.lang.Object) + */ + public Object visit(OrTypePattern node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.PerCflow, java.lang.Object) + */ + public Object visit(PerCflow node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.PerFromSuper, java.lang.Object) + */ + public Object visit(PerFromSuper node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.PerObject, java.lang.Object) + */ + public Object visit(PerObject node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.PerSingleton, java.lang.Object) + */ + public Object visit(PerSingleton node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.PerTypeWithin, java.lang.Object) + */ + public Object visit(PerTypeWithin node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.PatternNode, java.lang.Object) + */ + public Object visit(PatternNode node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.ReferencePointcut, java.lang.Object) + */ + public Object visit(ReferencePointcut node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.SignaturePattern, java.lang.Object) + */ + public Object visit(SignaturePattern node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.ThisOrTargetAnnotationPointcut, + * java.lang.Object) + */ + public Object visit(ThisOrTargetAnnotationPointcut node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.ThisOrTargetPointcut, java.lang.Object) + */ + public Object visit(ThisOrTargetPointcut node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.ThrowsPattern, java.lang.Object) + */ + public Object visit(ThrowsPattern node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.TypePatternList, java.lang.Object) + */ + public Object visit(TypePatternList node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.WildAnnotationTypePattern, + * java.lang.Object) + */ + public Object visit(WildAnnotationTypePattern node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.WildTypePattern, java.lang.Object) + */ + public Object visit(WildTypePattern node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.WithinAnnotationPointcut, + * java.lang.Object) + */ + public Object visit(WithinAnnotationPointcut node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.WithinCodeAnnotationPointcut, + * java.lang.Object) + */ + public Object visit(WithinCodeAnnotationPointcut node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.WithinPointcut, java.lang.Object) + */ + public Object visit(WithinPointcut node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.WithincodePointcut, java.lang.Object) + */ + public Object visit(WithincodePointcut node, Object data) { + return node; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PointcutVisitor#visit(org.aspectj.weaver.patterns.Pointcut.MatchesNothingPointcut, + * java.lang.Object) + */ + public Object visit(MatchesNothingPointcut node, Object data) { + return node; + } + + public Object visit(TypeVariablePattern node, Object data) { + return node; + } + + public Object visit(TypeVariablePatternList node, Object data) { + return node; + } + + public Object visit(HasMemberTypePattern node, Object data) { + return node; + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/AndAnnotationTypePattern.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/AndAnnotationTypePattern.java new file mode 100644 index 000000000..4392a19d3 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/AndAnnotationTypePattern.java @@ -0,0 +1,127 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.AnnotatedElement; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; +import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; +import org.aspectj.weaver.ResolvedType; +/** + * @author colyer + * + * TODO To change the template for this generated type comment go to + * Window - Preferences - Java - Code Style - Code Templates + */ +public class AndAnnotationTypePattern extends AnnotationTypePattern { + + private AnnotationTypePattern left; + private AnnotationTypePattern right; + + public AndAnnotationTypePattern(AnnotationTypePattern left, AnnotationTypePattern right) { + this.left = left; + this.right = right; + setLocation(left.getSourceContext(), left.getStart(), right.getEnd()); + } + + public FuzzyBoolean matches(AnnotatedElement annotated) { + return left.matches(annotated).and(right.matches(annotated)); + } + + public FuzzyBoolean matches(AnnotatedElement annotated, ResolvedType[] parameterAnnotations ) { + return left.matches(annotated,parameterAnnotations).and(right.matches(annotated,parameterAnnotations)); + } + + public void resolve(World world) { + left.resolve(world); + right.resolve(world); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.AnnotationTypePattern#resolveBindings(org.aspectj.weaver.patterns.IScope, org.aspectj.weaver.patterns.Bindings, boolean) + */ + public AnnotationTypePattern resolveBindings(IScope scope, + Bindings bindings, boolean allowBinding) { + left = left.resolveBindings(scope,bindings,allowBinding); + right =right.resolveBindings(scope,bindings,allowBinding); + return this; + } + + public AnnotationTypePattern parameterizeWith(Map typeVariableMap,World w) { + AnnotationTypePattern newLeft = left.parameterizeWith(typeVariableMap,w); + AnnotationTypePattern newRight = right.parameterizeWith(typeVariableMap,w); + AndAnnotationTypePattern ret = new AndAnnotationTypePattern(newLeft,newRight); + ret.copyLocationFrom(this); + if (this.isForParameterAnnotationMatch()) ret.setForParameterAnnotationMatch(); + return ret; + } + + public static AnnotationTypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + AnnotationTypePattern p = new AndAnnotationTypePattern( + AnnotationTypePattern.read(s,context), + AnnotationTypePattern.read(s,context)); + p.readLocation(context,s); + if (s.getMajorVersion()>=WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ160) { + if (s.readBoolean()) p.setForParameterAnnotationMatch(); + } + return p; + } + + public void write(DataOutputStream s) throws IOException { + s.writeByte(AnnotationTypePattern.AND); + left.write(s); + right.write(s); + writeLocation(s); + s.writeBoolean(isForParameterAnnotationMatch()); + } + + public boolean equals(Object obj) { + if (!(obj instanceof AndAnnotationTypePattern)) return false; + AndAnnotationTypePattern other = (AndAnnotationTypePattern) obj; + return (left.equals(other.left) && right.equals(other.right) && left.isForParameterAnnotationMatch()==right.isForParameterAnnotationMatch()); + } + + public int hashCode() { + int result = 17; + result = result*37 + left.hashCode(); + result = result*37 + right.hashCode(); + result = result*37 + (isForParameterAnnotationMatch()?0:1); + return result; + } + + public String toString() { + return left.toString() + " " + right.toString(); + } + + public AnnotationTypePattern getLeft() { return left; } + public AnnotationTypePattern getRight() { return right; } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public Object traverse(PatternNodeVisitor visitor, Object data) { + Object ret = accept(visitor,data); + left.traverse(visitor,ret); + right.traverse(visitor,ret); + return ret; + } + + public void setForParameterAnnotationMatch() { + left.setForParameterAnnotationMatch(); + right.setForParameterAnnotationMatch(); + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/AndPointcut.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/AndPointcut.java new file mode 100644 index 000000000..58608a793 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/AndPointcut.java @@ -0,0 +1,129 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Test; + +public class AndPointcut extends Pointcut { + Pointcut left, right; // exposed for testing + + private int couldMatchKinds; + + public AndPointcut(Pointcut left, Pointcut right) { + super(); + this.left = left; + this.right = right; + this.pointcutKind = AND; + setLocation(left.getSourceContext(), left.getStart(), right.getEnd()); + couldMatchKinds = left.couldMatchKinds() & right.couldMatchKinds(); + } + + public int couldMatchKinds() { + return couldMatchKinds; + } + + public FuzzyBoolean fastMatch(FastMatchInfo type) { + return left.fastMatch(type).and(right.fastMatch(type)); + } + + protected FuzzyBoolean matchInternal(Shadow shadow) { + FuzzyBoolean leftMatch = left.match(shadow); + if (leftMatch.alwaysFalse()) return leftMatch; + return leftMatch.and(right.match(shadow)); + } + + public String toString() { + return "(" + left.toString() + " && " + right.toString() + ")"; + } + + public boolean equals(Object other) { + if (!(other instanceof AndPointcut)) return false; + AndPointcut o = (AndPointcut)other; + return o.left.equals(left) && o.right.equals(right); + } + + public int hashCode() { + int result = 19; + result = 37*result + left.hashCode(); + result = 37*result + right.hashCode(); + return result; + } + + public void resolveBindings(IScope scope, Bindings bindings) { + left.resolveBindings(scope, bindings); + right.resolveBindings(scope, bindings); + } + + public void write(DataOutputStream s) throws IOException { + s.writeByte(Pointcut.AND); + left.write(s); + right.write(s); + writeLocation(s); + } + + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + AndPointcut ret = new AndPointcut(Pointcut.read(s, context), Pointcut.read(s, context)); + ret.readLocation(context, s); + return ret; + } + + + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + return Test.makeAnd(left.findResidue(shadow, state), right.findResidue(shadow, state)); + } + + public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + AndPointcut ret = new AndPointcut(left.concretize(inAspect, declaringType, bindings), + right.concretize(inAspect, declaringType, bindings)); + ret.copyLocationFrom(this); + return ret; + } + + public Pointcut parameterizeWith(Map typeVariableMap,World w) { + AndPointcut ret = new AndPointcut(left.parameterizeWith(typeVariableMap,w), + right.parameterizeWith(typeVariableMap,w)); + ret.copyLocationFrom(this); + return ret; + } + + public Pointcut getLeft() { + return left; + } + + public Pointcut getRight() { + return right; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public Object traverse(PatternNodeVisitor visitor, Object data) { + Object ret = accept(visitor,data); + left.traverse(visitor,ret); + right.traverse(visitor,ret); + return ret; + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/AndTypePattern.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/AndTypePattern.java new file mode 100644 index 000000000..ebf79de6d --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/AndTypePattern.java @@ -0,0 +1,176 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; + +/** + * left && right + * + *

any binding to formals is explicitly forbidden for any composite by the language + * + * @author Erik Hilsdale + * @author Jim Hugunin + */ +public class AndTypePattern extends TypePattern { + private TypePattern left, right; + + public AndTypePattern(TypePattern left, TypePattern right) { + super(false,false); //?? we override all methods that care about includeSubtypes + this.left = left; + this.right = right; + setLocation(left.getSourceContext(), left.getStart(), right.getEnd()); + } + + protected boolean couldEverMatchSameTypesAs(TypePattern other) { + return true; // don't dive into ands yet.... + } + public FuzzyBoolean matchesInstanceof(ResolvedType type) { + return left.matchesInstanceof(type).and(right.matchesInstanceof(type)); + } + + protected boolean matchesExactly(ResolvedType type) { + //??? if these had side-effects, this sort-circuit could be a mistake + return left.matchesExactly(type) && right.matchesExactly(type); + } + + protected boolean matchesExactly(ResolvedType type, ResolvedType annotatedType) { + return left.matchesExactly(type,annotatedType) && right.matchesExactly(type,annotatedType); + } + + public boolean matchesStatically(ResolvedType type) { + return left.matchesStatically(type) && right.matchesStatically(type); + } + + public void setIsVarArgs(boolean isVarArgs) { + this.isVarArgs = isVarArgs; + left.setIsVarArgs(isVarArgs); + right.setIsVarArgs(isVarArgs); + } + + public void setAnnotationTypePattern(AnnotationTypePattern annPatt) { + if (annPatt == AnnotationTypePattern.ANY) return; + if (left.annotationPattern == AnnotationTypePattern.ANY) { + left.setAnnotationTypePattern(annPatt); + } else { + left.setAnnotationTypePattern( + new AndAnnotationTypePattern(left.annotationPattern,annPatt)); + } + if (right.annotationPattern == AnnotationTypePattern.ANY) { + right.setAnnotationTypePattern(annPatt); + } else { + right.setAnnotationTypePattern( + new AndAnnotationTypePattern(right.annotationPattern,annPatt)); + } + } + + public void write(DataOutputStream s) throws IOException { + s.writeByte(TypePattern.AND); + left.write(s); + right.write(s); + writeLocation(s); + } + + public static TypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + AndTypePattern ret = new AndTypePattern(TypePattern.read(s, context), TypePattern.read(s, context)); + ret.readLocation(context, s); + if (ret.left.isVarArgs && ret.right.isVarArgs) ret.isVarArgs = true; + return ret; + } + + public TypePattern resolveBindings( + IScope scope, + Bindings bindings, + boolean allowBinding, boolean requireExactType) + { + if (requireExactType) return notExactType(scope); + left = left.resolveBindings(scope, bindings, false, false); + right = right.resolveBindings(scope, bindings, false, false); + return this; + } + + public TypePattern parameterizeWith(Map typeVariableMap,World w) { + TypePattern newLeft = left.parameterizeWith(typeVariableMap,w); + TypePattern newRight = right.parameterizeWith(typeVariableMap,w); + AndTypePattern ret = new AndTypePattern(newLeft,newRight); + ret.copyLocationFrom(this); + return ret; + } + + public String toString() { + StringBuffer buff = new StringBuffer(); + if (annotationPattern != AnnotationTypePattern.ANY) { + buff.append('('); + buff.append(annotationPattern.toString()); + buff.append(' '); + } + buff.append('('); + buff.append(left.toString()); + buff.append(" && "); + buff.append(right.toString()); + buff.append(')'); + if (annotationPattern != AnnotationTypePattern.ANY) { + buff.append(')'); + } + return buff.toString(); + } + + public TypePattern getLeft() { + return left; + } + + public TypePattern getRight() { + return right; + } + + public boolean equals(Object obj) { + if (! (obj instanceof AndTypePattern)) return false; + AndTypePattern atp = (AndTypePattern) obj; + return left.equals(atp.left) && right.equals(atp.right); + } + + public boolean isStarAnnotation() { + return left.isStarAnnotation() && right.isStarAnnotation(); + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + int ret = 17; + ret = ret + 37 * left.hashCode(); + ret = ret + 37 * right.hashCode(); + return ret; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public Object traverse(PatternNodeVisitor visitor, Object data) { + Object ret = accept(visitor,data); + left.traverse(visitor, ret); + right.traverse(visitor, ret); + return ret; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/AnnotationPatternList.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/AnnotationPatternList.java new file mode 100644 index 000000000..f423eb350 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/AnnotationPatternList.java @@ -0,0 +1,203 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; + +/** + * @author colyer + * + * TODO To change the template for this generated type comment go to + * Window - Preferences - Java - Code Style - Code Templates + */ +public class AnnotationPatternList extends PatternNode { + + private AnnotationTypePattern[] typePatterns; + int ellipsisCount = 0; + + public static final AnnotationPatternList EMPTY = + new AnnotationPatternList(new AnnotationTypePattern[] {}); + + public static final AnnotationPatternList ANY = + new AnnotationPatternList(new AnnotationTypePattern[] {AnnotationTypePattern.ELLIPSIS}); + + public AnnotationPatternList() { + typePatterns = new AnnotationTypePattern[0]; + ellipsisCount = 0; + } + + public AnnotationPatternList(AnnotationTypePattern[] arguments) { + this.typePatterns = arguments; + for (int i=0; i 0) && (ellipsisCount == 0)) { + return FuzzyBoolean.NO; + } + // now work through the args and the patterns, skipping at ellipsis + FuzzyBoolean ret = FuzzyBoolean.YES; + int argsIndex = 0; + for (int i = 0; i < typePatterns.length; i++) { + if (typePatterns[i] == AnnotationTypePattern.ELLIPSIS) { + // match ellipsisMatchCount args + argsIndex += numArgsMatchedByEllipsis; + } else if (typePatterns[i] == AnnotationTypePattern.ANY) { + argsIndex++; + } else { + // match the argument type at argsIndex with the ExactAnnotationTypePattern + // we know it is exact because nothing else is allowed in args + if (someArgs[argsIndex].isPrimitiveType()) return FuzzyBoolean.NO; // can never match + ExactAnnotationTypePattern ap = (ExactAnnotationTypePattern)typePatterns[i]; + FuzzyBoolean matches = ap.matchesRuntimeType(someArgs[argsIndex]); + if (matches == FuzzyBoolean.NO) { + return FuzzyBoolean.MAYBE; // could still match at runtime + } else { + argsIndex++; + ret = ret.and(matches); + } + } + } + return ret; + } + + public int size() { return typePatterns.length; } + + public AnnotationTypePattern get(int index) { + return typePatterns[index]; + } + + public AnnotationPatternList resolveBindings(IScope scope, Bindings bindings, boolean allowBinding) { + for (int i=0; i 0) buf.append(", "); + if (type == AnnotationTypePattern.ELLIPSIS) { + buf.append(".."); + } else { + String annPatt = type.toString(); + buf.append(annPatt.startsWith("@") ? annPatt.substring(1) : annPatt); + } + } + buf.append(")"); + return buf.toString(); + } + + public boolean equals(Object other) { + if (!(other instanceof AnnotationPatternList)) return false; + AnnotationPatternList o = (AnnotationPatternList)other; + int len = o.typePatterns.length; + if (len != this.typePatterns.length) return false; + for (int i=0; i + *

+ * Matches any join point where the subject of the join point has an annotation matching the annotationTypePattern: + * + *
+ * Join Point Kind - Subject
+ * ================================
+ * method call - the target method
+ * method execution - the method
+ * constructor call - the constructor
+ * constructor execution - the constructor
+ * get - the target field
+ * set - the target field
+ * adviceexecution - the advice
+ * initialization - the constructor
+ * preinitialization - the constructor
+ * staticinitialization - the type being initialized
+ * handler - the declared type of the handled exception
+ */ +public class AnnotationPointcut extends NameBindingPointcut { + + private ExactAnnotationTypePattern annotationTypePattern; + private String declarationText; + + public AnnotationPointcut(ExactAnnotationTypePattern type) { + super(); + this.annotationTypePattern = type; + this.pointcutKind = Pointcut.ANNOTATION; + buildDeclarationText(); + } + + public AnnotationPointcut(ExactAnnotationTypePattern type, ShadowMunger munger) { + this(type); + buildDeclarationText(); + } + + public ExactAnnotationTypePattern getAnnotationTypePattern() { + return annotationTypePattern; + } + + public int couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS_BITS; + } + + public Pointcut parameterizeWith(Map typeVariableMap, World w) { + AnnotationPointcut ret = new AnnotationPointcut((ExactAnnotationTypePattern) annotationTypePattern.parameterizeWith( + typeVariableMap, w)); + ret.copyLocationFrom(this); + return ret; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#fastMatch(org.aspectj.weaver.patterns.FastMatchInfo) + */ + public FuzzyBoolean fastMatch(FastMatchInfo info) { + if (info.getKind() == Shadow.StaticInitialization) { + return annotationTypePattern.fastMatches(info.getType()); + } else { + return FuzzyBoolean.MAYBE; + } + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#match(org.aspectj.weaver.Shadow) + */ + protected FuzzyBoolean matchInternal(Shadow shadow) { + AnnotatedElement toMatchAgainst = null; + Member member = shadow.getSignature(); + ResolvedMember rMember = member.resolve(shadow.getIWorld()); + + if (rMember == null) { + if (member.getName().startsWith(NameMangler.PREFIX)) { + return FuzzyBoolean.NO; + } + shadow.getIWorld().getLint().unresolvableMember.signal(member.toString(), getSourceLocation()); + return FuzzyBoolean.NO; + } + + Shadow.Kind kind = shadow.getKind(); + if (kind == Shadow.StaticInitialization) { + toMatchAgainst = rMember.getDeclaringType().resolve(shadow.getIWorld()); + } else if ((kind == Shadow.ExceptionHandler)) { + toMatchAgainst = rMember.getParameterTypes()[0].resolve(shadow.getIWorld()); + } else { + toMatchAgainst = rMember; + // FIXME asc I'd like to get rid of this bit of logic altogether, shame ITD fields don't have an effective sig attribute + // FIXME asc perf cache the result of discovering the member that contains the real annotations + if (rMember.isAnnotatedElsewhere()) { + if (kind == Shadow.FieldGet || kind == Shadow.FieldSet) { + // FIXME asc should include supers with getInterTypeMungersIncludingSupers ? + List mungers = rMember.getDeclaringType().resolve(shadow.getIWorld()).getInterTypeMungers(); + for (Iterator iter = mungers.iterator(); iter.hasNext();) { + ConcreteTypeMunger typeMunger = (ConcreteTypeMunger) iter.next(); + if (typeMunger.getMunger() instanceof NewFieldTypeMunger) { + ResolvedMember fakerm = typeMunger.getSignature(); + if (fakerm.equals(member)) { + ResolvedMember ajcMethod = AjcMemberMaker.interFieldInitializer(fakerm, typeMunger.getAspectType()); + ResolvedMember rmm = findMethod(typeMunger.getAspectType(), ajcMethod); + toMatchAgainst = rmm; + } + } + } + } + } + } + + annotationTypePattern.resolve(shadow.getIWorld()); + return annotationTypePattern.matches(toMatchAgainst); + } + + 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; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#resolveBindings(org.aspectj.weaver.patterns.IScope, + * org.aspectj.weaver.patterns.Bindings) + */ + protected void resolveBindings(IScope scope, Bindings bindings) { + if (!scope.getWorld().isInJava5Mode()) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.ATANNOTATION_ONLY_SUPPORTED_AT_JAVA5_LEVEL), + getSourceLocation())); + return; + } + annotationTypePattern = (ExactAnnotationTypePattern) annotationTypePattern.resolveBindings(scope, bindings, true); + // must be either a Var, or an annotation type pattern + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.Pointcut#concretize1(org.aspectj.weaver.ResolvedType, org.aspectj.weaver.IntMap) + */ + protected Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + ExactAnnotationTypePattern newType = (ExactAnnotationTypePattern) annotationTypePattern.remapAdviceFormals(bindings); + Pointcut ret = new AnnotationPointcut(newType, bindings.getEnclosingAdvice()); + ret.copyLocationFrom(this); + return ret; + } + + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + if (annotationTypePattern instanceof BindingAnnotationFieldTypePattern) { + if (shadow.getKind() != Shadow.MethodExecution) { + shadow.getIWorld().getMessageHandler().handleMessage( + MessageUtil.error( + "Annotation field binding is only supported at method-execution join points (compiler limitation)", + getSourceLocation())); + return Literal.TRUE; // exit quickly, error will prevent weaving + } + BindingAnnotationFieldTypePattern btp = (BindingAnnotationFieldTypePattern) annotationTypePattern; + ResolvedType formalType = btp.getFormalType().resolve(shadow.getIWorld()); + UnresolvedType annoType = btp.getAnnotationType(); + // TODO 2 need to sort out appropriate creation of the AnnotationAccessFieldVar - what happens for + // reflective (ReflectionShadow) access to types? + Var var = shadow.getKindedAnnotationVar(annoType); + if (var == null) { + throw new BCException("Unexpected problem locating annotation at join point '" + shadow + "'"); + } + state.set(btp.getFormalIndex(), var.getAccessorForValue(formalType)); + } else if (annotationTypePattern instanceof BindingAnnotationTypePattern) { + BindingAnnotationTypePattern btp = (BindingAnnotationTypePattern) annotationTypePattern; + UnresolvedType annotationType = btp.getAnnotationType(); + Var var = shadow.getKindedAnnotationVar(annotationType); + + // At this point, var *could* be null. The only reason this could happen (if we aren't failing...) + // is if another binding annotation designator elsewhere in the pointcut is going to expose the annotation + // eg. (execution(* a*(..)) && @annotation(foo)) || (execution(* b*(..)) && @this(foo)) + // where sometimes @annotation will be providing the value, and sometimes + // @this will be providing the value (see pr138223) + + // If we are here for other indecipherable reasons (it's not the case above...) then + // you might want to uncomment this next bit of code to collect the diagnostics + // if (var == null) throw new BCException("Impossible! annotation=["+annotationType+ + // "] shadow=["+shadow+" at "+shadow.getSourceLocation()+ + // "] pointcut is at ["+getSourceLocation()+"]"); + if (var == null) { + if (matchInternal(shadow).alwaysTrue()) + return Literal.TRUE; + else + return Literal.FALSE; + } + state.set(btp.getFormalIndex(), var); + } + + if (matchInternal(shadow).alwaysTrue()) + return Literal.TRUE; + else + return Literal.FALSE; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.NameBindingPointcut#getBindingAnnotationTypePatterns() + */ + public List getBindingAnnotationTypePatterns() { + if (annotationTypePattern instanceof BindingPattern) { // BindingAnnotationTypePattern) { + List l = new ArrayList(); + l.add(annotationTypePattern); + return l; + } else + return Collections.EMPTY_LIST; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.NameBindingPointcut#getBindingTypePatterns() + */ + public List getBindingTypePatterns() { + return Collections.EMPTY_LIST; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.patterns.PatternNode#write(java.io.DataOutputStream) + */ + public void write(DataOutputStream s) throws IOException { + s.writeByte(Pointcut.ANNOTATION); + annotationTypePattern.write(s); + writeLocation(s); + } + + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + AnnotationTypePattern type = AnnotationTypePattern.read(s, context); + AnnotationPointcut ret = new AnnotationPointcut((ExactAnnotationTypePattern) type); + ret.readLocation(context, s); + return ret; + } + + public boolean equals(Object other) { + if (!(other instanceof AnnotationPointcut)) + return false; + AnnotationPointcut o = (AnnotationPointcut) other; + return o.annotationTypePattern.equals(this.annotationTypePattern); + } + + public int hashCode() { + int result = 17; + result = 37 * result + annotationTypePattern.hashCode(); + return result; + } + + public void buildDeclarationText() { + StringBuffer buf = new StringBuffer(); + buf.append("@annotation("); + String annPatt = annotationTypePattern.toString(); + buf.append(annPatt.startsWith("@") ? annPatt.substring(1) : annPatt); + buf.append(")"); + this.declarationText = buf.toString(); + } + + public String toString() { + return this.declarationText; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/AnnotationTypePattern.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/AnnotationTypePattern.java new file mode 100644 index 000000000..e9ea8541f --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/AnnotationTypePattern.java @@ -0,0 +1,130 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.AnnotatedElement; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; + +public abstract class AnnotationTypePattern extends PatternNode { + + public static final AnnotationTypePattern ANY = new AnyAnnotationTypePattern(); + public static final AnnotationTypePattern ELLIPSIS = new EllipsisAnnotationTypePattern(); + public static final AnnotationTypePattern[] NONE = new AnnotationTypePattern[0]; + private boolean isForParameterAnnotationMatch; + + /** + * TODO: write, read, equals & hashcode both in annotation hierarachy and + * in altered TypePattern hierarchy + */ + protected AnnotationTypePattern() { + super(); + } + + public abstract FuzzyBoolean matches(AnnotatedElement annotated); + public abstract FuzzyBoolean matches(AnnotatedElement annotated,ResolvedType[] parameterAnnotations); + + public FuzzyBoolean fastMatches(AnnotatedElement annotated) { + return FuzzyBoolean.MAYBE; + } + + public AnnotationTypePattern remapAdviceFormals(IntMap bindings) { + return this; + } + + public abstract void resolve(World world); + + public abstract AnnotationTypePattern parameterizeWith(Map/*name -> ResolvedType*/ typeVariableMap,World w); + + public boolean isAny() { return false; } + + /** + * This can modify in place, or return a new TypePattern if the type changes. + */ + public AnnotationTypePattern resolveBindings(IScope scope, Bindings bindings, + boolean allowBinding) + { + return this; + } + + + public static final byte EXACT = 1; + public static final byte BINDING = 2; + public static final byte NOT = 3; + public static final byte OR = 4; + public static final byte AND = 5; + public static final byte ELLIPSIS_KEY = 6; + public static final byte ANY_KEY = 7; + public static final byte WILD = 8; + public static final byte EXACTFIELD = 9; + public static final byte BINDINGFIELD = 10; + + public static AnnotationTypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + byte key = s.readByte(); + switch(key) { + case EXACT: return ExactAnnotationTypePattern.read(s, context); + case BINDING: return BindingAnnotationTypePattern.read(s, context); + case NOT: return NotAnnotationTypePattern.read(s, context); + case OR: return OrAnnotationTypePattern.read(s, context); + case AND: return AndAnnotationTypePattern.read(s, context); + case WILD: return WildAnnotationTypePattern.read(s,context); + case EXACTFIELD: + return ExactAnnotationFieldTypePattern.read(s, context); + case BINDINGFIELD: + return BindingAnnotationFieldTypePattern.read(s, context); + case ELLIPSIS_KEY: return ELLIPSIS; + case ANY_KEY: return ANY; + } + throw new BCException("unknown TypePattern kind: " + key); + } + + public void setForParameterAnnotationMatch() { isForParameterAnnotationMatch = true; } + public boolean isForParameterAnnotationMatch() { return isForParameterAnnotationMatch; } + +} + + +class EllipsisAnnotationTypePattern extends AnnotationTypePattern { + + public FuzzyBoolean matches(AnnotatedElement annotated) { + return FuzzyBoolean.NO; + } + + public FuzzyBoolean matches(AnnotatedElement annotated,ResolvedType[] parameterAnnotations) { + return FuzzyBoolean.NO; + } + + public void write(DataOutputStream s) throws IOException { + s.writeByte(AnnotationTypePattern.ELLIPSIS_KEY); + } + + public void resolve(World world) { + } + + public String toString() { return ".."; } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public AnnotationTypePattern parameterizeWith(Map arg0,World w) { + return this; + } + +} \ No newline at end of file diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/AnyAnnotationTypePattern.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/AnyAnnotationTypePattern.java new file mode 100644 index 000000000..4a2f30af3 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/AnyAnnotationTypePattern.java @@ -0,0 +1,55 @@ +/* ******************************************************************* + * 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 - extracted from AnnotationTypePattern + * ******************************************************************/ + package org.aspectj.weaver.patterns; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.AnnotatedElement; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ResolvedType; + +public class AnyAnnotationTypePattern extends AnnotationTypePattern { + + public FuzzyBoolean fastMatches(AnnotatedElement annotated) { + return FuzzyBoolean.YES; + } + + public FuzzyBoolean matches(AnnotatedElement annotated) { + return FuzzyBoolean.YES; + } + + public FuzzyBoolean matches(AnnotatedElement annotated,ResolvedType[] parameterAnnotations) { + return FuzzyBoolean.YES; + } + + public void write(DataOutputStream s) throws IOException { + s.writeByte(AnnotationTypePattern.ANY_KEY); + } + + public void resolve(World world) { + } + + public String toString() { return "@ANY"; } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public boolean isAny() { return true; } + + public AnnotationTypePattern parameterizeWith(Map arg0,World w) { + return this; + } +} \ No newline at end of file diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ArgsAnnotationPointcut.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ArgsAnnotationPointcut.java new file mode 100644 index 000000000..5c749cabd --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ArgsAnnotationPointcut.java @@ -0,0 +1,245 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +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.ast.Var; + +/** + * @author colyer + * + * TODO To change the template for this generated type comment go to + * Window - Preferences - Java - Code Style - Code Templates + */ +public class ArgsAnnotationPointcut extends NameBindingPointcut { + + private AnnotationPatternList arguments; + private String declarationText; + + /** + * + */ + public ArgsAnnotationPointcut(AnnotationPatternList arguments) { + super(); + this.arguments = arguments; + this.pointcutKind = ATARGS; + buildDeclarationText(); + } + + public AnnotationPatternList getArguments() { + return arguments; + } + + public int couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS_BITS; // empty args() matches jps with no args + } + + public Pointcut parameterizeWith(Map typeVariableMap,World w) { + ArgsAnnotationPointcut ret = new ArgsAnnotationPointcut(arguments.parameterizeWith(typeVariableMap,w)); + ret.copyLocationFrom(this); + return ret; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.Pointcut#fastMatch(org.aspectj.weaver.patterns.FastMatchInfo) + */ + public FuzzyBoolean fastMatch(FastMatchInfo info) { + return FuzzyBoolean.MAYBE; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.Pointcut#match(org.aspectj.weaver.Shadow) + */ + protected FuzzyBoolean matchInternal(Shadow shadow) { + arguments.resolve(shadow.getIWorld()); + FuzzyBoolean ret = + arguments.matches(shadow.getIWorld().resolve(shadow.getArgTypes())); + return ret; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.Pointcut#resolveBindings(org.aspectj.weaver.patterns.IScope, org.aspectj.weaver.patterns.Bindings) + */ + protected void resolveBindings(IScope scope, Bindings bindings) { + if (!scope.getWorld().isInJava5Mode()) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.ATARGS_ONLY_SUPPORTED_AT_JAVA5_LEVEL), + getSourceLocation())); + return; + } + arguments.resolveBindings(scope, bindings, true); + if (arguments.ellipsisCount > 1) { + scope.message(IMessage.ERROR, this, + "uses more than one .. in args (compiler limitation)"); + } + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.Pointcut#concretize1(org.aspectj.weaver.ResolvedType, org.aspectj.weaver.IntMap) + */ + protected Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + if (isDeclare(bindings.getEnclosingAdvice())) { + // Enforce rule about which designators are supported in declare + inAspect.getWorld().showMessage(IMessage.ERROR, + WeaverMessages.format(WeaverMessages.ARGS_IN_DECLARE), + bindings.getEnclosingAdvice().getSourceLocation(), null); + return Pointcut.makeMatchesNothing(Pointcut.CONCRETE); + } + AnnotationPatternList list = arguments.resolveReferences(bindings); + Pointcut ret = new ArgsAnnotationPointcut(list); + ret.copyLocationFrom(this); + return ret; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.Pointcut#findResidue(org.aspectj.weaver.Shadow, org.aspectj.weaver.patterns.ExposedState) + */ + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + int len = shadow.getArgCount(); + + // do some quick length tests first + int numArgsMatchedByEllipsis = (len + arguments.ellipsisCount) - arguments.size(); + if (numArgsMatchedByEllipsis < 0) return Literal.FALSE; // should never happen + if ((numArgsMatchedByEllipsis > 0) && (arguments.ellipsisCount == 0)) { + return Literal.FALSE; // should never happen + } + // now work through the args and the patterns, skipping at ellipsis + Test ret = Literal.TRUE; + int argsIndex = 0; + for (int i = 0; i < arguments.size(); i++) { + if (arguments.get(i) == AnnotationTypePattern.ELLIPSIS) { + // match ellipsisMatchCount args + argsIndex += numArgsMatchedByEllipsis; + } else if (arguments.get(i) == AnnotationTypePattern.ANY) { + argsIndex++; + } else { + // match the argument type at argsIndex with the ExactAnnotationTypePattern + // we know it is exact because nothing else is allowed in args + ExactAnnotationTypePattern ap = (ExactAnnotationTypePattern)arguments.get(i); + UnresolvedType argType = shadow.getArgType(argsIndex); + ResolvedType rArgType = argType.resolve(shadow.getIWorld()); + if (rArgType.isMissing()) { + shadow.getIWorld().getLint().cantFindType.signal( + new String[] {WeaverMessages.format(WeaverMessages.CANT_FIND_TYPE_ARG_TYPE,argType.getName())}, + shadow.getSourceLocation(), + new ISourceLocation[]{getSourceLocation()} + ); +// IMessage msg = new Message( +// WeaverMessages.format(WeaverMessages.CANT_FIND_TYPE_ARG_TYPE,argType.getName()), +// "",IMessage.ERROR,shadow.getSourceLocation(),null,new ISourceLocation[]{getSourceLocation()}); + } + + ResolvedType rAnnType = ap.getAnnotationType().resolve(shadow.getIWorld()); + if (ap instanceof BindingAnnotationTypePattern) { + BindingAnnotationTypePattern btp = (BindingAnnotationTypePattern)ap; + Var annvar = shadow.getArgAnnotationVar(argsIndex,rAnnType); + state.set(btp.getFormalIndex(),annvar); + } + if (!ap.matches(rArgType).alwaysTrue()) { + // we need a test... + ret = Test.makeAnd(ret, + Test.makeHasAnnotation( + shadow.getArgVar(argsIndex), + rAnnType)); + } + argsIndex++; + } + } + return ret; + } + + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.NameBindingPointcut#getBindingAnnotationTypePatterns() + */ + public List getBindingAnnotationTypePatterns() { + List l = new ArrayList(); + AnnotationTypePattern[] pats = arguments.getAnnotationPatterns(); + for (int i = 0; i < pats.length; i++) { + if (pats[i] instanceof BindingAnnotationTypePattern) { + l.add(pats[i]); + } + } + return l; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.NameBindingPointcut#getBindingTypePatterns() + */ + public List getBindingTypePatterns() { + return Collections.EMPTY_LIST; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.PatternNode#write(java.io.DataOutputStream) + */ + public void write(DataOutputStream s) throws IOException { + s.writeByte(Pointcut.ATARGS); + arguments.write(s); + writeLocation(s); + } + + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + AnnotationPatternList annotationPatternList = AnnotationPatternList.read(s,context); + ArgsAnnotationPointcut ret = new ArgsAnnotationPointcut(annotationPatternList); + ret.readLocation(context, s); + return ret; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + if (!(obj instanceof ArgsAnnotationPointcut)) return false; + ArgsAnnotationPointcut other = (ArgsAnnotationPointcut) obj; + return other.arguments.equals(arguments); + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return 17 + 37*arguments.hashCode(); + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + private void buildDeclarationText() { + StringBuffer buf = new StringBuffer("@args"); + buf.append(arguments.toString()); + this.declarationText = buf.toString(); + } + + public String toString() { return this.declarationText; } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ArgsPointcut.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ArgsPointcut.java new file mode 100644 index 000000000..68d2d62f1 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ArgsPointcut.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.patterns; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Test; + +/** + * args(arguments) + * + * @author Erik Hilsdale + * @author Jim Hugunin + */ +public class ArgsPointcut extends NameBindingPointcut { + private static final String ASPECTJ_JP_SIGNATURE_PREFIX = "Lorg/aspectj/lang/JoinPoint"; + private static final String ASPECTJ_SYNTHETIC_SIGNATURE_PREFIX = "Lorg/aspectj/runtime/internal/"; + + private TypePatternList arguments; + private String stringRepresentation; + + public ArgsPointcut(TypePatternList arguments) { + this.arguments = arguments; + this.pointcutKind = ARGS; + this.stringRepresentation = "args" + arguments.toString() + ""; + } + + public TypePatternList getArguments() { + return arguments; + } + + public Pointcut parameterizeWith(Map typeVariableMap,World w) { + ArgsPointcut ret = new ArgsPointcut(this.arguments.parameterizeWith(typeVariableMap,w)); + ret.copyLocationFrom(this); + return ret; + } + + public int couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS_BITS; // empty args() matches jps with no args + } + + public FuzzyBoolean fastMatch(FastMatchInfo type) { + return FuzzyBoolean.MAYBE; + } + + protected FuzzyBoolean matchInternal(Shadow shadow) { + ResolvedType[] argumentsToMatchAgainst = getArgumentsToMatchAgainst(shadow); + FuzzyBoolean ret = + arguments.matches(argumentsToMatchAgainst, TypePattern.DYNAMIC); + return ret; + } + + private ResolvedType[] getArgumentsToMatchAgainst(Shadow shadow) { + + if (shadow.isShadowForArrayConstructionJoinpoint()) { + return shadow.getArgumentTypesForArrayConstructionShadow(); + } + + ResolvedType[] argumentsToMatchAgainst = shadow.getIWorld().resolve(shadow.getGenericArgTypes()); + + // special treatment for adviceexecution which may have synthetic arguments we + // want to ignore. + if (shadow.getKind() == Shadow.AdviceExecution) { + int numExtraArgs = 0; + for (int i = 0; i < argumentsToMatchAgainst.length; i++) { + String argumentSignature = argumentsToMatchAgainst[i].getSignature(); + if (argumentSignature.startsWith(ASPECTJ_JP_SIGNATURE_PREFIX) || argumentSignature.startsWith(ASPECTJ_SYNTHETIC_SIGNATURE_PREFIX)) { + numExtraArgs++; + } else { + // normal arg after AJ type means earlier arg was NOT synthetic + numExtraArgs = 0; + } + } + if (numExtraArgs > 0) { + int newArgLength = argumentsToMatchAgainst.length - numExtraArgs; + ResolvedType[] argsSubset = new ResolvedType[newArgLength]; + System.arraycopy(argumentsToMatchAgainst, 0, argsSubset, 0, newArgLength); + argumentsToMatchAgainst = argsSubset; + } + } else if (shadow.getKind() == Shadow.ConstructorExecution) { + if (shadow.getMatchingSignature().getParameterTypes().length < argumentsToMatchAgainst.length) { + // there are one or more synthetic args on the end, caused by non-public itd constructor + int newArgLength = shadow.getMatchingSignature().getParameterTypes().length; + ResolvedType[] argsSubset = new ResolvedType[newArgLength]; + System.arraycopy(argumentsToMatchAgainst, 0, argsSubset, 0, newArgLength); + argumentsToMatchAgainst = argsSubset; + } + } + + return argumentsToMatchAgainst; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.NameBindingPointcut#getBindingAnnotationTypePatterns() + */ + public List getBindingAnnotationTypePatterns() { + return Collections.EMPTY_LIST; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.NameBindingPointcut#getBindingTypePatterns() + */ + public List getBindingTypePatterns() { + List l = new ArrayList(); + TypePattern[] pats = arguments.getTypePatterns(); + for (int i = 0; i < pats.length; i++) { + if (pats[i] instanceof BindingTypePattern) { + l.add(pats[i]); + } + } + return l; + } + + public void write(DataOutputStream s) throws IOException { + s.writeByte(Pointcut.ARGS); + arguments.write(s); + writeLocation(s); + } + + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + ArgsPointcut ret = new ArgsPointcut(TypePatternList.read(s, context)); + ret.readLocation(context, s); + return ret; + } + + + public boolean equals(Object other) { + if (!(other instanceof ArgsPointcut)) return false; + ArgsPointcut o = (ArgsPointcut)other; + return o.arguments.equals(this.arguments); + } + + public int hashCode() { + return arguments.hashCode(); + } + + public void resolveBindings(IScope scope, Bindings bindings) { + arguments.resolveBindings(scope, bindings, true, true); + if (arguments.ellipsisCount > 1) { + scope.message(IMessage.ERROR, this, + "uses more than one .. in args (compiler limitation)"); + } + } + + public void postRead(ResolvedType enclosingType) { + arguments.postRead(enclosingType); + } + + + public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + if (isDeclare(bindings.getEnclosingAdvice())) { + // Enforce rule about which designators are supported in declare + inAspect.getWorld().showMessage(IMessage.ERROR, + WeaverMessages.format(WeaverMessages.ARGS_IN_DECLARE), + bindings.getEnclosingAdvice().getSourceLocation(), null); + return Pointcut.makeMatchesNothing(Pointcut.CONCRETE); + } + TypePatternList args = arguments.resolveReferences(bindings); + if (inAspect.crosscuttingMembers != null) { + inAspect.crosscuttingMembers.exposeTypes(args.getExactTypes()); + } + Pointcut ret = new ArgsPointcut(args); + ret.copyLocationFrom(this); + return ret; + } + + private Test findResidueNoEllipsis(Shadow shadow, ExposedState state, TypePattern[] patterns) { + ResolvedType[] argumentsToMatchAgainst = getArgumentsToMatchAgainst(shadow); + int len = argumentsToMatchAgainst.length; + //System.err.println("boudn to : " + len + ", " + patterns.length); + if (patterns.length != len) { + return Literal.FALSE; + } + + Test ret = Literal.TRUE; + + for (int i=0; i < len; i++) { + UnresolvedType argType = shadow.getGenericArgTypes()[i]; + TypePattern type = patterns[i]; + ResolvedType argRTX = shadow.getIWorld().resolve(argType,true); + if (!(type instanceof BindingTypePattern)) { + if (argRTX.isMissing()) { + shadow.getIWorld().getLint().cantFindType.signal( + new String[] {WeaverMessages.format(WeaverMessages.CANT_FIND_TYPE_ARG_TYPE,argType.getName())}, + shadow.getSourceLocation(), + new ISourceLocation[]{getSourceLocation()} + ); +// IMessage msg = new Message( +// WeaverMessages.format(WeaverMessages.CANT_FIND_TYPE_ARG_TYPE,argType.getName()), +// "",IMessage.ERROR,shadow.getSourceLocation(),null,new ISourceLocation[]{getSourceLocation()}); +// shadow.getIWorld().getMessageHandler().handleMessage(msg); + } + if (type.matchesInstanceof(argRTX).alwaysTrue()) { + continue; + } + } + + World world = shadow.getIWorld(); + ResolvedType typeToExpose = type.getExactType().resolve(world); + if (typeToExpose.isParameterizedType()) { + boolean inDoubt = (type.matchesInstanceof(argRTX) == FuzzyBoolean.MAYBE); + if (inDoubt && world.getLint().uncheckedArgument.isEnabled()) { + String uncheckedMatchWith = typeToExpose.getSimpleBaseName(); + if (argRTX.isParameterizedType() && (argRTX.getRawType() == typeToExpose.getRawType())) { + uncheckedMatchWith = argRTX.getSimpleName(); + } + if (!isUncheckedArgumentWarningSuppressed()) { + world.getLint().uncheckedArgument.signal( + new String[] { + typeToExpose.getSimpleName(), + uncheckedMatchWith, + typeToExpose.getSimpleBaseName(), + shadow.toResolvedString(world)}, + getSourceLocation(), + new ISourceLocation[] {shadow.getSourceLocation()}); + } + } + } + + ret = Test.makeAnd(ret, + exposeStateForVar(shadow.getArgVar(i), type, state,shadow.getIWorld())); + } + + return ret; + } + + /** + * We need to find out if someone has put the @SuppressAjWarnings{"uncheckedArgument"} + * annotation somewhere. That somewhere is going to be an a piece of advice that uses this + * pointcut. But how do we find it??? + * @return + */ + private boolean isUncheckedArgumentWarningSuppressed() { + return false; + } + + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + ResolvedType[] argsToMatch = getArgumentsToMatchAgainst(shadow); + if (arguments.matches(argsToMatch, TypePattern.DYNAMIC).alwaysFalse()) { + return Literal.FALSE; + } + int ellipsisCount = arguments.ellipsisCount; + if (ellipsisCount == 0) { + return findResidueNoEllipsis(shadow, state, arguments.getTypePatterns()); + } else if (ellipsisCount == 1) { + TypePattern[] patternsWithEllipsis = arguments.getTypePatterns(); + TypePattern[] patternsWithoutEllipsis = new TypePattern[argsToMatch.length]; + int lenWithEllipsis = patternsWithEllipsis.length; + int lenWithoutEllipsis = patternsWithoutEllipsis.length; + // l1+1 >= l0 + int indexWithEllipsis = 0; + int indexWithoutEllipsis = 0; + while (indexWithoutEllipsis < lenWithoutEllipsis) { + TypePattern p = patternsWithEllipsis[indexWithEllipsis++]; + if (p == TypePattern.ELLIPSIS) { + int newLenWithoutEllipsis = + lenWithoutEllipsis - (lenWithEllipsis-indexWithEllipsis); + while (indexWithoutEllipsis < newLenWithoutEllipsis) { + patternsWithoutEllipsis[indexWithoutEllipsis++] = TypePattern.ANY; + } + } else { + patternsWithoutEllipsis[indexWithoutEllipsis++] = p; + } + } + return findResidueNoEllipsis(shadow, state, patternsWithoutEllipsis); + } else { + throw new BCException("unimplemented"); + } + } + + public String toString() { + return this.stringRepresentation; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/BasicToken.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/BasicToken.java new file mode 100644 index 000000000..e6e201daf --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/BasicToken.java @@ -0,0 +1,75 @@ +/* ******************************************************************* + * 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; + + +public final class BasicToken implements IToken { + private String value; + private boolean isIdentifier; + private String literalKind; + + private int start; + private int end; + + public static BasicToken makeOperator(String value, int start, int end) { + return new BasicToken(value.intern(), false, null, start, end); + } + + public static BasicToken makeIdentifier(String value, int start, int end) { + return new BasicToken(value, true, null, start, end); + } + + public static BasicToken makeLiteral(String value, String kind, int start, int end) { + return new BasicToken(value, false, kind.intern(), start, end); + } + + + private BasicToken(String value, boolean isIdentifier, String literalKind, int start, int end) { + this.value = value; + this.isIdentifier = isIdentifier; + this.literalKind = literalKind; + this.start = start; + this.end = end; + } + + public int getStart() { return start; } + public int getEnd() { return end; } + public String getFileName() { return "unknown"; } + + public String getString() { + return value; + } + + public boolean isIdentifier() { + return isIdentifier; + } + + public Pointcut maybeGetParsedPointcut() { + return null; + } + + + + public String toString() { + String s; + if (isIdentifier) s = value; + else s = "'" + value + "'"; + + return s + "@" + start + ":" + end; + } + public String getLiteralKind() { + return literalKind; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/BasicTokenSource.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/BasicTokenSource.java new file mode 100644 index 000000000..37bc7eda1 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/BasicTokenSource.java @@ -0,0 +1,183 @@ +/* ******************************************************************* + * 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 java.util.ArrayList; +import java.util.List; + +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.ISourceContext; + + +public class BasicTokenSource implements ITokenSource { + private int index = 0; + private IToken[] tokens; + private ISourceContext sourceContext; + + public BasicTokenSource(IToken[] tokens, ISourceContext sourceContext) { + this.tokens = tokens; + this.sourceContext = sourceContext; + } + + public int getIndex() { + return index; + } + + public void setIndex(int newIndex) { + this.index = newIndex; + } + + public IToken next() { + try { + return tokens[index++]; + } catch (ArrayIndexOutOfBoundsException e) { + return IToken.EOF; + } + } + + public IToken peek() { + try { + return tokens[index]; + } catch (ArrayIndexOutOfBoundsException e) { + return IToken.EOF; + } + } + + public IToken peek(int offset) { + try { + return tokens[index+offset]; + } catch (ArrayIndexOutOfBoundsException e) { + return IToken.EOF; + } + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("["); + for (int i = 0; i < tokens.length; i++) { + IToken t = tokens[i]; + if (t == null) + break; + if (i > 0) + buf.append(", "); + buf.append(t.toString()); + } + buf.append("]"); + return buf.toString(); + } + + + ////////////////////////////////////////////////////// + // Convenience, maybe just for testing + static ITokenSource makeTokenSource(String input, ISourceContext context) { + char[] chars = input.toCharArray(); + + int i = 0; + List tokens = new ArrayList(); + + while (i < chars.length) { + char ch = chars[i++]; + switch(ch) { + case ' ': + case '\t': + case '\n': + case '\r': + continue; + case '*': + case '(': + case ')': + case '+': + case '[': + case ']': + case ',': + case '!': + case ':': + case '@': + case '<': + case '>': + case '=': + case '?': + tokens.add(BasicToken.makeOperator(makeString(ch), i-1, i-1)); + continue; + case '.': + if ((i+2)<=chars.length) { + // could be '...' + char nextChar1 = chars[i]; + char nextChar2 = chars[i+1]; + if (ch==nextChar1 && ch==nextChar2) { + // '...' + tokens.add(BasicToken.makeIdentifier("...",i-1,i+1)); + i=i+2; + } else { + tokens.add(BasicToken.makeOperator(makeString(ch), i-1, i-1)); + } + } else { + tokens.add(BasicToken.makeOperator(makeString(ch), i-1, i-1)); + } + continue; + case '&': + if ((i+1) <= chars.length && chars[i] != '&') { + tokens.add(BasicToken.makeOperator(makeString(ch),i-1,i-1)); + continue; + } + // fall-through + case '|': + if (i == chars.length) { + throw new BCException("bad " + ch); + } + char nextChar = chars[i++]; + if (nextChar == ch) { + tokens.add(BasicToken.makeOperator(makeString(ch, 2), i-2, i-1)); + } else { + throw new RuntimeException("bad " + ch); + } + continue; + + case '\"': + int start0 = i-1; + while (i < chars.length && !(chars[i]=='\"')) i++; + i += 1; + tokens.add(BasicToken.makeLiteral(new String(chars, start0+1, i-start0-2), "string", start0, i-1)); + continue; + default: + int start = i-1; + while (i < chars.length && Character.isJavaIdentifierPart(chars[i])) { i++; } + tokens.add(BasicToken.makeIdentifier(new String(chars, start, i-start), start, i-1)); + + } + } + + //System.out.println(tokens); + + return new BasicTokenSource((IToken[])tokens.toArray(new IToken[tokens.size()]), context); + } + + private static String makeString(char ch) { + return Character.toString(ch); + } + + private static String makeString(char ch, int count) { + // slightly inefficient ;-) + char[] chars = new char[count]; + for (int i=0; i + *

+ * before(Level lev): execution(* *(..)) && @annotation(TraceAnnotation(lev))
+ * 

+ * This binding annotation type pattern will be for 'lev'. + */ +public class BindingAnnotationFieldTypePattern extends ExactAnnotationTypePattern implements BindingPattern { + + protected int formalIndex; + UnresolvedType formalType; // In this construct the formal type differs from the annotation type + + public BindingAnnotationFieldTypePattern(UnresolvedType formalType, int formalIndex, UnresolvedType theAnnotationType) { + super(theAnnotationType, null); + this.formalIndex = formalIndex; + this.formalType = formalType; + } + + public void resolveBinding(World world) { + if (resolved) return; + resolved = true; + formalType = world.resolve(formalType); + annotationType = world.resolve(annotationType); + ResolvedType annoType = (ResolvedType) annotationType; + if (!annoType.isAnnotation()) { + IMessage m = MessageUtil.error(WeaverMessages.format(WeaverMessages.REFERENCE_TO_NON_ANNOTATION_TYPE, annoType.getName()), + getSourceLocation()); + world.getMessageHandler().handleMessage(m); + resolved = false; + } + } + + public AnnotationTypePattern parameterizeWith(Map typeVariableMap, World w) { + throw new BCException("Parameterization not implemented for annotation field binding construct (compiler limitation)"); + // UnresolvedType newAnnotationType = annotationType; + // if (annotationType.isTypeVariableReference()) { + // TypeVariableReference t = (TypeVariableReference) annotationType; + // String key = t.getTypeVariable().getName(); + // if (typeVariableMap.containsKey(key)) { + // newAnnotationType = (UnresolvedType) typeVariableMap.get(key); + // } + // } else if (annotationType.isParameterizedType()) { + // newAnnotationType = annotationType.parameterize(typeVariableMap); + // } + // BindingAnnotationTypePattern ret = new BindingAnnotationTypePattern(newAnnotationType, this.formalIndex); + // if (newAnnotationType instanceof ResolvedType) { + // ResolvedType rat = (ResolvedType) newAnnotationType; + // verifyRuntimeRetention(rat.getWorld(), rat); + // } + // ret.copyLocationFrom(this); + // return ret; + } + + public int getFormalIndex() { + return formalIndex; + } + + public boolean equals(Object obj) { + if (!(obj instanceof BindingAnnotationFieldTypePattern)) return false; + BindingAnnotationFieldTypePattern btp = (BindingAnnotationFieldTypePattern) obj; + return (btp.formalIndex == formalIndex) && (annotationType.equals(btp.annotationType)) && (formalType.equals(btp.formalType)); + } + + public int hashCode() { + return (annotationType.hashCode() * 37 + formalIndex * 37) + formalType.hashCode(); + } + + public AnnotationTypePattern remapAdviceFormals(IntMap bindings) { + if (!bindings.hasKey(formalIndex)) { + throw new BCException("Annotation field binding reference must be bound (compiler limitation)"); + // must be something like returning the unbound form: return new ExactAnnotationTypePattern(annotationType, + // null); + } else { + int newFormalIndex = bindings.get(formalIndex); + return new BindingAnnotationFieldTypePattern(formalType, newFormalIndex, annotationType); + } + } + + public void write(DataOutputStream s) throws IOException { + s.writeByte(AnnotationTypePattern.BINDINGFIELD); + formalType.write(s); // the type of the field within the annotation + s.writeShort((short) formalIndex); + annotationType.write(s); // the annotation type + writeLocation(s); + } + + public static AnnotationTypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + AnnotationTypePattern ret = new BindingAnnotationFieldTypePattern(UnresolvedType.read(s), s.readShort(), UnresolvedType.read(s)); + ret.readLocation(context, s); + return ret; + } + + + public FuzzyBoolean matches(AnnotatedElement annotated, ResolvedType[] parameterAnnotations) { + // Inheritance irrelevant because @annotation(Anno(x)) only supported at method execution join points (compiler limitation) +// boolean checkSupers = false; +// if (getResolvedAnnotationType().hasAnnotation(UnresolvedType.AT_INHERITED)) { +// if (annotated instanceof ResolvedType) { +// checkSupers = true; +// } +// } +// + if (annotated.hasAnnotation(annotationType)) { + if (annotationType instanceof ReferenceType) { + ReferenceType rt = (ReferenceType) annotationType; + if (rt.getRetentionPolicy() != null && rt.getRetentionPolicy().equals("SOURCE")) { + rt.getWorld().getMessageHandler().handleMessage( + MessageUtil.warn(WeaverMessages.format(WeaverMessages.NO_MATCH_BECAUSE_SOURCE_RETENTION, annotationType, annotated), + getSourceLocation())); + return FuzzyBoolean.NO; + } + ResolvedMember[] methods = rt.getDeclaredMethods(); + boolean found = false; + for (int i = 0; i < methods.length && !found; i++) { + if (methods[i].getReturnType().equals(formalType)) { + found = true; + } + } + return (found ? FuzzyBoolean.YES : FuzzyBoolean.NO); + } + } +// else if (checkSupers) { +// ResolvedType toMatchAgainst = ((ResolvedType) annotated).getSuperclass(); +// while (toMatchAgainst != null) { +// if (toMatchAgainst.hasAnnotation(annotationType)) { +// return FuzzyBoolean.YES; +// } +// toMatchAgainst = toMatchAgainst.getSuperclass(); +// } +// } +// + return FuzzyBoolean.NO; + } + + public UnresolvedType getFormalType() { + return formalType; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/BindingAnnotationTypePattern.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/BindingAnnotationTypePattern.java new file mode 100644 index 000000000..f0be890a1 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/BindingAnnotationTypePattern.java @@ -0,0 +1,134 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.util.Map; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.TypeVariableReference; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; + +public class BindingAnnotationTypePattern extends ExactAnnotationTypePattern implements BindingPattern { + + protected int formalIndex; + + /** + * @param annotationType + */ + public BindingAnnotationTypePattern(UnresolvedType annotationType, int index) { + super(annotationType,null); + this.formalIndex = index; + } + + public BindingAnnotationTypePattern(FormalBinding binding) { + this(binding.getType(),binding.getIndex()); + } + + public void resolveBinding(World world) { + if (resolved) return; + resolved = true; + annotationType = annotationType.resolve(world); + ResolvedType resolvedAnnotationType = (ResolvedType) annotationType; + if (!resolvedAnnotationType.isAnnotation()) { + IMessage m = MessageUtil.error( + WeaverMessages.format(WeaverMessages.REFERENCE_TO_NON_ANNOTATION_TYPE,annotationType.getName()), + getSourceLocation()); + world.getMessageHandler().handleMessage(m); + resolved = false; + } + if (annotationType.isTypeVariableReference()) return; // we'll deal with this next check when the type var is actually bound... + verifyRuntimeRetention(world, resolvedAnnotationType); + } + + private void verifyRuntimeRetention(World world, ResolvedType resolvedAnnotationType) { + if (!resolvedAnnotationType.isAnnotationWithRuntimeRetention()) { // default is class visibility + // default is class visibility + IMessage m = MessageUtil.error( + WeaverMessages.format(WeaverMessages.BINDING_NON_RUNTIME_RETENTION_ANNOTATION,annotationType.getName()), + getSourceLocation()); + world.getMessageHandler().handleMessage(m); + resolved = false; + } + } + + public AnnotationTypePattern parameterizeWith(Map typeVariableMap,World w) { + UnresolvedType newAnnotationType = annotationType; + if (annotationType.isTypeVariableReference()) { + TypeVariableReference t = (TypeVariableReference) annotationType; + String key = t.getTypeVariable().getName(); + if (typeVariableMap.containsKey(key)) { + newAnnotationType = (UnresolvedType) typeVariableMap.get(key); + } + } else if (annotationType.isParameterizedType()) { + newAnnotationType = annotationType.parameterize(typeVariableMap); + } + BindingAnnotationTypePattern ret = new BindingAnnotationTypePattern(newAnnotationType,this.formalIndex); + if (newAnnotationType instanceof ResolvedType) { + ResolvedType rat = (ResolvedType) newAnnotationType; + verifyRuntimeRetention(rat.getWorld(),rat); + } + ret.copyLocationFrom(this); + return ret; + } + + public int getFormalIndex() { + return formalIndex; + } + + public boolean equals(Object obj) { + if (!(obj instanceof BindingAnnotationTypePattern)) return false; + BindingAnnotationTypePattern btp = (BindingAnnotationTypePattern) obj; + return (super.equals(btp) && (btp.formalIndex == formalIndex)); + } + + public int hashCode() { + return super.hashCode()*37 + formalIndex; + } + + public AnnotationTypePattern remapAdviceFormals(IntMap bindings) { + if (!bindings.hasKey(formalIndex)) { + return new ExactAnnotationTypePattern(annotationType,null); + } else { + int newFormalIndex = bindings.get(formalIndex); + return new BindingAnnotationTypePattern(annotationType, newFormalIndex); + } + } + private static final byte VERSION = 1; // rev if serialised form changed + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.ExactAnnotationTypePattern#write(java.io.DataOutputStream) + */ + public void write(DataOutputStream s) throws IOException { + s.writeByte(AnnotationTypePattern.BINDING); + s.writeByte(VERSION); + annotationType.write(s); + s.writeShort((short)formalIndex); + writeLocation(s); + } + + public static AnnotationTypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + byte version = s.readByte(); + if (version > VERSION) { + throw new BCException("BindingAnnotationTypePattern was written by a more recent version of AspectJ"); + } + AnnotationTypePattern ret = new BindingAnnotationTypePattern(UnresolvedType.read(s),s.readShort()); + ret.readLocation(context,s); + return ret; + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/BindingPattern.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/BindingPattern.java new file mode 100644 index 000000000..4d909ede5 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/BindingPattern.java @@ -0,0 +1,19 @@ +/* ******************************************************************* + * 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 org.aspectj.weaver.IHasPosition; + +/** + * Marker interface for BindingTypePattern and BindingAnnotationTypePattern + */ +public interface BindingPattern extends IHasPosition { + int getFormalIndex(); +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/BindingTypePattern.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/BindingTypePattern.java new file mode 100644 index 000000000..28c5739ad --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/BindingTypePattern.java @@ -0,0 +1,97 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.util.Map; + +import org.aspectj.weaver.AjAttribute; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; + +public class BindingTypePattern extends ExactTypePattern implements BindingPattern { + private int formalIndex; + + public BindingTypePattern(UnresolvedType type, int index,boolean isVarArgs) { + super(type, false,isVarArgs); + this.formalIndex = index; + } + + public BindingTypePattern(FormalBinding binding, boolean isVarArgs) { + this(binding.getType(), binding.getIndex(),isVarArgs); + } + + public int getFormalIndex() { + return formalIndex; + } + + public boolean equals(Object other) { + if (!(other instanceof BindingTypePattern)) return false; + BindingTypePattern o = (BindingTypePattern)other; + if (includeSubtypes != o.includeSubtypes) return false; + if (isVarArgs != o.isVarArgs) return false; + return o.type.equals(this.type) && o.formalIndex == this.formalIndex; + } + public int hashCode() { + int result = 17; + result = 37*result + super.hashCode(); + result = 37*result + formalIndex; + return result; + } + + public void write(DataOutputStream out) throws IOException { + out.writeByte(TypePattern.BINDING); + type.write(out); + out.writeShort((short)formalIndex); + out.writeBoolean(isVarArgs); + writeLocation(out); + } + + public static TypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + UnresolvedType type = UnresolvedType.read(s); + int index = s.readShort(); + boolean isVarargs = false; + if (s.getMajorVersion()>=AjAttribute.WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ150) { + isVarargs = s.readBoolean(); + } + TypePattern ret = new BindingTypePattern(type,index,isVarargs); + ret.readLocation(context, s); + return ret; + } + + public TypePattern remapAdviceFormals(IntMap bindings) { + if (!bindings.hasKey(formalIndex)) { + return new ExactTypePattern(type, false, isVarArgs); + } else { + int newFormalIndex = bindings.get(formalIndex); + return new BindingTypePattern(type, newFormalIndex, isVarArgs); + } + } + + public TypePattern parameterizeWith(Map typeVariableMap,World w) { + ExactTypePattern superParameterized = (ExactTypePattern) super.parameterizeWith(typeVariableMap,w); + BindingTypePattern ret = new BindingTypePattern(superParameterized.getExactType(),this.formalIndex,this.isVarArgs); + ret.copyLocationFrom(this); + return ret; + } + + public String toString() { + //Thread.currentThread().dumpStack(); + return "BindingTypePattern(" + super.toString() + ", " + formalIndex + ")"; + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/Bindings.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/Bindings.java new file mode 100644 index 000000000..4853dd049 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/Bindings.java @@ -0,0 +1,136 @@ +/* ******************************************************************* + * 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.bridge.IMessage; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.UnresolvedType; + +public class Bindings { + public static final Bindings NONE = new Bindings(0); + + private BindingPattern[] bindings; + + public Bindings(BindingPattern[] bindings) { + this.bindings = bindings; + } + + public Bindings(int count) { + this(new BindingPattern[count]); + } + + public void register(BindingPattern binding, IScope scope) { + int index = binding.getFormalIndex(); + BindingPattern existingBinding = bindings[index]; + if (existingBinding != null) { + scope.message(IMessage.ERROR, existingBinding, binding, "multiple bindings" + index + ", " + binding); + } + bindings[index] = binding; + } + + public void mergeIn(Bindings other, IScope scope) { + for (int i = 0, len = other.bindings.length; i < len; i++) { + if (other.bindings[i] != null) { + register(other.bindings[i], scope); + } + } + } + + /** + * signals an error if one has a binding and other doesn't + */ + public void checkEquals(Bindings other, IScope scope) { + BindingPattern[] b1 = this.bindings; + BindingPattern[] b2 = other.bindings; + int len = b1.length; + if (len != b2.length) { + throw new BCException("INSANE"); + } + + for (int i = 0; i < len; i++) { + if (b1[i] == null && b2[i] != null) { + scope.message(IMessage.ERROR, b2[i], "inconsistent binding"); + b1[i] = b2[i]; // done just to produce fewer error messages + } else if (b2[i] == null && b1[i] != null) { + scope.message(IMessage.ERROR, b1[i], "inconsistent binding"); + b2[i] = b1[i]; // done just to produce fewer error messages + } + } + } + + public String toString() { + StringBuffer buf = new StringBuffer("Bindings("); + for (int i = 0, len = bindings.length; i < len; i++) { + if (i > 0) + buf.append(", "); + buf.append(bindings[i]); + } + buf.append(")"); + return buf.toString(); + } + + public int[] getUsedFormals() { + // System.out.println("used: " + this); + int[] ret = new int[bindings.length]; + int index = 0; + for (int i = 0, len = bindings.length; i < len; i++) { + if (bindings[i] != null) { + ret[index++] = i; + } + } + int[] newRet = new int[index]; + System.arraycopy(ret, 0, newRet, 0, index); + // System.out.println("ret: " + index); + return newRet; + } + + public UnresolvedType[] getUsedFormalTypes() { + UnresolvedType[] ret = new UnresolvedType[bindings.length]; + int index = 0; + for (int i = 0, len = bindings.length; i < len; i++) { + if (bindings[i] != null) { + ret[index++] = ((BindingTypePattern) bindings[i]).getExactType(); + } + } + UnresolvedType[] newRet = new UnresolvedType[index]; + System.arraycopy(ret, 0, newRet, 0, index); + // System.out.println("ret: " + index); + return newRet; + } + + public Bindings copy() { + // int len = bindings.length; + // boolean[] a = new boolean[len]; + // System.arraycopy(bindings, 0, a, 0, len); + return new Bindings((BindingPattern[]) bindings.clone()); + } + + public void checkAllBound(IScope scope) { + for (int i = 0, len = bindings.length; i < len; i++) { + if (bindings[i] == null) { + // ATAJ: avoid warnings for implicit bindings + if (scope.getFormal(i) instanceof FormalBinding.ImplicitFormalBinding) { + bindings[i] = new BindingTypePattern(scope.getFormal(i), false); + } else { + scope.message(IMessage.ERROR, scope.getFormal(i), "formal unbound in pointcut "); + } + } + } + + } + + public int size() { + return bindings.length; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/CflowPointcut.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/CflowPointcut.java new file mode 100644 index 000000000..05ff2d824 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/CflowPointcut.java @@ -0,0 +1,353 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.aspectj.bridge.IMessage; +import org.aspectj.util.FileUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.Advice; +import org.aspectj.weaver.CrosscuttingMembers; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.NameMangler; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedMemberImpl; +import org.aspectj.weaver.ResolvedPointcutDefinition; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Test; + +public class CflowPointcut extends Pointcut { + private final Pointcut entry; // The pointcut inside the cflow() that + // represents the 'entry' point + boolean isBelow;// Is this cflowbelow? + private int[] freeVars; + + /** + * Used to indicate that we're in the context of a cflow when concretizing if's + * + * Will be removed or replaced with something better when we handle this as a non-error + */ + public static final ResolvedPointcutDefinition CFLOW_MARKER = new ResolvedPointcutDefinition(null, 0, null, + UnresolvedType.NONE, Pointcut.makeMatchesNothing(Pointcut.RESOLVED)); + + public CflowPointcut(Pointcut entry, boolean isBelow, int[] freeVars) { + // System.err.println("Building cflow pointcut "+entry.toString()); + this.entry = entry; + this.isBelow = isBelow; + this.freeVars = freeVars; + pointcutKind = CFLOW; + } + + /** + * @return Returns true is this is a cflowbelow pointcut + */ + public boolean isCflowBelow() { + return isBelow; + } + + public int couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS_BITS; + } + + // enh 76055 + public Pointcut getEntry() { + return entry; + } + + public FuzzyBoolean fastMatch(FastMatchInfo type) { + return FuzzyBoolean.MAYBE; + } + + protected FuzzyBoolean matchInternal(Shadow shadow) { + // ??? this is not maximally efficient + return FuzzyBoolean.MAYBE; + } + + public void write(DataOutputStream s) throws IOException { + s.writeByte(Pointcut.CFLOW); + entry.write(s); + s.writeBoolean(isBelow); + FileUtil.writeIntArray(freeVars, s); + writeLocation(s); + } + + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + + CflowPointcut ret = new CflowPointcut(Pointcut.read(s, context), s.readBoolean(), FileUtil.readIntArray(s)); + ret.readLocation(context, s); + return ret; + } + + public Pointcut parameterizeWith(Map typeVariableMap, World w) { + CflowPointcut ret = new CflowPointcut(entry.parameterizeWith(typeVariableMap, w), isBelow, freeVars); + ret.copyLocationFrom(this); + return ret; + } + + public void resolveBindings(IScope scope, Bindings bindings) { + if (bindings == null) { + entry.resolveBindings(scope, null); + entry.state = RESOLVED; + freeVars = new int[0]; + } else { + // ??? for if's sake we might need to be more careful here + Bindings entryBindings = new Bindings(bindings.size()); + + entry.resolveBindings(scope, entryBindings); + entry.state = RESOLVED; + + freeVars = entryBindings.getUsedFormals(); + + bindings.mergeIn(entryBindings, scope); + } + } + + public boolean equals(Object other) { + if (!(other instanceof CflowPointcut)) + return false; + CflowPointcut o = (CflowPointcut) other; + return o.entry.equals(entry) && o.isBelow == isBelow; + } + + public int hashCode() { + int result = 17; + result = 37 * result + entry.hashCode(); + result = 37 * result + (isBelow ? 0 : 1); + return result; + } + + public String toString() { + return "cflow" + (isBelow ? "below" : "") + "(" + entry + ")"; + } + + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + throw new RuntimeException("unimplemented - did concretization fail?"); + } + + public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + + // the pointcut is marked as CONCRETE after returning from this + // call - so we can't skip concretization + // if (this.entry.state == Pointcut.SYMBOLIC) { + // // too early to concretize, return unchanged + // return this; + // } + + // Enforce rule about which designators are supported in declare + if (isDeclare(bindings.getEnclosingAdvice())) { + inAspect.getWorld().showMessage(IMessage.ERROR, + WeaverMessages.format(WeaverMessages.CFLOW_IN_DECLARE, isBelow ? "below" : ""), + bindings.getEnclosingAdvice().getSourceLocation(), null); + return Pointcut.makeMatchesNothing(Pointcut.CONCRETE); + } + + // make this remap from formal positions to arrayIndices + IntMap entryBindings = new IntMap(); + if (freeVars != null) { + for (int i = 0, len = freeVars.length; i < len; i++) { + int freeVar = freeVars[i]; + // int formalIndex = bindings.get(freeVar); + entryBindings.put(freeVar, i); + } + } + entryBindings.copyContext(bindings); + // System.out.println(this + " bindings: " + entryBindings); + + World world = inAspect.getWorld(); + + Pointcut concreteEntry; + + ResolvedType concreteAspect = bindings.getConcreteAspect(); + + CrosscuttingMembers xcut = concreteAspect.crosscuttingMembers; + Collection previousCflowEntries = xcut.getCflowEntries(); + + entryBindings.pushEnclosingDefinition(CFLOW_MARKER); + // This block concretizes the pointcut within the cflow pointcut + try { + concreteEntry = entry.concretize(inAspect, declaringType, entryBindings); + } finally { + entryBindings.popEnclosingDefinitition(); + } + + List innerCflowEntries = new ArrayList(xcut.getCflowEntries()); + innerCflowEntries.removeAll(previousCflowEntries); + + // Four routes of interest through this code (did I hear someone say + // refactor??) + // 1) no state in the cflow - we can use a counter *and* we have seen + // this pointcut + // before - so use the same counter as before. + // 2) no state in the cflow - we can use a counter, but this is the + // first time + // we have seen this pointcut, so build the infrastructure. + // 3) state in the cflow - we need to use a stack *and* we have seen + // this pointcut + // before - so share the stack. + // 4) state in the cflow - we need to use a stack, but this is the first + // time + // we have seen this pointcut, so build the infrastructure. + + if (freeVars == null || freeVars.length == 0) { // No state, so don't + // use a stack, use a + // counter. + ResolvedMember localCflowField = null; + + Object field = getCflowfield(xcut, concreteEntry, concreteAspect, "counter"); + + // Check if we have already got a counter for this cflow pointcut + if (field != null) { + localCflowField = (ResolvedMember) field; // Use the one we + // already have + + } else { + + // Create a counter field in the aspect + localCflowField = new ResolvedMemberImpl(Member.FIELD, concreteAspect, Modifier.STATIC | Modifier.PUBLIC + | Modifier.FINAL, NameMangler.cflowCounter(xcut), UnresolvedType.forName(NameMangler.CFLOW_COUNTER_TYPE) + .getSignature()); + + // Create type munger to add field to the aspect + concreteAspect.crosscuttingMembers.addTypeMunger(world.getWeavingSupport().makeCflowCounterFieldAdder( + localCflowField)); + + // Create shadow munger to push stuff onto the stack + concreteAspect.crosscuttingMembers.addConcreteShadowMunger(Advice.makeCflowEntry(world, concreteEntry, isBelow, + localCflowField, freeVars == null ? 0 : freeVars.length, innerCflowEntries, inAspect)); + + putCflowfield(xcut, concreteEntry, concreteAspect, localCflowField, "counter"); // Remember + // it + } + + Pointcut ret = new ConcreteCflowPointcut(concreteAspect, localCflowField, null, true); + ret.copyLocationFrom(this); + return ret; + } else { + + List slots = new ArrayList(); + + for (int i = 0, len = freeVars.length; i < len; i++) { + int freeVar = freeVars[i]; + + // we don't need to keep state that isn't actually exposed to + // advice + // ??? this means that we will store some state that we won't + // actually use, optimize this later + if (!bindings.hasKey(freeVar)) + continue; + + int formalIndex = bindings.get(freeVar); + + // We need to look in the right place for the type of the + // formal. Suppose the advice looks like this: + // before(String s): somePointcut(*,s) + // where the first argument in somePointcut is of type Number + // for free variable 0 we want to ask the pointcut for the type + // of its first argument, if we only + // ask the advice for the type of its first argument then we'll + // get the wrong type (pr86903) + + ResolvedPointcutDefinition enclosingDef = bindings.peekEnclosingDefinition(); + ResolvedType formalType = null; + + // Is there a useful enclosing pointcut? + if (enclosingDef != null && enclosingDef.getParameterTypes().length > 0) { + formalType = enclosingDef.getParameterTypes()[freeVar].resolve(world); + } else { + formalType = bindings.getAdviceSignature().getParameterTypes()[formalIndex].resolve(world); + } + + ConcreteCflowPointcut.Slot slot = new ConcreteCflowPointcut.Slot(formalIndex, formalType, i); + slots.add(slot); + } + ResolvedMember localCflowField = null; + Object field = getCflowfield(xcut, concreteEntry, concreteAspect, "stack"); + if (field != null) { + localCflowField = (ResolvedMember) field; + } else { + + localCflowField = new ResolvedMemberImpl(Member.FIELD, concreteAspect, Modifier.STATIC | Modifier.PUBLIC + | Modifier.FINAL, NameMangler.cflowStack(xcut), UnresolvedType.forName(NameMangler.CFLOW_STACK_TYPE) + .getSignature()); + // System.out.println("adding field to: " + inAspect + " field " + // + cflowField); + + // add field and initializer to inAspect + // XXX and then that info above needs to be mapped down here to + // help with + // XXX getting the exposed state right + concreteAspect.crosscuttingMembers.addConcreteShadowMunger(Advice.makeCflowEntry(world, concreteEntry, isBelow, + localCflowField, freeVars.length, innerCflowEntries, inAspect)); + + concreteAspect.crosscuttingMembers.addTypeMunger(world.getWeavingSupport() + .makeCflowStackFieldAdder(localCflowField)); + putCflowfield(xcut, concreteEntry, concreteAspect, localCflowField, "stack"); + } + Pointcut ret = new ConcreteCflowPointcut(concreteAspect, localCflowField, slots, false); + ret.copyLocationFrom(this); + return ret; + } + + } + + private String getKey(Pointcut p, ResolvedType a, String stackOrCounter) { + StringBuffer sb = new StringBuffer(); + sb.append(a.getName()); + sb.append("::"); + sb.append(p.toString()); + sb.append("::"); + sb.append(stackOrCounter); + return sb.toString(); + } + + private Object getCflowfield(CrosscuttingMembers xcut, Pointcut pcutkey, ResolvedType concreteAspect, String stackOrCounter) { + String key = getKey(pcutkey, concreteAspect, stackOrCounter); + Object o = null; + if (isBelow) + o = xcut.getCflowBelowFields().get(key); + else + o = xcut.getCflowFields().get(key); + // System.err.println("Retrieving for key "+key+" returning "+o); + return o; + } + + private void putCflowfield(CrosscuttingMembers xcut, Pointcut pcutkey, ResolvedType concreteAspect, Object o, + String stackOrCounter) { + String key = getKey(pcutkey, concreteAspect, stackOrCounter); + // System.err.println("Storing cflow field for key"+key); + if (isBelow) { + xcut.getCflowBelowFields().put(key, o); + } else { + xcut.getCflowFields().put(key, o); + } + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ConcreteCflowPointcut.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ConcreteCflowPointcut.java new file mode 100644 index 000000000..842384b01 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ConcreteCflowPointcut.java @@ -0,0 +1,184 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.Message; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.MemberImpl; +import org.aspectj.weaver.NameMangler; +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.ast.Expr; +import org.aspectj.weaver.ast.Test; + +public class ConcreteCflowPointcut extends Pointcut { + private final Member cflowField; + List/* Slot */slots; // exposed for testing + boolean usesCounter; + ResolvedType aspect; + + // Can either use a counter or a stack to implement cflow. + public ConcreteCflowPointcut(ResolvedType aspect, Member cflowField, List slots, boolean usesCounter) { + this.aspect = aspect; + this.cflowField = cflowField; + this.slots = slots; + this.usesCounter = usesCounter; + this.pointcutKind = CFLOW; + } + + public int couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS_BITS; + } + + public FuzzyBoolean fastMatch(FastMatchInfo type) { + return FuzzyBoolean.MAYBE; + } + + protected FuzzyBoolean matchInternal(Shadow shadow) { + // ??? this is not maximally efficient + // Check we'll be able to do the residue! + + // this bit is for pr145693 - we cannot match at all if one of the types is missing, we will be unable + // to create the residue + if (slots != null) { + for (Iterator i = slots.iterator(); i.hasNext();) { + Slot slot = (Slot) i.next(); + ResolvedType rt = slot.formalType; + if (rt.isMissing()) { + ISourceLocation[] locs = new ISourceLocation[] { getSourceLocation() }; + Message m = new Message(WeaverMessages.format(WeaverMessages.MISSING_TYPE_PREVENTS_MATCH, rt.getName()), "", + Message.WARNING, shadow.getSourceLocation(), null, locs); + rt.getWorld().getMessageHandler().handleMessage(m); + return FuzzyBoolean.NO; + } + } + } + return FuzzyBoolean.MAYBE; + } + + // used by weaver when validating bindings + public int[] getUsedFormalSlots() { + if (slots == null) + return new int[0]; + int[] indices = new int[slots.size()]; + for (int i = 0; i < indices.length; i++) { + indices[i] = ((Slot) slots.get(i)).formalIndex; + } + return indices; + } + + public void write(DataOutputStream s) throws IOException { + throw new RuntimeException("unimplemented"); + } + + public void resolveBindings(IScope scope, Bindings bindings) { + throw new RuntimeException("unimplemented"); + } + + public Pointcut parameterizeWith(Map typeVariableMap, World w) { + throw new RuntimeException("unimplemented"); + } + + public boolean equals(Object other) { + if (!(other instanceof ConcreteCflowPointcut)) + return false; + ConcreteCflowPointcut o = (ConcreteCflowPointcut) other; + return o.cflowField.equals(this.cflowField); + } + + public int hashCode() { + int result = 17; + result = 37 * result + cflowField.hashCode(); + return result; + } + + public String toString() { + return "concretecflow(" + cflowField + ")"; + } + + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + // System.out.println("find residue: " + this); + if (usesCounter) { + return Test.makeFieldGetCall(cflowField, cflowCounterIsValidMethod, Expr.NONE); + } else { + if (slots != null) { // null for cflows managed by counters + for (Iterator i = slots.iterator(); i.hasNext();) { + Slot slot = (Slot) i.next(); + // System.out.println("slot: " + slot.formalIndex); + state.set(slot.formalIndex, aspect.getWorld().getWeavingSupport().makeCflowAccessVar(slot.formalType, + cflowField, slot.arrayIndex)); + } + } + return Test.makeFieldGetCall(cflowField, cflowStackIsValidMethod, Expr.NONE); + } + } + + private static final Member cflowStackIsValidMethod = MemberImpl.method(NameMangler.CFLOW_STACK_UNRESOLVEDTYPE, 0, + ResolvedType.BOOLEAN, "isValid", UnresolvedType.NONE); + + private static final Member cflowCounterIsValidMethod = MemberImpl.method(NameMangler.CFLOW_COUNTER_UNRESOLVEDTYPE, 0, + ResolvedType.BOOLEAN, "isValid", UnresolvedType.NONE); + + public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + throw new RuntimeException("unimplemented"); + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public static class Slot { + int formalIndex; + ResolvedType formalType; + int arrayIndex; + + public Slot(int formalIndex, ResolvedType formalType, int arrayIndex) { + this.formalIndex = formalIndex; + this.formalType = formalType; + this.arrayIndex = arrayIndex; + } + + public boolean equals(Object other) { + if (!(other instanceof Slot)) + return false; + + Slot o = (Slot) other; + return o.formalIndex == this.formalIndex && o.arrayIndex == this.arrayIndex && o.formalType.equals(this.formalType); + } + + public int hashCode() { + int result = 19; + result = 37 * result + formalIndex; + result = 37 * result + arrayIndex; + result = 37 * result + formalType.hashCode(); + return result; + } + + public String toString() { + return "Slot(" + formalIndex + ", " + formalType + ", " + arrayIndex + ")"; + } + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/Declare.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/Declare.java new file mode 100644 index 000000000..174001047 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/Declare.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.patterns; + +import java.io.IOException; +import java.util.Map; + +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; + +public abstract class Declare extends PatternNode { + public static final byte ERROR_OR_WARNING = 1; + public static final byte PARENTS = 2; + public static final byte SOFT = 3; + public static final byte DOMINATES = 4; + public static final byte ANNOTATION = 5; + + // set when reading declare from aspect + private ResolvedType declaringType; + + public static Declare read(VersionedDataInputStream s, ISourceContext context) throws IOException { + byte kind = s.readByte(); + switch (kind) { + case ERROR_OR_WARNING: + return DeclareErrorOrWarning.read(s, context); + case DOMINATES: + return DeclarePrecedence.read(s, context); + case PARENTS: + return DeclareParents.read(s, context); + case SOFT: + return DeclareSoft.read(s, context); + case ANNOTATION: + return DeclareAnnotation.read(s,context); + default: + throw new RuntimeException("unimplemented"); + } + } + + /** + * Returns this declare mutated + */ + public abstract void resolve(IScope scope); + + /** + * Returns a version of this declare element in which all references to type variables + * are replaced with their bindings given in the map. + */ + public abstract Declare parameterizeWith(Map typeVariableBindingMap,World w); + + /** + * Indicates if this declare should be treated like advice. If true, the + * declare will have no effect in an abstract aspect. It will be inherited by + * any concrete aspects and will have an effect for each concrete aspect it + * is ultimately inherited by. + */ + public abstract boolean isAdviceLike(); + + /** + * Declares have methods in the .class file against which info can be stored + * (for example, the annotation in the case of declare annotation). The + * name is of the form ajc$declare_XXX_NNN where XXX can optionally be set in + * this 'getNameSuffix()' method - depending on whether, at weave time, we + * want to easily differentiate between the declare methods. + */ + public abstract String getNameSuffix(); + + public void setDeclaringType(ResolvedType aType) { + this.declaringType = aType; + } + + public ResolvedType getDeclaringType() { + return declaringType; + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/DeclareAnnotation.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/DeclareAnnotation.java new file mode 100644 index 000000000..3958566be --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/DeclareAnnotation.java @@ -0,0 +1,408 @@ +/* ******************************************************************* + * Copyright (c) 2005 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: + * Adrian Colyer initial implementation + * Andy Clement got it working + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Iterator; +import java.util.Map; + +import org.aspectj.bridge.MessageUtil; +import org.aspectj.weaver.AnnotationAJ; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ResolvedMember; +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; + +public class DeclareAnnotation extends Declare { + + public static final Kind AT_TYPE = new Kind(1, "type"); + public static final Kind AT_FIELD = new Kind(2, "field"); + public static final Kind AT_METHOD = new Kind(3, "method"); + public static final Kind AT_CONSTRUCTOR = new Kind(4, "constructor"); + + private Kind kind; + private TypePattern typePattern; // for declare @type + private SignaturePattern sigPattern; // for declare + // @field,@method,@constructor + private String annotationMethod = "unknown"; + private String annotationString = "@"; + private ResolvedType containingAspect; + private AnnotationAJ annotation; + + /** + * Captures type of declare annotation (method/type/field/constructor) + */ + public static class Kind { + private final int id; + private String s; + + private Kind(int n, String name) { + id = n; + s = name; + } + + public int hashCode() { + return (19 + 37 * id); + } + + public boolean equals(Object obj) { + if (!(obj instanceof Kind)) + return false; + Kind other = (Kind) obj; + return other.id == id; + } + + public String toString() { + return "at_" + s; + } + } + + public DeclareAnnotation(Kind kind, TypePattern typePattern) { + this.typePattern = typePattern; + this.kind = kind; + } + + /** + * Returns the string, useful before the real annotation has been resolved + */ + public String getAnnotationString() { + return annotationString; + } + + public DeclareAnnotation(Kind kind, SignaturePattern sigPattern) { + this.sigPattern = sigPattern; + this.kind = kind; + } + + public boolean isExactPattern() { + return typePattern instanceof ExactTypePattern; + } + + public String getAnnotationMethod() { + return annotationMethod; + } + + public String toString() { + StringBuffer ret = new StringBuffer(); + ret.append("declare @"); + ret.append(kind); + ret.append(" : "); + ret.append(typePattern != null ? typePattern.toString() : sigPattern + .toString()); + ret.append(" : "); + ret.append(annotationString); + return ret.toString(); + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public void resolve(IScope scope) { + if (!scope.getWorld().isInJava5Mode()) { + String msg = null; + if (kind == AT_TYPE) { + msg = WeaverMessages.DECLARE_ATTYPE_ONLY_SUPPORTED_AT_JAVA5_LEVEL; + } else if (kind == AT_METHOD) { + msg = WeaverMessages.DECLARE_ATMETHOD_ONLY_SUPPORTED_AT_JAVA5_LEVEL; + } else if (kind == AT_FIELD) { + msg = WeaverMessages.DECLARE_ATFIELD_ONLY_SUPPORTED_AT_JAVA5_LEVEL; + } else if (kind == AT_CONSTRUCTOR) { + msg = WeaverMessages.DECLARE_ATCONS_ONLY_SUPPORTED_AT_JAVA5_LEVEL; + } + scope.message(MessageUtil.error(WeaverMessages.format(msg), + getSourceLocation())); + return; + } + if (typePattern != null) { + typePattern = typePattern.resolveBindings(scope, Bindings.NONE, + false, false); + } + if (sigPattern != null) { + sigPattern = sigPattern.resolveBindings(scope, Bindings.NONE); + } + this.containingAspect = scope.getEnclosingType(); + } + + public Declare parameterizeWith(Map typeVariableBindingMap, World w) { + DeclareAnnotation ret; + if (this.kind == AT_TYPE) { + ret = new DeclareAnnotation(kind, this.typePattern + .parameterizeWith(typeVariableBindingMap, w)); + } else { + ret = new DeclareAnnotation(kind, this.sigPattern.parameterizeWith( + typeVariableBindingMap, w)); + } + ret.annotationMethod = this.annotationMethod; + ret.annotationString = this.annotationString; + ret.containingAspect = this.containingAspect; + ret.annotation = this.annotation; + ret.copyLocationFrom(this); + return ret; + } + + public boolean isAdviceLike() { + return false; + } + + public void setAnnotationString(String as) { + this.annotationString = as; + } + + public void setAnnotationMethod(String methName) { + this.annotationMethod = methName; + } + + public boolean equals(Object obj) { + if (!(obj instanceof DeclareAnnotation)) + return false; + DeclareAnnotation other = (DeclareAnnotation) obj; + if (!this.kind.equals(other.kind)) + return false; + if (!this.annotationString.equals(other.annotationString)) + return false; + if (!this.annotationMethod.equals(other.annotationMethod)) + return false; + if (this.typePattern != null) { + if (!typePattern.equals(other.typePattern)) + return false; + } + if (this.sigPattern != null) { + if (!sigPattern.equals(other.sigPattern)) + return false; + } + return true; + } + + public int hashCode() { + int result = 19; + result = 37 * result + kind.hashCode(); + result = 37 * result + annotationString.hashCode(); + result = 37 * result + annotationMethod.hashCode(); + if (typePattern != null) + result = 37 * result + typePattern.hashCode(); + if (sigPattern != null) + result = 37 * result + sigPattern.hashCode(); + return result; + } + + /* + * (non-Javadoc) + * + * @see + * org.aspectj.weaver.patterns.PatternNode#write(java.io.DataOutputStream) + */ + public void write(DataOutputStream s) throws IOException { + s.writeByte(Declare.ANNOTATION); + s.writeInt(kind.id); + s.writeUTF(annotationString); + s.writeUTF(annotationMethod); + if (typePattern != null) + typePattern.write(s); + if (sigPattern != null) + sigPattern.write(s); + writeLocation(s); + } + + public static Declare read(VersionedDataInputStream s, + ISourceContext context) throws IOException { + DeclareAnnotation ret = null; + int kind = s.readInt(); + String annotationString = s.readUTF(); + String annotationMethod = s.readUTF(); + TypePattern tp = null; + SignaturePattern sp = null; + switch (kind) { + case 1: + tp = TypePattern.read(s, context); + ret = new DeclareAnnotation(AT_TYPE, tp); + break; + case 2: + sp = SignaturePattern.read(s, context); + ret = new DeclareAnnotation(AT_FIELD, sp); + break; + case 3: + sp = SignaturePattern.read(s, context); + ret = new DeclareAnnotation(AT_METHOD, sp); + break; + case 4: + sp = SignaturePattern.read(s, context); + ret = new DeclareAnnotation(AT_CONSTRUCTOR, sp); + break; + + } + // if (kind==AT_TYPE.id) { + // tp = TypePattern.read(s,context); + // ret = new DeclareAnnotation(AT_TYPE,tp); + // } else { + // sp = SignaturePattern.read(s,context); + // ret = new DeclareAnnotation(kind,sp); + // } + ret.setAnnotationString(annotationString); + ret.setAnnotationMethod(annotationMethod); + ret.readLocation(context, s); + return ret; + } + + // public boolean getAnnotationIfMatches(ResolvedType onType) { + // return (match(onType)); + // } + + /** + * For @constructor, @method, @field + */ + public boolean matches(ResolvedMember rm, World world) { + return sigPattern.matches(rm, world, false); + } + + /** + * For @type + */ + public boolean matches(ResolvedType typeX) { + if (!typePattern.matchesStatically(typeX)) + return false; + if (typeX.getWorld().getLint().typeNotExposedToWeaver.isEnabled() + && !typeX.isExposedToWeaver()) { + typeX.getWorld().getLint().typeNotExposedToWeaver.signal(typeX + .getName(), getSourceLocation()); + } + return true; + } + + public void setAspect(ResolvedType typeX) { + containingAspect = typeX; + } + + public UnresolvedType getAspect() { + return containingAspect; + } + + public void copyAnnotationTo(ResolvedType onType) { + ensureAnnotationDiscovered(); + if (!onType.hasAnnotation(annotation.getType())) { + onType.addAnnotation(annotation); + } + } + + public AnnotationAJ getAnnotationX() { + ensureAnnotationDiscovered(); + return annotation; + } + + /** + * The annotation specified in the declare @type is stored against a simple + * method of the form "ajc$declare_", this method finds that method and + * retrieves the annotation + */ + private void ensureAnnotationDiscovered() { + if (annotation != null) + return; + for (Iterator iter = containingAspect.getMethods(); iter.hasNext();) { + ResolvedMember member = (ResolvedMember) iter.next(); + if (member.getName().equals(annotationMethod)) { + annotation = member.getAnnotations()[0]; + } + } + } + + public TypePattern getTypePattern() { + return typePattern; + } + + public SignaturePattern getSignaturePattern() { + return sigPattern; + } + + public boolean isStarredAnnotationPattern() { + if (typePattern != null) + return typePattern.isStarAnnotation(); + if (sigPattern != null) + return sigPattern.isStarAnnotation(); + throw new RuntimeException("Impossible! what kind of deca is this: " + + this); + } + + public Kind getKind() { + return kind; + } + + public boolean isDeclareAtConstuctor() { + return kind.equals(AT_CONSTRUCTOR); + } + + public boolean isDeclareAtMethod() { + return kind.equals(AT_METHOD); + } + + public boolean isDeclareAtType() { + return kind.equals(AT_TYPE); + } + + public boolean isDeclareAtField() { + return kind.equals(AT_FIELD); + } + + /** + * @return UnresolvedType for the annotation + */ + public UnresolvedType getAnnotationTypeX() { + ensureAnnotationDiscovered(); + return this.annotation.getType(); + } + + /** + * @return true if the annotation specified is allowed on a field + */ + public boolean isAnnotationAllowedOnField() { + ensureAnnotationDiscovered(); + return annotation.allowedOnField(); + } + + public String getPatternAsString() { + if (sigPattern != null) + return sigPattern.toString(); + if (typePattern != null) + return typePattern.toString(); + return "DONT KNOW"; + } + + /** + * Return true if this declare annotation could ever match something in the + * specified type - only really able to make intelligent decision if a type + * was specified in the sig/type pattern signature. + */ + public boolean couldEverMatch(ResolvedType type) { + // Haven't implemented variant for typePattern (doesn't seem worth it!) + // BUGWARNING This test might not be sufficient for funny cases relating + // to interfaces and the use of '+' - but it seems really important to + // do something here so we don't iterate over all fields and all methods + // in all types exposed to the weaver! So look out for bugs here and + // we can update the test as appropriate. + if (sigPattern != null) + return sigPattern.getDeclaringType().matches(type, + TypePattern.STATIC).maybeTrue(); + return true; + } + + /** + * Provide a name suffix so that we can tell the different declare + * annotations forms apart in the AjProblemReporter + */ + public String getNameSuffix() { + return getKind().toString(); + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/DeclareErrorOrWarning.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/DeclareErrorOrWarning.java new file mode 100644 index 000000000..c8922933b --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/DeclareErrorOrWarning.java @@ -0,0 +1,129 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.util.Map; + +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; + +public class DeclareErrorOrWarning extends Declare { + private boolean isError; + private Pointcut pointcut; + private String message; + + public DeclareErrorOrWarning(boolean isError, Pointcut pointcut, String message) { + this.isError = isError; + this.pointcut = pointcut; + this.message = message; + } + + /** + * returns "declare warning: " or "declare error: " + */ + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("declare "); + if (isError) buf.append("error: "); + else buf.append("warning: "); + buf.append(pointcut); + buf.append(": "); + buf.append("\""); + buf.append(message); + buf.append("\";"); + return buf.toString(); + } + + public boolean equals(Object other) { + if (!(other instanceof DeclareErrorOrWarning)) return false; + DeclareErrorOrWarning o = (DeclareErrorOrWarning)other; + return (o.isError == isError) && + o.pointcut.equals(pointcut) && + o.message.equals(message); + } + + public int hashCode() { + int result = isError ? 19 : 23; + result = 37*result + pointcut.hashCode(); + result = 37*result + message.hashCode(); + return result; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this,data); + } + + public void write(DataOutputStream s) throws IOException { + s.writeByte(Declare.ERROR_OR_WARNING); + s.writeBoolean(isError); + pointcut.write(s); + s.writeUTF(message); + writeLocation(s); + } + + public static Declare read(VersionedDataInputStream s, ISourceContext context) throws IOException { + Declare ret = new DeclareErrorOrWarning( + s.readBoolean(), + Pointcut.read(s, context), + s.readUTF() + ); + ret.readLocation(context, s); + return ret; + } + + + public boolean isError() { + return isError; + } + + public String getMessage() { + return message; + } + + public Pointcut getPointcut() { + return pointcut; + } + + public void resolve(IScope scope) { + pointcut = pointcut.resolve(scope); + } + + public Declare parameterizeWith(Map typeVariableBindingMap,World w) { + Declare ret = new DeclareErrorOrWarning(isError,pointcut.parameterizeWith(typeVariableBindingMap,w),message); + ret.copyLocationFrom(this); + return ret; + } + + public boolean isAdviceLike() { + return true; + } + + public String getNameSuffix() { + return "eow"; + } + + /** + * returns "declare warning" or "declare error" + */ + public String getName() { + StringBuffer buf = new StringBuffer(); + buf.append("declare "); + if (isError) buf.append("error"); + else buf.append("warning"); + return buf.toString(); + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/DeclareParents.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/DeclareParents.java new file mode 100644 index 000000000..3d97bcad7 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/DeclareParents.java @@ -0,0 +1,352 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.Message; +import org.aspectj.weaver.ISourceContext; +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; + +public class DeclareParents extends Declare { + private TypePattern child; + private TypePatternList parents; + private boolean isWildChild = false; + private boolean isExtends = true; +// private String[] typeVariablesInScope = new String[0]; // AspectJ 5 extension for generic types + + + public DeclareParents(TypePattern child, List parents, boolean isExtends) { + this(child, new TypePatternList(parents),isExtends); + } + + private DeclareParents(TypePattern child, TypePatternList parents, boolean isExtends) { + this.child = child; + this.parents = parents; + this.isExtends = isExtends; + if (child instanceof WildTypePattern) isWildChild = true; + } + +// public String[] getTypeParameterNames() { +// return this.typeVariablesInScope; +// } +// +// public void setTypeParametersInScope(String[] typeParameters) { +// this.typeVariablesInScope = typeParameters; +// } + + public boolean match(ResolvedType typeX) { + if (!child.matchesStatically(typeX)) return false; + if (typeX.getWorld().getLint().typeNotExposedToWeaver.isEnabled() && + !typeX.isExposedToWeaver()) + { + typeX.getWorld().getLint().typeNotExposedToWeaver.signal(typeX.getName(), getSourceLocation()); + } + + return true; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this,data); + } + + public Declare parameterizeWith(Map typeVariableBindingMap,World w) { + DeclareParents ret = + new DeclareParents( + child.parameterizeWith(typeVariableBindingMap,w), + parents.parameterizeWith(typeVariableBindingMap,w), + isExtends); + ret.copyLocationFrom(this); + return ret; + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("declare parents: "); + buf.append(child); + buf.append(isExtends ? " extends " : " implements "); //extends and implements are treated equivalently + buf.append(parents); + buf.append(";"); + return buf.toString(); + } + + public boolean equals(Object other) { + if (!(other instanceof DeclareParents)) return false; + DeclareParents o = (DeclareParents)other; + return o.child.equals(child) && o.parents.equals(parents); + } + + //??? cache this + public int hashCode() { + int result = 23; + result = 37*result + child.hashCode(); + result = 37*result + parents.hashCode(); + return result; + } + + + public void write(DataOutputStream s) throws IOException { + s.writeByte(Declare.PARENTS); + child.write(s); + parents.write(s); +// s.writeInt(typeVariablesInScope.length); +// for (int i = 0; i < typeVariablesInScope.length; i++) { +// s.writeUTF(typeVariablesInScope[i]); +// } + writeLocation(s); + } + + public static Declare read(VersionedDataInputStream s, ISourceContext context) throws IOException { + DeclareParents ret = new DeclareParents(TypePattern.read(s, context), TypePatternList.read(s, context),true); +// if (s.getMajorVersion()>=AjAttribute.WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ150) { +// int numTypeVariablesInScope = s.readInt(); +// ret.typeVariablesInScope = new String[numTypeVariablesInScope]; +// for (int i = 0; i < numTypeVariablesInScope; i++) { +// ret.typeVariablesInScope[i] = s.readUTF(); +// } +// } + ret.readLocation(context, s); + return ret; + } + + public boolean parentsIncludeInterface(World w) { + for (int i = 0; i < parents.size(); i++) { + if (parents.get(i).getExactType().resolve(w).isInterface()) return true; + } + return false; + } + public boolean parentsIncludeClass(World w) { + for (int i = 0; i < parents.size(); i++) { + if (parents.get(i).getExactType().resolve(w).isClass()) return true; + } + return false; + } + + public void resolve(IScope scope) { +// ScopeWithTypeVariables resolutionScope = new ScopeWithTypeVariables(typeVariablesInScope,scope); + child = child.resolveBindings(scope, Bindings.NONE, false, false); + parents = parents.resolveBindings(scope, Bindings.NONE, false, true); + +// Could assert this ... +// for (int i=0; i < parents.size(); i++) { +// parents.get(i).assertExactType(scope.getMessageHandler()); +// } + } + + public TypePatternList getParents() { + return parents; + } + + public TypePattern getChild() { + return child; + } + + // note - will always return true after deserialization, this doesn't affect weaver + public boolean isExtends() { + return this.isExtends; + } + + public boolean isAdviceLike() { + return false; + } + + private ResolvedType maybeGetNewParent(ResolvedType targetType, TypePattern typePattern, World world,boolean reportErrors) { + if (typePattern == TypePattern.NO) return null; // already had an error here + UnresolvedType iType = typePattern.getExactType(); + ResolvedType parentType = iType.resolve(world); + + if (targetType.equals(world.getCoreType(UnresolvedType.OBJECT))) { + world.showMessage(IMessage.ERROR, + WeaverMessages.format(WeaverMessages.DECP_OBJECT), + this.getSourceLocation(), null); + return null; + } + + // Ensure the target doesn't already have an + // alternate parameterization of the generic type on it + if (parentType.isParameterizedType() || parentType.isRawType()) { + // Let's take a look at the parents we already have + boolean isOK = verifyNoInheritedAlternateParameterization(targetType,parentType,world); + if (!isOK) return null; + } + + if (parentType.isAssignableFrom(targetType)) return null; // already a parent + + // Enum types that are targetted for decp through a wild type pattern get linted + if (reportErrors && isWildChild && targetType.isEnum()) { + world.getLint().enumAsTargetForDecpIgnored.signal(targetType.toString(),getSourceLocation()); + } + + // Annotation types that are targetted for decp through a wild type pattern get linted + if (reportErrors && isWildChild && targetType.isAnnotation()) { + world.getLint().annotationAsTargetForDecpIgnored.signal(targetType.toString(),getSourceLocation()); + } + + // 1. Can't use decp to make an enum/annotation type implement an interface + if (targetType.isEnum() && parentType.isInterface()) { + if (reportErrors && !isWildChild) { + world.showMessage(IMessage.ERROR, + WeaverMessages.format(WeaverMessages.CANT_DECP_ON_ENUM_TO_IMPL_INTERFACE,targetType),getSourceLocation(),null); + } + return null; + } + if (targetType.isAnnotation() && parentType.isInterface()) { + if (reportErrors && !isWildChild) { + world.showMessage(IMessage.ERROR,WeaverMessages.format(WeaverMessages.CANT_DECP_ON_ANNOTATION_TO_IMPL_INTERFACE,targetType),getSourceLocation(),null); + } + return null; + } + + // 2. Can't use decp to change supertype of an enum/annotation + if (targetType.isEnum() && parentType.isClass()) { + if (reportErrors && !isWildChild) { + world.showMessage(IMessage.ERROR,WeaverMessages.format(WeaverMessages.CANT_DECP_ON_ENUM_TO_EXTEND_CLASS,targetType),getSourceLocation(),null); + } + return null; + } + if (targetType.isAnnotation() && parentType.isClass()) { + if (reportErrors && !isWildChild) { + world.showMessage(IMessage.ERROR,WeaverMessages.format(WeaverMessages.CANT_DECP_ON_ANNOTATION_TO_EXTEND_CLASS,targetType),getSourceLocation(),null); + } + return null; + } + + // 3. Can't use decp to declare java.lang.Enum/java.lang.annotation.Annotation as the parent of a type + if (parentType.getSignature().equals(UnresolvedType.ENUM.getSignature())) { + if (reportErrors && !isWildChild) { + world.showMessage(IMessage.ERROR, + WeaverMessages.format(WeaverMessages.CANT_DECP_TO_MAKE_ENUM_SUPERTYPE,targetType),getSourceLocation(),null); + } + return null; + } + if (parentType.getSignature().equals(UnresolvedType.ANNOTATION.getSignature())) { + if (reportErrors && !isWildChild) { + world.showMessage(IMessage.ERROR, + WeaverMessages.format(WeaverMessages.CANT_DECP_TO_MAKE_ANNOTATION_SUPERTYPE,targetType),getSourceLocation(),null); + } + return null; + } + + if (parentType.isAssignableFrom(targetType)) return null; // already a parent + + if (targetType.isAssignableFrom(parentType)) { + world.showMessage(IMessage.ERROR, + WeaverMessages.format(WeaverMessages.CANT_EXTEND_SELF,targetType.getName()), + this.getSourceLocation(), null + ); + return null; + } + + if (parentType.isClass()) { + if (targetType.isInterface()) { + world.showMessage(IMessage.ERROR, + WeaverMessages.format(WeaverMessages.INTERFACE_CANT_EXTEND_CLASS), + this.getSourceLocation(), null + ); + return null; + // how to handle xcutting errors??? + } + + if (!targetType.getSuperclass().isAssignableFrom(parentType)) { + world.showMessage(IMessage.ERROR, + WeaverMessages.format(WeaverMessages.DECP_HIERARCHY_ERROR, + iType.getName(), + targetType.getSuperclass().getName()), + this.getSourceLocation(), null + ); + return null; + } else { + return parentType; + } + } else { + return parentType; + } + } + + /** + * This method looks through the type hierarchy for some target type - it is attempting to + * find an existing parameterization that clashes with the new parent that the user + * wants to apply to the type. If it finds an existing parameterization that matches the + * new one, it silently completes, if it finds one that clashes (e.g. a type already has + * A when the user wants to add A) then it will produce an error. + * + * It uses recursion and exits recursion on hitting 'jlObject' + * + * Related bugzilla entries: pr110788 + */ + private boolean verifyNoInheritedAlternateParameterization(ResolvedType typeToVerify,ResolvedType newParent,World world) { + + + if (typeToVerify.equals(ResolvedType.OBJECT)) return true; + + ResolvedType newParentGenericType = newParent.getGenericType(); + Iterator iter = typeToVerify.getDirectSupertypes(); + while (iter.hasNext()) { + ResolvedType supertype = (ResolvedType)iter.next(); + if ( ((supertype.isRawType() && newParent.isParameterizedType()) || + (supertype.isParameterizedType() && newParent.isRawType())) && newParentGenericType.equals(supertype.getGenericType())) { + // new parent is a parameterized type, but this is a raw type + world.getMessageHandler().handleMessage(new Message( + WeaverMessages.format(WeaverMessages.CANT_DECP_MULTIPLE_PARAMETERIZATIONS,newParent.getName(),typeToVerify.getName(),supertype.getName()), + getSourceLocation(), true, new ISourceLocation[]{typeToVerify.getSourceLocation()})); + return false; + } + if (supertype.isParameterizedType()) { + ResolvedType generictype = supertype.getGenericType(); + + // If the generic types are compatible but the parameterizations aren't then we have a problem + if (generictype.isAssignableFrom(newParentGenericType) && + !supertype.isAssignableFrom(newParent)) { + world.getMessageHandler().handleMessage(new Message( + WeaverMessages.format(WeaverMessages.CANT_DECP_MULTIPLE_PARAMETERIZATIONS,newParent.getName(),typeToVerify.getName(),supertype.getName()), + getSourceLocation(), true, new ISourceLocation[]{typeToVerify.getSourceLocation()})); + return false; + } + } + if (!verifyNoInheritedAlternateParameterization(supertype,newParent,world)) { + return false; + } + } + return true; + } + + public List/**/ findMatchingNewParents(ResolvedType onType,boolean reportErrors) { + if (onType.isRawType()) onType = onType.getGenericType(); + if (!match(onType)) return Collections.EMPTY_LIST; + + List ret = new ArrayList(); + for (int i=0; i < parents.size(); i++) { + ResolvedType t = maybeGetNewParent(onType, parents.get(i), onType.getWorld(),reportErrors); + if (t != null) ret.add(t); + } + + return ret; + } + + public String getNameSuffix() { + return "parents"; + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/DeclarePrecedence.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/DeclarePrecedence.java new file mode 100644 index 000000000..810f5ed91 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/DeclarePrecedence.java @@ -0,0 +1,166 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import org.aspectj.bridge.IMessage; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; + +public class DeclarePrecedence extends Declare { + private TypePatternList patterns; + + + public DeclarePrecedence(List patterns) { + this(new TypePatternList(patterns)); + } + + private DeclarePrecedence(TypePatternList patterns) { + this.patterns = patterns; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this,data); + } + + public Declare parameterizeWith(Map typeVariableBindingMap,World w) { + DeclarePrecedence ret = new DeclarePrecedence(this.patterns.parameterizeWith(typeVariableBindingMap,w)); + ret.copyLocationFrom(this); + return ret; + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("declare precedence: "); + buf.append(patterns); + buf.append(";"); + return buf.toString(); + } + + public boolean equals(Object other) { + if (!(other instanceof DeclarePrecedence)) return false; + DeclarePrecedence o = (DeclarePrecedence)other; + return o.patterns.equals(patterns); + } + + public int hashCode() { + return patterns.hashCode(); + } + + + public void write(DataOutputStream s) throws IOException { + s.writeByte(Declare.DOMINATES); + patterns.write(s); + writeLocation(s); + } + + public static Declare read(VersionedDataInputStream s, ISourceContext context) throws IOException { + Declare ret = new DeclarePrecedence(TypePatternList.read(s, context)); + ret.readLocation(context, s); + return ret; + } + + public void resolve(IScope scope) { + patterns = patterns.resolveBindings(scope, Bindings.NONE, false, false); + boolean seenStar = false; + + for (int i=0; i < patterns.size(); i++) { + TypePattern pi = patterns.get(i); + if (pi.isStar()) { + if (seenStar) { + scope.getWorld().showMessage(IMessage.ERROR, + WeaverMessages.format(WeaverMessages.TWO_STARS_IN_PRECEDENCE), + pi.getSourceLocation(), null); + } + seenStar = true; + continue; + } + ResolvedType exactType = pi.getExactType().resolve(scope.getWorld()); + if (exactType.isMissing()) continue; + + // Cannot do a dec prec specifying a non-aspect types unless suffixed with a '+' + if (!exactType.isAspect() && !pi.isIncludeSubtypes() && !exactType.isTypeVariableReference()) { + scope.getWorld().showMessage(IMessage.ERROR, + WeaverMessages.format(WeaverMessages.CLASSES_IN_PRECEDENCE,exactType.getName()), + pi.getSourceLocation(),null); + } + + for (int j=0; j < patterns.size(); j++) { + if (j == i) continue; + TypePattern pj = patterns.get(j); + if (pj.isStar()) continue; + if (pj.matchesStatically(exactType)) { + scope.getWorld().showMessage(IMessage.ERROR, + WeaverMessages.format(WeaverMessages.TWO_PATTERN_MATCHES_IN_PRECEDENCE,exactType.getName()), + pi.getSourceLocation(), pj.getSourceLocation()); + } + } + } + } + + public TypePatternList getPatterns() { + return patterns; + } + + private int matchingIndex(ResolvedType a) { + int knownMatch = -1; + int starMatch = -1; + for (int i=0, len=patterns.size(); i < len; i++) { + TypePattern p = patterns.get(i); + if (p.isStar()) { + starMatch = i; + } else if (p.matchesStatically(a)) { + if (knownMatch != -1) { + a.getWorld().showMessage(IMessage.ERROR, + WeaverMessages.format(WeaverMessages.MULTIPLE_MATCHES_IN_PRECEDENCE,a,patterns.get(knownMatch),p), + patterns.get(knownMatch).getSourceLocation(), p.getSourceLocation()); + return -1; + } else { + knownMatch = i; + } + } + } + if (knownMatch == -1) return starMatch; + else return knownMatch; + } + + + public int compare(ResolvedType aspect1, ResolvedType aspect2) { + int index1 = matchingIndex(aspect1); + int index2 = matchingIndex(aspect2); + + //System.out.println("a1: " + aspect1 + ", " + aspect2 + " = " + index1 + ", " + index2); + + if (index1 == -1 || index2 == -1) return 0; + + if (index1 == index2) return 0; + else if (index1 > index2) return -1; + else return +1; + } + + public boolean isAdviceLike() { + return false; + } + + public String getNameSuffix() { + return "precedence"; + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/DeclareSoft.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/DeclareSoft.java new file mode 100644 index 000000000..c8a3bfad7 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/DeclareSoft.java @@ -0,0 +1,137 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.util.Map; + +import org.aspectj.bridge.IMessage; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.TypeVariableReferenceType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; + +public class DeclareSoft extends Declare { + private TypePattern exception; + private Pointcut pointcut; + + public DeclareSoft(TypePattern exception, Pointcut pointcut) { + this.exception = exception; + this.pointcut = pointcut; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this,data); + } + + public Declare parameterizeWith(Map typeVariableBindingMap,World w) { + DeclareSoft ret = + new DeclareSoft( + exception.parameterizeWith(typeVariableBindingMap,w), + pointcut.parameterizeWith(typeVariableBindingMap,w)); + ret.copyLocationFrom(this); + return ret; + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("declare soft: "); + buf.append(exception); + buf.append(": "); + buf.append(pointcut); + buf.append(";"); + return buf.toString(); + } + + public boolean equals(Object other) { + if (!(other instanceof DeclareSoft)) return false; + DeclareSoft o = (DeclareSoft)other; + return + o.pointcut.equals(pointcut) && + o.exception.equals(exception); + } + + public int hashCode() { + int result = 19; + result = 37*result + pointcut.hashCode(); + result = 37*result + exception.hashCode(); + return result; + } + + + public void write(DataOutputStream s) throws IOException { + s.writeByte(Declare.SOFT); + exception.write(s); + pointcut.write(s); + writeLocation(s); + } + + public static Declare read(VersionedDataInputStream s, ISourceContext context) throws IOException { + Declare ret = new DeclareSoft( + TypePattern.read(s, context), + Pointcut.read(s, context) + ); + ret.readLocation(context, s); + return ret; + } + + public Pointcut getPointcut() { + return pointcut; + } + + public TypePattern getException() { + return exception; + } + + public void resolve(IScope scope) { + exception = exception.resolveBindings(scope, null, false, true); + ResolvedType excType = exception.getExactType().resolve(scope.getWorld()); + if (!excType.isMissing()) { + if (excType.isTypeVariableReference()) { + TypeVariableReferenceType typeVariableRT = (TypeVariableReferenceType) excType; + // a declare soft in a generic abstract aspect, we need to check the upper bound + excType = typeVariableRT.getUpperBound().resolve(scope.getWorld()); + } + if (!scope.getWorld().getCoreType(UnresolvedType.THROWABLE).isAssignableFrom(excType)) { + scope.getWorld().showMessage(IMessage.ERROR, + WeaverMessages.format(WeaverMessages.NOT_THROWABLE,excType.getName()), + exception.getSourceLocation(), null); + pointcut = Pointcut.makeMatchesNothing(Pointcut.RESOLVED); + return; + } + // ENH 42743 suggests that we don't soften runtime exceptions. + if (scope.getWorld().getCoreType(UnresolvedType.RUNTIME_EXCEPTION).isAssignableFrom(excType)) { + scope.getWorld().getLint().runtimeExceptionNotSoftened.signal( + new String[]{excType.getName()}, + exception.getSourceLocation(),null); + pointcut = Pointcut.makeMatchesNothing(Pointcut.RESOLVED); + return; + } + } + + pointcut = pointcut.resolve(scope); + } + + public boolean isAdviceLike() { + return true; + } + + public String getNameSuffix() { + return "soft"; + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ExactAnnotationFieldTypePattern.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ExactAnnotationFieldTypePattern.java new file mode 100644 index 000000000..0263035eb --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ExactAnnotationFieldTypePattern.java @@ -0,0 +1,206 @@ +/* ******************************************************************* + * 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.patterns; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Map; + +import org.aspectj.bridge.IMessage; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.AnnotatedElement; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ReferenceType; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; + +/** + * Represents an attempt to bind the field of an annotation within a pointcut. For example:
+ *
+ * before(Level lev): execution(* *(..)) && @annotation(TraceAnnotation(lev))
+ * 

+ * This binding annotation type pattern will be for 'lev'. + */ +public class ExactAnnotationFieldTypePattern extends ExactAnnotationTypePattern { + + UnresolvedType annotationType; + private ResolvedMember field; + + public ExactAnnotationFieldTypePattern(ExactAnnotationTypePattern p, String formalName) { + super(formalName); + this.annotationType = p.annotationType; + this.copyLocationFrom(p); + } + + public ExactAnnotationFieldTypePattern(UnresolvedType annotationType, String formalName) { + super(formalName); + this.annotationType = annotationType; + } + + /** + * resolve one of these funky things. Need to:
+ * (a) Check the formal is bound
+ * (b) Check the annotation type is valid + */ + public AnnotationTypePattern resolveBindings(IScope scope, Bindings bindings, boolean allowBinding) { + if (resolved) return this; + resolved = true; + FormalBinding formalBinding = scope.lookupFormal(formalName); + if (formalBinding == null) { + scope.message(IMessage.ERROR, this, "When using @annotation(()), must be bound"); + return this; + } + + annotationType = scope.getWorld().resolve(annotationType, true); + + // May not be directly found if in a package, so go looking if that is the case: + if (ResolvedType.isMissing(annotationType)) { + String cleanname = annotationType.getName(); + UnresolvedType type = null; + while (ResolvedType.isMissing(type = scope.lookupType(cleanname, this))) { + int lastDot = cleanname.lastIndexOf('.'); + if (lastDot == -1) break; + cleanname = cleanname.substring(0, lastDot) + "$" + cleanname.substring(lastDot + 1); + } + annotationType = scope.getWorld().resolve(type, true); + if (ResolvedType.isMissing(annotationType)) { + // there are likely to be other errors around that have led to us being unable to + // resolve the annotation type, let's quit now + return this; + } + } + + verifyIsAnnotationType((ResolvedType) annotationType, scope); + + if (!formalBinding.getType().resolve(scope.getWorld()).isEnum()) { + scope.message(IMessage.ERROR, this, "The field within the annotation must be an Enum. '" + formalBinding.getType() + + "' is not an Enum (compiler limitation)"); + } + bindingPattern = true; + + // Check that the formal is bound to a type that is represented by one field in the annotation type + ReferenceType theAnnotationType = (ReferenceType) annotationType; + ResolvedMember[] annotationFields = theAnnotationType.getDeclaredMethods(); + field = null; + for (int i = 0; i < annotationFields.length; i++) { + ResolvedMember resolvedMember = annotationFields[i]; + if (resolvedMember.getReturnType().equals(formalBinding.getType())) { + if (field != null) { + scope.message(IMessage.ERROR, this, "The field type '" + formalBinding.getType() + "' is ambiguous for annotation type '" + + theAnnotationType.getName() + "'"); + } + field = resolvedMember; + } + } + if (field == null) { + scope.message(IMessage.ERROR, this, "No field of type '" + formalBinding.getType() + "' exists on annotation type '" + + theAnnotationType.getName() + "'"); + } + + BindingAnnotationFieldTypePattern binding = new BindingAnnotationFieldTypePattern(formalBinding.getType(), formalBinding.getIndex(), + theAnnotationType); + binding.copyLocationFrom(this); + bindings.register(binding, scope); + binding.resolveBinding(scope.getWorld()); + return binding; + } + + public void write(DataOutputStream s) throws IOException { + s.writeByte(AnnotationTypePattern.EXACTFIELD); + s.writeUTF(formalName); + annotationType.write(s); + writeLocation(s); + } + + public static AnnotationTypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + ExactAnnotationFieldTypePattern ret; + String formalName = s.readUTF(); + UnresolvedType annotationType = UnresolvedType.read(s); + ret = new ExactAnnotationFieldTypePattern(annotationType, formalName); + ret.readLocation(context, s); + return ret; + } + + // --- + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public boolean equals(Object obj) { + if (!(obj instanceof ExactAnnotationFieldTypePattern)) return false; + ExactAnnotationFieldTypePattern other = (ExactAnnotationFieldTypePattern) obj; + return + (other.annotationType.equals(annotationType)) && + (other.field.equals(field)) && (other.formalName.equals(this.formalName)); + } + + public int hashCode() { + int hashcode = annotationType.hashCode(); + hashcode = hashcode * 37 + field.hashCode(); + hashcode = hashcode * 37 + formalName.hashCode(); + return hashcode; + } + + // TODO these are currently unimplemented as I believe it resolves to a Binding form *always* and so they don't get + // called + + public FuzzyBoolean fastMatches(AnnotatedElement annotated) { + throw new BCException("unimplemented"); + } + + public UnresolvedType getAnnotationType() { + throw new BCException("unimplemented"); + } + + public Map getAnnotationValues() { + throw new BCException("unimplemented"); + } + + public ResolvedType getResolvedAnnotationType() { + throw new BCException("unimplemented"); + } + + + public FuzzyBoolean matches(AnnotatedElement annotated, ResolvedType[] parameterAnnotations) { + throw new BCException("unimplemented"); + } + + public FuzzyBoolean matches(AnnotatedElement annotated) { + throw new BCException("unimplemented"); + } + + public FuzzyBoolean matchesRuntimeType(AnnotatedElement annotated) { + throw new BCException("unimplemented"); + } + + public AnnotationTypePattern parameterizeWith(Map typeVariableMap, World w) { + throw new BCException("unimplemented"); + } + + public void resolve(World world) { + throw new BCException("unimplemented"); + } + + public String toString() { + if (!resolved && formalName != null) return formalName; + StringBuffer ret = new StringBuffer(); + ret.append("@").append(annotationType.toString()); + ret.append("(").append(formalName).append(")"); + return ret.toString(); + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ExactAnnotationTypePattern.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ExactAnnotationTypePattern.java new file mode 100644 index 000000000..539dc6bb7 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ExactAnnotationTypePattern.java @@ -0,0 +1,469 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.AnnotatedElement; +import org.aspectj.weaver.AnnotationAJ; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ReferenceType; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.TypeVariableReference; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; +import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; + +/** + * Matches an annotation of a given type + */ +public class ExactAnnotationTypePattern extends AnnotationTypePattern { + + protected UnresolvedType annotationType; + protected String formalName; + protected boolean resolved = false; + protected boolean bindingPattern = false; + private Map annotationValues; + + // OPTIMIZE is annotationtype really unresolved???? surely it is resolved by + // now... + public ExactAnnotationTypePattern(UnresolvedType annotationType, + Map annotationValues) { + this.annotationType = annotationType; + this.annotationValues = annotationValues; + this.resolved = (annotationType instanceof ResolvedType); + } + + // Used when deserializing, values will be added + private ExactAnnotationTypePattern(UnresolvedType annotationType) { + this.annotationType = annotationType; + this.resolved = (annotationType instanceof ResolvedType); + } + + protected ExactAnnotationTypePattern(String formalName) { + this.formalName = formalName; + this.resolved = false; + this.bindingPattern = true; + // will be turned into BindingAnnotationTypePattern during resolution + } + + public ResolvedType getResolvedAnnotationType() { + if (!resolved) + throw new IllegalStateException("I need to be resolved first!"); + return (ResolvedType) annotationType; + } + + public UnresolvedType getAnnotationType() { + return annotationType; + } + + public Map getAnnotationValues() { + return annotationValues; + } + + public FuzzyBoolean fastMatches(AnnotatedElement annotated) { + if (annotated.hasAnnotation(annotationType) && annotationValues == null) { + return FuzzyBoolean.YES; + } else { + // could be inherited, but we don't know that until we are + // resolved, and we're not yet... + return FuzzyBoolean.MAYBE; + } + } + + public FuzzyBoolean matches(AnnotatedElement annotated) { + return matches(annotated, null); + } + + public FuzzyBoolean matches(AnnotatedElement annotated, + ResolvedType[] parameterAnnotations) { + if (!isForParameterAnnotationMatch()) { + boolean checkSupers = false; + if (getResolvedAnnotationType().hasAnnotation( + UnresolvedType.AT_INHERITED)) { + if (annotated instanceof ResolvedType) { + checkSupers = true; + } + } + + if (annotated.hasAnnotation(annotationType)) { + if (annotationType instanceof ReferenceType) { + ReferenceType rt = (ReferenceType) annotationType; + if (rt.getRetentionPolicy() != null + && rt.getRetentionPolicy().equals("SOURCE")) { + rt + .getWorld() + .getMessageHandler() + .handleMessage( + MessageUtil + .warn( + WeaverMessages + .format( + WeaverMessages.NO_MATCH_BECAUSE_SOURCE_RETENTION, + annotationType, + annotated), + getSourceLocation())); + return FuzzyBoolean.NO; + } + } + + // Are we also matching annotation values? + if (annotationValues != null) { + AnnotationAJ theAnnotation = annotated + .getAnnotationOfType(annotationType); + + // Check each one + Set keys = annotationValues.keySet(); + for (Iterator keyIter = keys.iterator(); keyIter.hasNext();) { + String k = (String) keyIter.next(); + String v = (String) annotationValues.get(k); + if (theAnnotation.hasNamedValue(k)) { + // Simple case, value is 'name=value' and the + // annotation specified the same thing + if (!theAnnotation.hasNameValuePair(k, v)) { + return FuzzyBoolean.NO; + } + } else { + // Complex case, look at the default value + ResolvedMember[] ms = ((ResolvedType) annotationType) + .getDeclaredMethods(); + boolean foundMatch = false; + for (int i = 0; i < ms.length && !foundMatch; i++) { + if (ms[i].isAbstract() + && ms[i].getParameterTypes().length == 0 + && ms[i].getName().equals(k)) { + // we might be onto something + String s = ms[i] + .getAnnotationDefaultValue(); + if (s != null && s.equals(v)) + foundMatch = true; + } + } + if (!foundMatch) + return FuzzyBoolean.NO; + } + } + } + return FuzzyBoolean.YES; + } else if (checkSupers) { + ResolvedType toMatchAgainst = ((ResolvedType) annotated) + .getSuperclass(); + while (toMatchAgainst != null) { + if (toMatchAgainst.hasAnnotation(annotationType)) { + // Are we also matching annotation values? + if (annotationValues != null) { + AnnotationAJ theAnnotation = toMatchAgainst + .getAnnotationOfType(annotationType); + + // Check each one + Set keys = annotationValues.keySet(); + for (Iterator keyIter = keys.iterator(); keyIter + .hasNext();) { + String k = (String) keyIter.next(); + String v = (String) annotationValues.get(k); + if (theAnnotation.hasNamedValue(k)) { + // Simple case, value is 'name=value' and + // the annotation specified the same thing + if (!theAnnotation.hasNameValuePair(k, v)) { + return FuzzyBoolean.NO; + } + } else { + // Complex case, look at the default value + ResolvedMember[] ms = ((ResolvedType) annotationType) + .getDeclaredMethods(); + boolean foundMatch = false; + for (int i = 0; i < ms.length + && !foundMatch; i++) { + if (ms[i].isAbstract() + && ms[i].getParameterTypes().length == 0 + && ms[i].getName().equals(k)) { + // we might be onto something + String s = ms[i] + .getAnnotationDefaultValue(); + if (s != null && s.equals(v)) + foundMatch = true; + } + } + if (!foundMatch) + return FuzzyBoolean.NO; + } + } + } + return FuzzyBoolean.YES; + } + toMatchAgainst = toMatchAgainst.getSuperclass(); + } + } + } else { + // check parameter annotations + if (parameterAnnotations == null) + return FuzzyBoolean.NO; + for (int i = 0; i < parameterAnnotations.length; i++) { + if (annotationType.equals(parameterAnnotations[i])) { + // Are we also matching annotation values? + if (annotationValues != null) { + parameterAnnotations[i] + .getWorld() + .getMessageHandler() + .handleMessage( + MessageUtil + .error("Compiler limitation: annotation value matching for parameter annotations not yet supported")); + return FuzzyBoolean.NO; + } + return FuzzyBoolean.YES; + } + } + } + + return FuzzyBoolean.NO; + } + + // this version should be called for @this, @target, @args + public FuzzyBoolean matchesRuntimeType(AnnotatedElement annotated) { + if (getResolvedAnnotationType().hasAnnotation( + UnresolvedType.AT_INHERITED)) { + // a static match is good enough + if (matches(annotated).alwaysTrue()) { + return FuzzyBoolean.YES; + } + } + // a subtype could match at runtime + return FuzzyBoolean.MAYBE; + } + + public void resolve(World world) { + if (!resolved) { + annotationType = annotationType.resolve(world); + } + resolved = true; + } + + /* + * (non-Javadoc) + * + * @see + * org.aspectj.weaver.patterns.AnnotationTypePattern#resolveBindings(org + * .aspectj.weaver.patterns.IScope, org.aspectj.weaver.patterns.Bindings, + * boolean) + */ + public AnnotationTypePattern resolveBindings(IScope scope, + Bindings bindings, boolean allowBinding) { + if (resolved) + return this; + resolved = true; + String simpleName = maybeGetSimpleName(); + if (simpleName != null) { + FormalBinding formalBinding = scope.lookupFormal(simpleName); + if (formalBinding != null) { + if (bindings == null) { + scope.message(IMessage.ERROR, this, + "negation doesn't allow binding"); + return this; + } + if (!allowBinding) { + scope + .message(IMessage.ERROR, this, + "name binding only allowed in @pcds, args, this, and target"); + return this; + } + formalName = simpleName; + bindingPattern = true; + verifyIsAnnotationType(formalBinding.getType().resolve( + scope.getWorld()), scope); + BindingAnnotationTypePattern binding = new BindingAnnotationTypePattern( + formalBinding); + binding.copyLocationFrom(this); + bindings.register(binding, scope); + binding.resolveBinding(scope.getWorld()); + if (isForParameterAnnotationMatch()) + binding.setForParameterAnnotationMatch(); + + return binding; + } + } + + // Non binding case + String cleanname = annotationType.getName(); + annotationType = scope.getWorld().resolve(annotationType, true); + + // We may not have found it if it is in a package, lets look it up... + if (ResolvedType.isMissing(annotationType)) { + UnresolvedType type = null; + while (ResolvedType.isMissing(type = scope.lookupType(cleanname, + this))) { + int lastDot = cleanname.lastIndexOf('.'); + if (lastDot == -1) + break; + cleanname = cleanname.substring(0, lastDot) + "$" + + cleanname.substring(lastDot + 1); + } + annotationType = scope.getWorld().resolve(type, true); + } + + verifyIsAnnotationType((ResolvedType) annotationType, scope); + return this; + } + + public AnnotationTypePattern parameterizeWith(Map typeVariableMap, World w) { + UnresolvedType newAnnotationType = annotationType; + if (annotationType.isTypeVariableReference()) { + TypeVariableReference t = (TypeVariableReference) annotationType; + String key = t.getTypeVariable().getName(); + if (typeVariableMap.containsKey(key)) { + newAnnotationType = (UnresolvedType) typeVariableMap.get(key); + } + } else if (annotationType.isParameterizedType()) { + newAnnotationType = annotationType.parameterize(typeVariableMap); + } + ExactAnnotationTypePattern ret = new ExactAnnotationTypePattern( + newAnnotationType, annotationValues); + ret.formalName = formalName; + ret.bindingPattern = bindingPattern; + ret.copyLocationFrom(this); + if (isForParameterAnnotationMatch()) + ret.setForParameterAnnotationMatch(); + return ret; + } + + protected String maybeGetSimpleName() { + if (formalName != null) + return formalName; + String ret = annotationType.getName(); + return (ret.indexOf('.') == -1) ? ret : null; + } + + protected void verifyIsAnnotationType(ResolvedType type, IScope scope) { + if (!type.isAnnotation()) { + IMessage m = MessageUtil.error(WeaverMessages.format( + WeaverMessages.REFERENCE_TO_NON_ANNOTATION_TYPE, type + .getName()), getSourceLocation()); + scope.getWorld().getMessageHandler().handleMessage(m); + resolved = false; + } + } + + private static byte VERSION = 1; // rev if serialisation form changes + + /* + * (non-Javadoc) + * + * @see + * org.aspectj.weaver.patterns.PatternNode#write(java.io.DataOutputStream) + */ + public void write(DataOutputStream s) throws IOException { + s.writeByte(AnnotationTypePattern.EXACT); + s.writeByte(VERSION); + s.writeBoolean(bindingPattern); + if (bindingPattern) { + s.writeUTF(formalName); + } else { + annotationType.write(s); + } + writeLocation(s); + s.writeBoolean(isForParameterAnnotationMatch()); + if (annotationValues == null) { + s.writeInt(0); + } else { + s.writeInt(annotationValues.size()); + Set key = annotationValues.keySet(); + for (Iterator keys = key.iterator(); keys.hasNext();) { + String k = (String) keys.next(); + s.writeUTF(k); + s.writeUTF((String) annotationValues.get(k)); + } + } + } + + public static AnnotationTypePattern read(VersionedDataInputStream s, + ISourceContext context) throws IOException { + ExactAnnotationTypePattern ret; + byte version = s.readByte(); + if (version > VERSION) { + throw new BCException( + "ExactAnnotationTypePattern was written by a newer version of AspectJ"); + } + boolean isBindingPattern = s.readBoolean(); + if (isBindingPattern) { + ret = new ExactAnnotationTypePattern(s.readUTF()); + } else { + ret = new ExactAnnotationTypePattern(UnresolvedType.read(s)); + } + ret.readLocation(context, s); + if (s.getMajorVersion() >= WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ160) { + if (s.readBoolean()) + ret.setForParameterAnnotationMatch(); + } + if (s.getMajorVersion() >= WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ160M2) { + int annotationValueCount = s.readInt(); + if (annotationValueCount > 0) { + Map aValues = new HashMap(); + for (int i = 0; i < annotationValueCount; i++) { + String key = s.readUTF(); + String val = s.readUTF(); + aValues.put(key, val); + } + ret.annotationValues = aValues; + } + } + return ret; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + if (!(obj instanceof ExactAnnotationTypePattern)) + return false; + ExactAnnotationTypePattern other = (ExactAnnotationTypePattern) obj; + return (other.annotationType.equals(annotationType)) + && isForParameterAnnotationMatch() == other + .isForParameterAnnotationMatch() + && (annotationValues == null ? other.annotationValues == null + : annotationValues.equals(other.annotationValues)); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return (((annotationType.hashCode()) * 37 + (isForParameterAnnotationMatch() ? 0 + : 1)) * 37) + + (annotationValues == null ? 0 : annotationValues.hashCode()); + } + + public String toString() { + if (!resolved && formalName != null) + return formalName; + String ret = "@" + annotationType.toString(); + if (formalName != null) + ret = ret + " " + formalName; + return ret; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ExactTypePattern.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ExactTypePattern.java new file mode 100644 index 000000000..859e8316f --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ExactTypePattern.java @@ -0,0 +1,268 @@ +/* ******************************************************************* + * 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 java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.AjAttribute; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.TypeVariableReference; +import org.aspectj.weaver.TypeVariableReferenceType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; + +public class ExactTypePattern extends TypePattern { + protected UnresolvedType type; + + public static final Map primitiveTypesMap; + public static final Map boxedPrimitivesMap; + private static final Map boxedTypesMap; + + static { + primitiveTypesMap = new HashMap(); + primitiveTypesMap.put("int",int.class); + primitiveTypesMap.put("short",short.class); + primitiveTypesMap.put("long",long.class); + primitiveTypesMap.put("byte",byte.class); + primitiveTypesMap.put("char",char.class); + primitiveTypesMap.put("float",float.class); + primitiveTypesMap.put("double",double.class); + + boxedPrimitivesMap = new HashMap(); + boxedPrimitivesMap.put("java.lang.Integer",Integer.class); + boxedPrimitivesMap.put("java.lang.Short",Short.class); + boxedPrimitivesMap.put("java.lang.Long",Long.class); + boxedPrimitivesMap.put("java.lang.Byte",Byte.class); + boxedPrimitivesMap.put("java.lang.Character",Character.class); + boxedPrimitivesMap.put("java.lang.Float",Float.class); + boxedPrimitivesMap.put("java.lang.Double",Double.class); + + + boxedTypesMap = new HashMap(); + boxedTypesMap.put("int",Integer.class); + boxedTypesMap.put("short",Short.class); + boxedTypesMap.put("long",Long.class); + boxedTypesMap.put("byte",Byte.class); + boxedTypesMap.put("char",Character.class); + boxedTypesMap.put("float",Float.class); + boxedTypesMap.put("double",Double.class); + + } + + public ExactTypePattern(UnresolvedType type, boolean includeSubtypes,boolean isVarArgs) { + super(includeSubtypes,isVarArgs); + this.type = type; + } + + public boolean isArray() { + return type.isArray(); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.TypePattern#couldEverMatchSameTypesAs(org.aspectj.weaver.patterns.TypePattern) + */ + protected boolean couldEverMatchSameTypesAs(TypePattern other) { + if (super.couldEverMatchSameTypesAs(other)) return true; + // false is necessary but not sufficient + UnresolvedType otherType = other.getExactType(); + if (!ResolvedType.isMissing(otherType)) { + return type.equals(otherType); + } + if (other instanceof WildTypePattern) { + WildTypePattern owtp = (WildTypePattern) other; + String yourSimpleNamePrefix = owtp.getNamePatterns()[0].maybeGetSimpleName(); + if (yourSimpleNamePrefix != null) { + return (type.getName().startsWith(yourSimpleNamePrefix)); + } + } + return true; + } + + protected boolean matchesExactly(ResolvedType matchType) { + boolean typeMatch = this.type.equals(matchType); + if (!typeMatch && (matchType.isParameterizedType() || matchType.isGenericType())) { + typeMatch = this.type.equals(matchType.getRawType()); + } + if (!typeMatch && matchType.isTypeVariableReference()) { + typeMatch = matchesTypeVariable((TypeVariableReferenceType)matchType); + } + annotationPattern.resolve(matchType.getWorld()); + boolean annMatch = false; + if (matchType.temporaryAnnotationTypes!=null) { + annMatch = annotationPattern.matches(matchType,matchType.temporaryAnnotationTypes).alwaysTrue(); + } else { + annMatch = annotationPattern.matches(matchType).alwaysTrue(); + } + return (typeMatch && annMatch); + } + + private boolean matchesTypeVariable(TypeVariableReferenceType matchType) { + // was this method previously coded to return false *on purpose* ?? pr124808 + return this.type.equals(((TypeVariableReference)matchType).getTypeVariable().getFirstBound()); + //return false; + } + + protected boolean matchesExactly(ResolvedType matchType, ResolvedType annotatedType) { + boolean typeMatch = this.type.equals(matchType); + if (!typeMatch && (matchType.isParameterizedType() || matchType.isGenericType())) { + typeMatch = this.type.equals(matchType.getRawType()); + } + if (!typeMatch && matchType.isTypeVariableReference()) { + typeMatch = matchesTypeVariable((TypeVariableReferenceType)matchType); + } + annotationPattern.resolve(matchType.getWorld()); + boolean annMatch = false; + if (annotatedType.temporaryAnnotationTypes!=null) { + annMatch = annotationPattern.matches(annotatedType,annotatedType.temporaryAnnotationTypes).alwaysTrue(); + } else { + annMatch = annotationPattern.matches(annotatedType).alwaysTrue(); + } + return (typeMatch && annMatch); + } + + public UnresolvedType getType() { return type; } + + // true if (matchType instanceof this.type) + public FuzzyBoolean matchesInstanceof(ResolvedType matchType) { + // in our world, Object is assignable from anything + annotationPattern.resolve(matchType.getWorld()); + if (type.equals(ResolvedType.OBJECT)) + return FuzzyBoolean.YES.and(annotationPattern.matches(matchType)); + + if (type.resolve(matchType.getWorld()).isAssignableFrom(matchType)) { + return FuzzyBoolean.YES.and(annotationPattern.matches(matchType)); + } + + // fix for PR 64262 - shouldn't try to coerce primitives + if (type.isPrimitiveType()) { + return FuzzyBoolean.NO; + } else { + return matchType.isCoerceableFrom(type.resolve(matchType.getWorld())) ? FuzzyBoolean.MAYBE : FuzzyBoolean.NO; + } + } + + public boolean equals(Object other) { + if (!(other instanceof ExactTypePattern)) return false; + if (other instanceof BindingTypePattern) return false; + ExactTypePattern o = (ExactTypePattern)other; + if (includeSubtypes != o.includeSubtypes) return false; + if (isVarArgs != o.isVarArgs) return false; + if (!typeParameters.equals(o.typeParameters)) return false; + return (o.type.equals(this.type) && o.annotationPattern.equals(this.annotationPattern)); + } + + public int hashCode() { + int result = 17; + result = 37*result + type.hashCode(); + result = 37*result + new Boolean(includeSubtypes).hashCode(); + result = 37*result + new Boolean(isVarArgs).hashCode(); + result = 37*result + typeParameters.hashCode(); + result = 37*result + annotationPattern.hashCode(); + return result; + } + + private static final byte EXACT_VERSION = 1; // rev if changed + public void write(DataOutputStream out) throws IOException { + out.writeByte(TypePattern.EXACT); + out.writeByte(EXACT_VERSION); + type.write(out); + out.writeBoolean(includeSubtypes); + out.writeBoolean(isVarArgs); + annotationPattern.write(out); + typeParameters.write(out); + writeLocation(out); + } + + public static TypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + if (s.getMajorVersion()>=AjAttribute.WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ150) { + return readTypePattern150(s,context); + } else { + return readTypePatternOldStyle(s,context); + } + } + + public static TypePattern readTypePattern150(VersionedDataInputStream s, ISourceContext context) throws IOException { + byte version = s.readByte(); + if (version > EXACT_VERSION) throw new BCException("ExactTypePattern was written by a more recent version of AspectJ"); + TypePattern ret = new ExactTypePattern(UnresolvedType.read(s), s.readBoolean(), s.readBoolean()); + ret.setAnnotationTypePattern(AnnotationTypePattern.read(s,context)); + ret.setTypeParameters(TypePatternList.read(s,context)); + ret.readLocation(context, s); + return ret; + } + + public static TypePattern readTypePatternOldStyle(DataInputStream s, ISourceContext context) throws IOException { + TypePattern ret = new ExactTypePattern(UnresolvedType.read(s), s.readBoolean(),false); + ret.readLocation(context, s); + return ret; + } + + public String toString() { + StringBuffer buff = new StringBuffer(); + if (annotationPattern != AnnotationTypePattern.ANY) { + buff.append('('); + buff.append(annotationPattern.toString()); + buff.append(' '); + } + String typeString = type.toString(); + if (isVarArgs) typeString = typeString.substring(0,typeString.lastIndexOf('[')); + buff.append(typeString); + if (includeSubtypes) buff.append('+'); + if (isVarArgs) buff.append("..."); + if (annotationPattern != AnnotationTypePattern.ANY) { + buff.append(')'); + } + return buff.toString(); + } + public TypePattern resolveBindings(IScope scope, Bindings bindings, + boolean allowBinding, boolean requireExactType) + { + throw new BCException("trying to re-resolve"); + + } + + /** + * return a version of this type pattern with all type variables references replaced + * by the corresponding entry in the map. + */ + public TypePattern parameterizeWith(Map typeVariableMap,World w) { + UnresolvedType newType = type; + if (type.isTypeVariableReference()) { + TypeVariableReference t = (TypeVariableReference) type; + String key = t.getTypeVariable().getName(); + if (typeVariableMap.containsKey(key)) { + newType = (UnresolvedType) typeVariableMap.get(key); + } + } else if (type.isParameterizedType()) { + newType = w.resolve(type).parameterize(typeVariableMap); + } + ExactTypePattern ret = new ExactTypePattern(newType,includeSubtypes,isVarArgs); + ret.annotationPattern = annotationPattern.parameterizeWith(typeVariableMap,w); + ret.copyLocationFrom(this); + return ret; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ExposedState.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ExposedState.java new file mode 100644 index 000000000..fb34da91c --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ExposedState.java @@ -0,0 +1,98 @@ +/* ******************************************************************* + * 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 java.util.Arrays; + +import org.aspectj.weaver.Member; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.ast.Expr; +import org.aspectj.weaver.ast.Var; + +public class ExposedState { + public Var[] vars; + private boolean[] erroneousVars; + private Expr aspectInstance; + private UnresolvedType[] expectedVarTypes; // enables us to check that binding is occurring with the *right* types + + public ExposedState(int size) { + super(); + vars = new Var[size]; + erroneousVars = new boolean[size]; + } + + public ExposedState(Member signature) { + // XXX there maybe something about target for non-static sigs + this(signature.getParameterTypes().length); + expectedVarTypes = new UnresolvedType[signature.getParameterTypes().length]; + if (expectedVarTypes.length>0) { + for (int i = 0; i < signature.getParameterTypes().length; i++) { + expectedVarTypes[i] = signature.getParameterTypes()[i]; + } + } + + } + + public boolean isFullySetUp() { + for (int i = 0; i < vars.length; i++) { + if (vars[i]==null) return false; + } + return true; + } + + public void set(int i, Var var) { + // check the type is OK if we can... these are the same rules as in matchesInstanceOf() processing + if (expectedVarTypes!=null) { + ResolvedType expected = expectedVarTypes[i].resolve(var.getType().getWorld()); + if (!expected.equals(ResolvedType.OBJECT)) { + if (!expected.isAssignableFrom(var.getType())) { + if (!var.getType().isCoerceableFrom(expected)) { +// throw new BCException("Expected type "+expectedVarTypes[i]+" in slot "+i+" but attempt to put "+var.getType()+" into it"); + return; + } + } + } + } + vars[i] = var; + } + public Var get(int i) { + return vars[i]; + } + public int size() { + return vars.length; + } + + public Expr getAspectInstance() { + return aspectInstance; + } + + public void setAspectInstance(Expr aspectInstance) { + this.aspectInstance = aspectInstance; + } + + public String toString() { + return "ExposedState(#Vars="+vars.length+",Vars=" + Arrays.asList(vars) + ",AspectInstance=" + aspectInstance + ")"; + } + + // Set to true if we have reported an error message against it, + // prevents us blowing up in later code gen. + public void setErroneousVar(int formalIndex) { + erroneousVars[formalIndex]=true; + } + + public boolean isErroneousVar(int formalIndex) { + return erroneousVars[formalIndex]; + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/FastMatchInfo.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/FastMatchInfo.java new file mode 100644 index 000000000..132b86501 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/FastMatchInfo.java @@ -0,0 +1,47 @@ +/* ******************************************************************* + * Copyright (c) 2004 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: + * Jim Hugunin initial implementation + * ******************************************************************/ + + +package org.aspectj.weaver.patterns; + +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.Shadow.Kind; + + +public class FastMatchInfo { + private Kind kind; + private ResolvedType type; + + public FastMatchInfo(ResolvedType type, Shadow.Kind kind) { + this.type = type; + this.kind = kind; + } + + /** + * kind can be null to indicate that all kinds should be considered. + * This is usually done as a first pass + * @return + */ + public Kind getKind() { + return kind; + } + + public ResolvedType getType() { + return type; + } + + public String toString() { + return "FastMatchInfo [type="+type.getName()+"] ["+(kind==null?"AllKinds":"Kind="+kind)+"]"; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/FormalBinding.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/FormalBinding.java new file mode 100644 index 000000000..73693f07c --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/FormalBinding.java @@ -0,0 +1,82 @@ +/* ******************************************************************* + * 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.IHasPosition; +import org.aspectj.weaver.UnresolvedType; + +public class FormalBinding implements IHasPosition { + private final UnresolvedType type; + private final String name; + private final int index; + private final int start, end; + + public FormalBinding(UnresolvedType type, String name, int index, int start, int end) { + this.type = type; + this.name = name; + this.index = index; + this.start = start; + this.end = end; + } + + public FormalBinding(UnresolvedType type, int index) { + this(type, "unknown", index, 0, 0); + } + + public FormalBinding(UnresolvedType type, String name, int index) { + this(type, name, index, 0, 0); + } + + // ---- + + public String toString() { + return type.toString() + ":" + index; + } + + public int getEnd() { + return end; + } + + public int getStart() { + return start; + } + + public int getIndex() { + return index; + } + + public String getName() { + return name; + } + + public UnresolvedType getType() { + return type; + } + + // ---- + + public static final FormalBinding[] NONE = new FormalBinding[0]; + + /** + * A marker class for bindings for which we want to ignore unbound issue and consider them as implicit binding - f.e. to handle + * JoinPoint in @AJ advices + * + * @author
Alexandre Vasseur + */ + public static class ImplicitFormalBinding extends FormalBinding { + public ImplicitFormalBinding(UnresolvedType type, String name, int index) { + super(type, name, index); + } + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/HandlerPointcut.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/HandlerPointcut.java new file mode 100644 index 000000000..b26dc9bb7 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/HandlerPointcut.java @@ -0,0 +1,139 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.util.Map; + +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Test; + +/** + * This is a kind of KindedPointcut. This belongs either in + * a hierarchy with it or in a new place to share code + * with other potential future statement-level pointcuts like + * synchronized and throws + */ +public class HandlerPointcut extends Pointcut { + TypePattern exceptionType; + + private static final int MATCH_KINDS = Shadow.ExceptionHandler.bit; + + public HandlerPointcut(TypePattern exceptionType) { + this.exceptionType = exceptionType; + this.pointcutKind = HANDLER; + } + + public int couldMatchKinds() { + return MATCH_KINDS; + } + + public FuzzyBoolean fastMatch(FastMatchInfo type) { + //??? should be able to do better by finding all referenced types in type + return FuzzyBoolean.MAYBE; + } + + protected FuzzyBoolean matchInternal(Shadow shadow) { + if (shadow.getKind() != Shadow.ExceptionHandler) return FuzzyBoolean.NO; + + exceptionType.resolve(shadow.getIWorld()); + + // we know we have exactly one parameter since we're checking an exception handler + return exceptionType.matches( + shadow.getSignature().getParameterTypes()[0].resolve(shadow.getIWorld()), + TypePattern.STATIC); + } + + public Pointcut parameterizeWith(Map typeVariableMap,World w) { + HandlerPointcut ret = new HandlerPointcut(exceptionType.parameterizeWith(typeVariableMap,w)); + ret.copyLocationFrom(this); + return ret; + } + + public boolean equals(Object other) { + if (!(other instanceof HandlerPointcut)) return false; + HandlerPointcut o = (HandlerPointcut)other; + return o.exceptionType.equals(this.exceptionType); } + + public int hashCode() { + int result = 17; + result = 37*result + exceptionType.hashCode(); + return result; + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("handler("); + buf.append(exceptionType.toString()); + buf.append(")"); + return buf.toString(); + } + + + public void write(DataOutputStream s) throws IOException { + s.writeByte(Pointcut.HANDLER); + exceptionType.write(s); + writeLocation(s); + } + + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + HandlerPointcut ret = new HandlerPointcut(TypePattern.read(s, context)); + ret.readLocation(context, s); + return ret; + } + + // XXX note: there is no namebinding in any kinded pointcut. + // still might want to do something for better error messages + // We want to do something here to make sure we don't sidestep the parameter + // list in capturing type identifiers. + public void resolveBindings(IScope scope, Bindings bindings) { + exceptionType = exceptionType.resolveBindings(scope, bindings, false, false); + boolean invalidParameterization = false; + if (exceptionType.getTypeParameters().size() > 0) invalidParameterization = true ; + UnresolvedType exactType = exceptionType.getExactType(); + if (exactType != null && exactType.isParameterizedType()) invalidParameterization = true; + if (invalidParameterization) { + // no parameterized or generic types for handler + scope.message( + MessageUtil.error(WeaverMessages.format(WeaverMessages.HANDLER_PCD_DOESNT_SUPPORT_PARAMETERS), + getSourceLocation())); + } + //XXX add error if exact binding and not an exception + } + + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + return match(shadow).alwaysTrue() ? Literal.TRUE : Literal.FALSE; + } + + public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + Pointcut ret = new HandlerPointcut(exceptionType); + ret.copyLocationFrom(this); + return ret; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/HasMemberTypePattern.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/HasMemberTypePattern.java new file mode 100644 index 000000000..263b7888f --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/HasMemberTypePattern.java @@ -0,0 +1,160 @@ +/* ******************************************************************* + * 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.patterns; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.lang.reflect.Modifier; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.aspectj.bridge.IMessage; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.ConcreteTypeMunger; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; + +/** + * @author colyer + * Matches types that have a certain method / constructor / field + * Currently only allowed within declare parents and declare @type + */ +public class HasMemberTypePattern extends TypePattern { + + private SignaturePattern signaturePattern; + + public HasMemberTypePattern(SignaturePattern aSignaturePattern) { + super(false,false); + this.signaturePattern = aSignaturePattern; + } + + protected boolean matchesExactly(ResolvedType type) { + if (signaturePattern.getKind() == Member.FIELD) { + return hasField(type); + } else { + return hasMethod(type); + } + } + + private final static String declareAtPrefix = "ajc$declare_at"; + + private boolean hasField(ResolvedType type) { + // TODO what about ITDs + World world = type.getWorld(); + for (Iterator iter = type.getFields(); iter.hasNext();) { + Member field = (Member) iter.next(); + if (field.getName().startsWith(declareAtPrefix)) continue; + if (signaturePattern.matches(field, type.getWorld(), false)) { + if (field.getDeclaringType().resolve(world) != type) { + if (Modifier.isPrivate(field.getModifiers())) continue; + } + return true; + } + } + return false; + } + + private boolean hasMethod(ResolvedType type) { + // TODO what about ITDs + World world = type.getWorld(); + for (Iterator iter = type.getMethods(); iter.hasNext();) { + Member method = (Member) iter.next(); + if (method.getName().startsWith(declareAtPrefix)) continue; + if (signaturePattern.matches(method, type.getWorld(), false)) { + if (method.getDeclaringType().resolve(world) != type) { + if (Modifier.isPrivate(method.getModifiers())) continue; + } + return true; + } + } + // try itds before we give up + List mungers = type.getInterTypeMungersIncludingSupers(); + for (Iterator iter = mungers.iterator(); iter.hasNext();) { + ConcreteTypeMunger munger = (ConcreteTypeMunger) iter.next(); + Member member = munger.getSignature(); + if (signaturePattern.matches(member, type.getWorld(), false)) { + if (!Modifier.isPublic(member.getModifiers())) continue; + return true; + } + } + return false; + } + + protected boolean matchesExactly(ResolvedType type, ResolvedType annotatedType) { + return matchesExactly(type); + } + + public FuzzyBoolean matchesInstanceof(ResolvedType type) { + throw new UnsupportedOperationException("hasmethod/field do not support instanceof matching"); + } + + public TypePattern parameterizeWith(Map typeVariableMap,World w) { + HasMemberTypePattern ret = new HasMemberTypePattern(signaturePattern.parameterizeWith(typeVariableMap,w)); + ret.copyLocationFrom(this); + return ret; + } + + public TypePattern resolveBindings(IScope scope, Bindings bindings, boolean allowBinding, boolean requireExactType) { + // check that hasmember type patterns are allowed! + if (!scope.getWorld().isHasMemberSupportEnabled()) { + String msg = WeaverMessages.format(WeaverMessages.HAS_MEMBER_NOT_ENABLED,this.toString()); + scope.message(IMessage.ERROR, this, msg); + } + signaturePattern.resolveBindings(scope,bindings); + return this; + } + + public boolean equals(Object obj) { + if (!(obj instanceof HasMemberTypePattern)) return false; + if (this == obj) return true; + return signaturePattern.equals(((HasMemberTypePattern)obj).signaturePattern); + } + + public int hashCode() { + return signaturePattern.hashCode(); + } + + public String toString() { + StringBuffer buff = new StringBuffer(); + if (signaturePattern.getKind() == Member.FIELD) { + buff.append("hasfield("); + } else { + buff.append("hasmethod("); + } + buff.append(signaturePattern.toString()); + buff.append(")"); + return buff.toString(); + } + + public void write(DataOutputStream s) throws IOException { + s.writeByte(TypePattern.HAS_MEMBER); + signaturePattern.write(s); + writeLocation(s); + } + + public static TypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + SignaturePattern sp = SignaturePattern.read(s, context); + HasMemberTypePattern ret = new HasMemberTypePattern(sp); + ret.readLocation(context,s); + return ret; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/HasMemberTypePatternFinder.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/HasMemberTypePatternFinder.java new file mode 100644 index 000000000..5fb59503f --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/HasMemberTypePatternFinder.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 org.aspectj.weaver.patterns; + +/** + * @author colyer + * usage : new HasMemberTypePatternFinder(pattern).hasMemberTypePattern() + */ +public class HasMemberTypePatternFinder extends AbstractPatternNodeVisitor { + + private boolean hasMemberTypePattern = false; + + public HasMemberTypePatternFinder(TypePattern aPattern) { + aPattern.traverse(this, null); + } + + public Object visit(HasMemberTypePattern node, Object data) { + hasMemberTypePattern = true; + return null; + } + + public boolean hasMemberTypePattern() { return hasMemberTypePattern; } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor.java new file mode 100644 index 000000000..0b2b9b5fc --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor.java @@ -0,0 +1,50 @@ +/* ******************************************************************* + * 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.patterns; + +import org.aspectj.weaver.UnresolvedType; + +/** + * @author colyer + * + */ +public class HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor + extends AbstractPatternNodeVisitor { + + boolean ohYesItHas = false; + + /** + * Is the Exact type parameterized? + * Generic is ok as that just means we resolved a simple type pattern to a generic type + */ + public Object visit(ExactTypePattern node, Object data) { + UnresolvedType theExactType = node.getExactType(); + if (theExactType.isParameterizedType()) ohYesItHas = true; + //if (theExactType.isGenericType()) ohYesItHas = true; + return data; + } + + /** + * Any type bounds are bad. + * Type parameters are right out. + */ + public Object visit(WildTypePattern node, Object data) { + if (node.getUpperBound() != null) ohYesItHas = true; + if (node.getLowerBound() != null) ohYesItHas = true; + if (node.getTypeParameters().size() != 0) ohYesItHas = true; + return data; + } + + public boolean wellHasItThen/*?*/() { + return ohYesItHas; + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/IScope.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/IScope.java new file mode 100644 index 000000000..e96cb25a6 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/IScope.java @@ -0,0 +1,51 @@ +/* ******************************************************************* + * 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.bridge.IMessage; +import org.aspectj.bridge.IMessageHandler; +import org.aspectj.weaver.IHasPosition; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.World; + +public interface IScope { + + /** returns the type corresponding to the name in this scope + * returns ResolvedType.MISSING if no such type exists and reports a problem + */ + UnresolvedType lookupType(String name, IHasPosition location); + + World getWorld(); + + ResolvedType getEnclosingType(); + + // these next three are used to create {@link BindingTypePattern} objects. + IMessageHandler getMessageHandler(); + /** returns the formal associated with the name, or null if no such formal exists */ + FormalBinding lookupFormal(String name); + /** returns the formal with the index. Throws ArrayOutOfBounds exception if out of bounds */ + FormalBinding getFormal(int i); + + int getFormalCount(); + + String[] getImportedPrefixes(); + String[] getImportedNames(); + + void message(IMessage.Kind kind, IHasPosition location, String message); + void message(IMessage.Kind kind, IHasPosition location1, IHasPosition location2, String message); + void message(IMessage aMessage); + + //ISourceLocation makeSourceLocation(ILocation location); +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/IToken.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/IToken.java new file mode 100644 index 000000000..aa925fd69 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/IToken.java @@ -0,0 +1,53 @@ +/* ******************************************************************* + * 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.IHasPosition; + +public interface IToken extends IHasPosition { + public static final IToken EOF = BasicToken.makeOperator("", 0, 0); + + /** + * Returns the string value of this token. + * + * If isIdentifier is false, then this string must be intern'd + * so that == matching can be used. + * + * If isIdentifier is true, interning is not required. + */ + public String getString(); + + /** + * Whether this should be treated as a token or a generic identifier + */ + public boolean isIdentifier(); + + /** + * Whether this should be treated as a literal value + * + * Kinds == "string", ??? + * + * returns null if this isn't a literal + */ + public String getLiteralKind(); + + + /** + * If this token represents a pre-parsed Pointcut, then return it; + * otherwise returns null. + * + * Needed for the implementation of 'if' + */ + public Pointcut maybeGetParsedPointcut(); +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ITokenSource.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ITokenSource.java new file mode 100644 index 000000000..ea777a47f --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ITokenSource.java @@ -0,0 +1,26 @@ +/* ******************************************************************* + * 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.ISourceContext; + + +public interface ITokenSource { + public IToken next(); + public IToken peek(); + public IToken peek(int offset); + public int getIndex(); + public void setIndex(int newIndex); + public ISourceContext getSourceContext(); +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/IVerificationRequired.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/IVerificationRequired.java new file mode 100644 index 000000000..f1ce19eff --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/IVerificationRequired.java @@ -0,0 +1,22 @@ +/* ******************************************************************* + * 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 IBM initial implementation + * ******************************************************************/ +package org.aspectj.weaver.patterns; + + +/** + * Implementors provide a 'verify()' method that is invoked at the end of type + * binding completion. + * @see WildTypePattern.VerifyBoundsForTypePattern + */ +public interface IVerificationRequired { + public void verify(); +} \ No newline at end of file diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/IfPointcut.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/IfPointcut.java new file mode 100644 index 000000000..eaa2e1f61 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/IfPointcut.java @@ -0,0 +1,506 @@ +/* ******************************************************************* + * 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 if() implementation for @AJ style + * ******************************************************************/ +package org.aspectj.weaver.patterns; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.aspectj.bridge.IMessage; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.Advice; +import org.aspectj.weaver.AjcMemberMaker; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedMemberImpl; +import org.aspectj.weaver.ResolvedPointcutDefinition; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.ShadowMunger; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Expr; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Test; +import org.aspectj.weaver.ast.Var; + +public class IfPointcut extends Pointcut { + public ResolvedMember testMethod; + public int extraParameterFlags; + + /** + * A token source dump that looks like a pointcut (but is NOT parseable at all - just here to help debugging etc) + */ + private final String enclosingPointcutHint; + + public Pointcut residueSource; + int baseArgsCount; + + // XXX some way to compute args + + public IfPointcut(ResolvedMember testMethod, int extraParameterFlags) { + this.testMethod = testMethod; + this.extraParameterFlags = extraParameterFlags; + this.pointcutKind = IF; + this.enclosingPointcutHint = null; + } + + /** + * No-arg constructor for @AJ style, where the if() body is actually the @Pointcut annotated method + */ + public IfPointcut(String enclosingPointcutHint) { + this.pointcutKind = IF; + this.enclosingPointcutHint = enclosingPointcutHint; + this.testMethod = null;// resolved during concretize + this.extraParameterFlags = -1;// allows to keep track of the @Aj style + } + + public int couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS_BITS; + } + + public FuzzyBoolean fastMatch(FastMatchInfo type) { + return FuzzyBoolean.MAYBE; + } + + protected FuzzyBoolean matchInternal(Shadow shadow) { + // ??? this is not maximally efficient + return FuzzyBoolean.MAYBE; + } + + public boolean alwaysFalse() { + return false; + } + + public boolean alwaysTrue() { + return false; + } + + // enh 76055 + public Pointcut getResidueSource() { + return residueSource; + } + + public void write(DataOutputStream s) throws IOException { + s.writeByte(Pointcut.IF); + s.writeBoolean(testMethod != null); // do we have a test method? + if (testMethod != null) + testMethod.write(s); + s.writeByte(extraParameterFlags); + writeLocation(s); + } + + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + boolean hasTestMethod = s.readBoolean(); + ResolvedMember resolvedTestMethod = null; + if (hasTestMethod) { // should always have a test method unless @AJ style + resolvedTestMethod = ResolvedMemberImpl.readResolvedMember(s, context); + } + IfPointcut ret = new IfPointcut(resolvedTestMethod, s.readByte()); + ret.readLocation(context, s); + return ret; + } + + public void resolveBindings(IScope scope, Bindings bindings) { + // ??? all we need is good error messages in here in cflow contexts + } + + public boolean equals(Object other) { + if (!(other instanceof IfPointcut)) + return false; + IfPointcut o = (IfPointcut) other; + if (o.testMethod == null) + return (this.testMethod == null); + return o.testMethod.equals(this.testMethod); + } + + public int hashCode() { + int result = 17; + result = 37 * result + testMethod.hashCode(); + return result; + } + + public String toString() { + if (extraParameterFlags < 0) { + // @AJ style + return "if()"; + } else { + return "if(" + testMethod + ")";// FIXME AV - bad, this makes it unparsable. Perhaps we can use if() for code style + // behind the scene! + } + } + + // ??? The implementation of name binding and type checking in if PCDs is very convoluted + // There has to be a better way... + private boolean findingResidue = false; + + // Similar to lastMatchedShadowId - but only for if PCDs. + private int ifLastMatchedShadowId; + private Test ifLastMatchedShadowResidue; + + /** + * At each shadow that matched, the residue can be different. + */ + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + if (findingResidue) + return Literal.TRUE; + findingResidue = true; + try { + + // Have we already been asked this question? + if (shadow.shadowId == ifLastMatchedShadowId) + return ifLastMatchedShadowResidue; + + Test ret = Literal.TRUE; + List args = new ArrayList(); + + // code style + if (extraParameterFlags >= 0) { + // If there are no args to sort out, don't bother with the recursive call + if (baseArgsCount > 0) { + ExposedState myState = new ExposedState(baseArgsCount); + // ??? we throw out the test that comes from this walk. All we want here + // is bindings for the arguments + residueSource.findResidue(shadow, myState); + + UnresolvedType[] pTypes = (testMethod == null ? null : testMethod.getParameterTypes()); + if (pTypes != null && baseArgsCount > pTypes.length) { // pr155347 + throw new BCException("Unexpected problem with testMethod " + testMethod + ": expecting " + baseArgsCount + + " arguments"); + } + // pr118149 + // It is possible for vars in myState (which would normally be set + // in the call to residueSource.findResidue) to not be set (be null) + // in an Or pointcut with if expressions in both branches, and where + // one branch is known statically to not match. In this situation we + // simply return Test. + for (int i = 0; i < baseArgsCount; i++) { + Var v = myState.get(i); + if (v == null) + continue; // pr118149 + args.add(v); + ret = Test.makeAnd(ret, Test.makeInstanceof(v, pTypes[i].resolve(shadow.getIWorld()))); + } + } + + // handle thisJoinPoint parameters + if ((extraParameterFlags & Advice.ThisJoinPoint) != 0) { + args.add(shadow.getThisJoinPointVar()); + } + + if ((extraParameterFlags & Advice.ThisJoinPointStaticPart) != 0) { + args.add(shadow.getThisJoinPointStaticPartVar()); + } + + if ((extraParameterFlags & Advice.ThisEnclosingJoinPointStaticPart) != 0) { + args.add(shadow.getThisEnclosingJoinPointStaticPartVar()); + } + } else { + // @style is slightly different + int currentStateIndex = 0; + // FIXME AV - "args(jp)" test(jp, thejp) will fail here + for (int i = 0; i < testMethod.getParameterTypes().length; i++) { + String argSignature = testMethod.getParameterTypes()[i].getSignature(); + if (AjcMemberMaker.TYPEX_JOINPOINT.getSignature().equals(argSignature)) { + args.add(shadow.getThisJoinPointVar()); + } else if (AjcMemberMaker.TYPEX_PROCEEDINGJOINPOINT.getSignature().equals(argSignature)) { + args.add(shadow.getThisJoinPointVar()); + } else if (AjcMemberMaker.TYPEX_STATICJOINPOINT.getSignature().equals(argSignature)) { + args.add(shadow.getThisJoinPointStaticPartVar()); + } else if (AjcMemberMaker.TYPEX_ENCLOSINGSTATICJOINPOINT.getSignature().equals(argSignature)) { + args.add(shadow.getThisEnclosingJoinPointStaticPartVar()); + } else { + // we don't use i as JoinPoint.* can be anywhere in the signature in @style + Var v = state.get(currentStateIndex++); + args.add(v); + ret = Test.makeAnd(ret, Test.makeInstanceof(v, testMethod.getParameterTypes()[i] + .resolve(shadow.getIWorld()))); + } + } + } + + ret = Test.makeAnd(ret, Test.makeCall(testMethod, (Expr[]) args.toArray(new Expr[args.size()]))); + + // Remember... + ifLastMatchedShadowId = shadow.shadowId; + ifLastMatchedShadowResidue = ret; + return ret; + } finally { + findingResidue = false; + } + } + + // amc - the only reason this override seems to be here is to stop the copy, but + // that can be prevented by overriding shouldCopyLocationForConcretization, + // allowing me to make the method final in Pointcut. + // public Pointcut concretize(ResolvedType inAspect, IntMap bindings) { + // return this.concretize1(inAspect, bindings); + // } + + protected boolean shouldCopyLocationForConcretize() { + return false; + } + + private IfPointcut partiallyConcretized = null; + + public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + // System.err.println("concretize: " + this + " already: " + partiallyConcretized); + + if (isDeclare(bindings.getEnclosingAdvice())) { + // Enforce rule about which designators are supported in declare + inAspect.getWorld().showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.IF_IN_DECLARE), + bindings.getEnclosingAdvice().getSourceLocation(), null); + return Pointcut.makeMatchesNothing(Pointcut.CONCRETE); + } + + if (partiallyConcretized != null) { + return partiallyConcretized; + } + + final IfPointcut ret; + if (extraParameterFlags < 0 && testMethod == null) { + // @AJ style, we need to find the testMethod in the aspect defining the "if()" enclosing pointcut + ResolvedPointcutDefinition def = bindings.peekEnclosingDefinition(); + if (def != null) { + ResolvedType aspect = inAspect.getWorld().resolve(def.getDeclaringType()); + for (Iterator memberIter = aspect.getMethods(); memberIter.hasNext();) { + ResolvedMember method = (ResolvedMember) memberIter.next(); + if (def.getName().equals(method.getName()) + && def.getParameterTypes().length == method.getParameterTypes().length) { + boolean sameSig = true; + for (int j = 0; j < method.getParameterTypes().length; j++) { + UnresolvedType argJ = method.getParameterTypes()[j]; + if (!argJ.equals(def.getParameterTypes()[j])) { + sameSig = false; + break; + } + } + if (sameSig) { + testMethod = method; + break; + } + } + } + if (testMethod == null) { + inAspect.getWorld().showMessage(IMessage.ERROR, + "Cannot find if() body from '" + def.toString() + "' for '" + enclosingPointcutHint + "'", + this.getSourceLocation(), null); + return Pointcut.makeMatchesNothing(Pointcut.CONCRETE); + } + } else { + testMethod = inAspect.getWorld().resolve(bindings.getAdviceSignature()); + } + ret = new IfPointcut(enclosingPointcutHint); + ret.testMethod = testMethod; + } else { + ret = new IfPointcut(testMethod, extraParameterFlags); + } + ret.copyLocationFrom(this); + partiallyConcretized = ret; + + // It is possible to directly code your pointcut expression in a per clause + // rather than defining a pointcut declaration and referencing it in your + // per clause. If you do this, we have problems (bug #62458). For now, + // let's police that you are trying to code a pointcut in a per clause and + // put out a compiler error. + if (bindings.directlyInAdvice() && bindings.getEnclosingAdvice() == null) { + // Assumption: if() is in a per clause if we say we are directly in advice + // but we have no enclosing advice. + inAspect.getWorld().showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.IF_IN_PERCLAUSE), + this.getSourceLocation(), null); + return Pointcut.makeMatchesNothing(Pointcut.CONCRETE); + } + + if (bindings.directlyInAdvice()) { + ShadowMunger advice = bindings.getEnclosingAdvice(); + if (advice instanceof Advice) { + ret.baseArgsCount = ((Advice) advice).getBaseParameterCount(); + } else { + ret.baseArgsCount = 0; + } + ret.residueSource = advice.getPointcut().concretize(inAspect, inAspect, ret.baseArgsCount, advice); + } else { + ResolvedPointcutDefinition def = bindings.peekEnclosingDefinition(); + if (def == CflowPointcut.CFLOW_MARKER) { + inAspect.getWorld().showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.IF_LEXICALLY_IN_CFLOW), + getSourceLocation(), null); + return Pointcut.makeMatchesNothing(Pointcut.CONCRETE); + } + ret.baseArgsCount = def.getParameterTypes().length; + + // for @style, we have implicit binding for JoinPoint.* things + // FIXME AV - will lead to failure for "args(jp)" test(jp, thejp) / see args() implementation + if (ret.extraParameterFlags < 0) { + ret.baseArgsCount = 0; + for (int i = 0; i < testMethod.getParameterTypes().length; i++) { + String argSignature = testMethod.getParameterTypes()[i].getSignature(); + if (AjcMemberMaker.TYPEX_JOINPOINT.getSignature().equals(argSignature) + || AjcMemberMaker.TYPEX_PROCEEDINGJOINPOINT.getSignature().equals(argSignature) + || AjcMemberMaker.TYPEX_STATICJOINPOINT.getSignature().equals(argSignature) + || AjcMemberMaker.TYPEX_ENCLOSINGSTATICJOINPOINT.getSignature().equals(argSignature)) { + + } else { + ret.baseArgsCount++; + } + } + } + + IntMap newBindings = IntMap.idMap(ret.baseArgsCount); + newBindings.copyContext(bindings); + ret.residueSource = def.getPointcut().concretize(inAspect, declaringType, newBindings); + } + + return ret; + } + + // we can't touch "if" methods + public Pointcut parameterizeWith(Map typeVariableMap, World w) { + return this; + } + + // public static Pointcut MatchesNothing = new MatchesNothingPointcut(); + // ??? there could possibly be some good optimizations to be done at this point + public static IfPointcut makeIfFalsePointcut(State state) { + IfPointcut ret = new IfFalsePointcut(); + ret.state = state; + return ret; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public static class IfFalsePointcut extends IfPointcut { + + public IfFalsePointcut() { + super(null, 0); + this.pointcutKind = Pointcut.IF_FALSE; + } + + public int couldMatchKinds() { + return Shadow.NO_SHADOW_KINDS_BITS; + } + + public boolean alwaysFalse() { + return true; + } + + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + return Literal.FALSE; // can only get here if an earlier error occurred + } + + public FuzzyBoolean fastMatch(FastMatchInfo type) { + return FuzzyBoolean.NO; + } + + protected FuzzyBoolean matchInternal(Shadow shadow) { + return FuzzyBoolean.NO; + } + + public void resolveBindings(IScope scope, Bindings bindings) { + } + + public void postRead(ResolvedType enclosingType) { + } + + public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + if (isDeclare(bindings.getEnclosingAdvice())) { + // Enforce rule about which designators are supported in declare + inAspect.getWorld().showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.IF_IN_DECLARE), + bindings.getEnclosingAdvice().getSourceLocation(), null); + return Pointcut.makeMatchesNothing(Pointcut.CONCRETE); + } + return makeIfFalsePointcut(state); + } + + public void write(DataOutputStream s) throws IOException { + s.writeByte(Pointcut.IF_FALSE); + } + + public int hashCode() { + int result = 17; + return result; + } + + public String toString() { + return "if(false)"; + } + } + + public static IfPointcut makeIfTruePointcut(State state) { + IfPointcut ret = new IfTruePointcut(); + ret.state = state; + return ret; + } + + public static class IfTruePointcut extends IfPointcut { + + public IfTruePointcut() { + super(null, 0); + this.pointcutKind = Pointcut.IF_TRUE; + } + + public boolean alwaysTrue() { + return true; + } + + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + return Literal.TRUE; // can only get here if an earlier error occurred + } + + public FuzzyBoolean fastMatch(FastMatchInfo type) { + return FuzzyBoolean.YES; + } + + protected FuzzyBoolean matchInternal(Shadow shadow) { + return FuzzyBoolean.YES; + } + + public void resolveBindings(IScope scope, Bindings bindings) { + } + + public void postRead(ResolvedType enclosingType) { + } + + public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + if (isDeclare(bindings.getEnclosingAdvice())) { + // Enforce rule about which designators are supported in declare + inAspect.getWorld().showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.IF_IN_DECLARE), + bindings.getEnclosingAdvice().getSourceLocation(), null); + return Pointcut.makeMatchesNothing(Pointcut.CONCRETE); + } + return makeIfTruePointcut(state); + } + + public void write(DataOutputStream s) throws IOException { + s.writeByte(IF_TRUE); + } + + public int hashCode() { + int result = 37; + return result; + } + + public String toString() { + return "if(true)"; + } + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/KindedPointcut.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/KindedPointcut.java new file mode 100644 index 000000000..2d68d17b8 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/KindedPointcut.java @@ -0,0 +1,393 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.util.Map; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.Checker; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.ShadowMunger; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Test; + +public class KindedPointcut extends Pointcut { + Shadow.Kind kind; + private SignaturePattern signature; + private int matchKinds; + + private ShadowMunger munger = null; // only set after concretization + + public KindedPointcut( + Shadow.Kind kind, + SignaturePattern signature) { + this.kind = kind; + this.signature = signature; + this.pointcutKind = KINDED; + this.matchKinds = kind.bit; + } + public KindedPointcut( + Shadow.Kind kind, + SignaturePattern signature, + ShadowMunger munger) { + this(kind, signature); + this.munger = munger; + } + + public SignaturePattern getSignature() { + return signature; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.Pointcut#couldMatchKinds() + */ + public int couldMatchKinds() { + return matchKinds; + } + + public boolean couldEverMatchSameJoinPointsAs(KindedPointcut other) { + if (this.kind != other.kind) return false; + String myName = signature.getName().maybeGetSimpleName(); + String yourName = other.signature.getName().maybeGetSimpleName(); + if (myName != null && yourName != null) { + if ( !myName.equals(yourName)) { + return false; + } + } + if (signature.getParameterTypes().ellipsisCount == 0) { + if (other.signature.getParameterTypes().ellipsisCount == 0) { + if (signature.getParameterTypes().getTypePatterns().length != + other.signature.getParameterTypes().getTypePatterns().length) { + return false; + } + } + } + return true; + } + + public FuzzyBoolean fastMatch(FastMatchInfo info) { + if (info.getKind() != null) { + if (info.getKind() != kind) return FuzzyBoolean.NO; + } + + return FuzzyBoolean.MAYBE; + } + + protected FuzzyBoolean matchInternal(Shadow shadow) { + if (shadow.getKind() != kind) return FuzzyBoolean.NO; + + if (shadow.getKind() == Shadow.SynchronizationLock && kind == Shadow.SynchronizationLock) return FuzzyBoolean.YES; + if (shadow.getKind() == Shadow.SynchronizationUnlock && kind == Shadow.SynchronizationUnlock) return FuzzyBoolean.YES; + + if (!signature.matches(shadow.getMatchingSignature(), shadow.getIWorld(),this.kind == Shadow.MethodCall)){ + + if(kind == Shadow.MethodCall) { + warnOnConfusingSig(shadow); + //warnOnBridgeMethod(shadow); + } + return FuzzyBoolean.NO; + } + + return FuzzyBoolean.YES; + } + + +// private void warnOnBridgeMethod(Shadow shadow) { +// if (shadow.getIWorld().getLint().noJoinpointsForBridgeMethods.isEnabled()) { +// ResolvedMember rm = shadow.getSignature().resolve(shadow.getIWorld()); +// if (rm!=null) { +// int shadowModifiers = rm.getModifiers(); //shadow.getSignature().getModifiers(shadow.getIWorld()); +// if (ResolvedType.hasBridgeModifier(shadowModifiers)) { +// shadow.getIWorld().getLint().noJoinpointsForBridgeMethods.signal(new String[]{},getSourceLocation(), +// new ISourceLocation[]{shadow.getSourceLocation()}); +// } +// } +// } +// } + + private void warnOnConfusingSig(Shadow shadow) { + // Don't do all this processing if we don't need to ! + if (!shadow.getIWorld().getLint().unmatchedSuperTypeInCall.isEnabled()) return; + + // no warnings for declare error/warning + if (munger instanceof Checker) return; + + World world = shadow.getIWorld(); + + // warning never needed if the declaring type is any + UnresolvedType exactDeclaringType = signature.getDeclaringType().getExactType(); + + ResolvedType shadowDeclaringType = + shadow.getSignature().getDeclaringType().resolve(world); + + if (signature.getDeclaringType().isStar() + || ResolvedType.isMissing(exactDeclaringType) + || exactDeclaringType.resolve(world).isMissing()) + return; + + // warning not needed if match type couldn't ever be the declaring type + if (!shadowDeclaringType.isAssignableFrom(exactDeclaringType.resolve(world))) { + return; + } + + // if the method in the declaring type is *not* visible to the + // exact declaring type then warning not needed. + ResolvedMember rm = shadow.getSignature().resolve(world); + // rm can be null in the case where we are binary weaving, and looking at a class with a call to a method in another class, + // but because of class incompatibilities, the method does not exist on the target class anymore. + // this will be reported elsewhere. + if (rm == null) return; + + int shadowModifiers = rm.getModifiers(); + if (!ResolvedType + .isVisible( + shadowModifiers, + shadowDeclaringType, + exactDeclaringType.resolve(world))) { + return; + } + + if (!signature.getReturnType().matchesStatically(shadow.getSignature().getReturnType().resolve(world))) { + // Covariance issue... + // The reason we didn't match is that the type pattern for the pointcut (Car) doesn't match the + // return type for the specific declaration at the shadow. (FastCar Sub.getCar()) + // XXX Put out another XLINT in this case? + return; + } + // PR60015 - Don't report the warning if the declaring type is object and 'this' is an interface + if (exactDeclaringType.resolve(world).isInterface() && shadowDeclaringType.equals(world.resolve("java.lang.Object"))) { + return; + } + + SignaturePattern nonConfusingPattern = + new SignaturePattern( + signature.getKind(), + signature.getModifiers(), + signature.getReturnType(), + TypePattern.ANY, + signature.getName(), + signature.getParameterTypes(), + signature.getThrowsPattern(), + signature.getAnnotationPattern()); + + if (nonConfusingPattern + .matches(shadow.getSignature(), shadow.getIWorld(),true)) { + shadow.getIWorld().getLint().unmatchedSuperTypeInCall.signal( + new String[] { + shadow.getSignature().getDeclaringType().toString(), + signature.getDeclaringType().toString() + }, + this.getSourceLocation(), + new ISourceLocation[] {shadow.getSourceLocation()} ); + } + } + + public boolean equals(Object other) { + if (!(other instanceof KindedPointcut)) return false; + KindedPointcut o = (KindedPointcut)other; + return o.kind == this.kind && o.signature.equals(this.signature); + } + + public int hashCode() { + int result = 17; + result = 37*result + kind.hashCode(); + result = 37*result + signature.hashCode(); + return result; + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append(kind.getSimpleName()); + buf.append("("); + buf.append(signature.toString()); + buf.append(")"); + return buf.toString(); + } + + + public void postRead(ResolvedType enclosingType) { + signature.postRead(enclosingType); + } + + public void write(DataOutputStream s) throws IOException { + s.writeByte(Pointcut.KINDED); + kind.write(s); + signature.write(s); + writeLocation(s); + } + + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + Shadow.Kind kind = Shadow.Kind.read(s); + SignaturePattern sig = SignaturePattern.read(s, context); + KindedPointcut ret = new KindedPointcut(kind, sig); + ret.readLocation(context, s); + return ret; + } + + // XXX note: there is no namebinding in any kinded pointcut. + // still might want to do something for better error messages + // We want to do something here to make sure we don't sidestep the parameter + // list in capturing type identifiers. + public void resolveBindings(IScope scope, Bindings bindings) { + if (kind == Shadow.Initialization) { +// scope.getMessageHandler().handleMessage( +// MessageUtil.error( +// "initialization unimplemented in 1.1beta1", +// this.getSourceLocation())); + } + signature = signature.resolveBindings(scope, bindings); + + + if (kind == Shadow.ConstructorExecution) { // Bug fix 60936 + if (signature.getDeclaringType() != null) { + World world = scope.getWorld(); + UnresolvedType exactType = signature.getDeclaringType().getExactType(); + if (signature.getKind() == Member.CONSTRUCTOR && + !ResolvedType.isMissing(exactType) && + exactType.resolve(world).isInterface() && + !signature.getDeclaringType().isIncludeSubtypes()) { + world.getLint().noInterfaceCtorJoinpoint.signal(exactType.toString(), getSourceLocation()); + } + } + } + + // no parameterized types + if (kind == Shadow.StaticInitialization) { + HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor + visitor = new HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor(); + signature.getDeclaringType().traverse(visitor, null); + if (visitor.wellHasItThen/*?*/()) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.NO_STATIC_INIT_JPS_FOR_PARAMETERIZED_TYPES), + getSourceLocation())); + } + } + + // no parameterized types in declaring type position + if ((kind == Shadow.FieldGet) || (kind == Shadow.FieldSet)) { + HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor + visitor = new HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor(); + signature.getDeclaringType().traverse(visitor, null); + if (visitor.wellHasItThen/*?*/()) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.GET_AND_SET_DONT_SUPPORT_DEC_TYPE_PARAMETERS), + getSourceLocation())); + } + + // fields can't have a void type! + UnresolvedType returnType = signature.getReturnType().getExactType(); + if (returnType == ResolvedType.VOID) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.FIELDS_CANT_HAVE_VOID_TYPE), + getSourceLocation())); + } + } + + // no join points for initialization and preinitialization of parameterized types + // no throwable parameterized types + if ((kind == Shadow.Initialization) || (kind == Shadow.PreInitialization)) { + HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor + visitor = new HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor(); + signature.getDeclaringType().traverse(visitor, null); + if (visitor.wellHasItThen/*?*/()) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.NO_INIT_JPS_FOR_PARAMETERIZED_TYPES), + getSourceLocation())); + } + + visitor = new HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor(); + signature.getThrowsPattern().traverse(visitor, null); + if (visitor.wellHasItThen/*?*/()) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.NO_GENERIC_THROWABLES), + getSourceLocation())); + } + } + + // no parameterized types in declaring type position + // no throwable parameterized types + if ((kind == Shadow.MethodExecution) || (kind == Shadow.ConstructorExecution)) { + HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor + visitor = new HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor(); + signature.getDeclaringType().traverse(visitor, null); + if (visitor.wellHasItThen/*?*/()) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.EXECUTION_DOESNT_SUPPORT_PARAMETERIZED_DECLARING_TYPES), + getSourceLocation())); + } + + visitor = new HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor(); + signature.getThrowsPattern().traverse(visitor, null); + if (visitor.wellHasItThen/*?*/()) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.NO_GENERIC_THROWABLES), + getSourceLocation())); + } + } + + // no parameterized types in declaring type position + // no throwable parameterized types + if ((kind == Shadow.MethodCall) || (kind == Shadow.ConstructorCall)) { + HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor + visitor = new HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor(); + signature.getDeclaringType().traverse(visitor, null); + if (visitor.wellHasItThen/*?*/()) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.CALL_DOESNT_SUPPORT_PARAMETERIZED_DECLARING_TYPES), + getSourceLocation())); + } + + visitor = new HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor(); + signature.getThrowsPattern().traverse(visitor, null); + if (visitor.wellHasItThen/*?*/()) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.NO_GENERIC_THROWABLES), + getSourceLocation())); + } + if (!scope.getWorld().isJoinpointArrayConstructionEnabled() && + kind==Shadow.ConstructorCall && + signature.getDeclaringType().isArray()) { + scope.message(MessageUtil.warn(WeaverMessages.format(WeaverMessages.NO_NEWARRAY_JOINPOINTS_BY_DEFAULT),getSourceLocation())); + } + } + } + + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + return match(shadow).alwaysTrue() ? Literal.TRUE : Literal.FALSE; + } + + public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + Pointcut ret = new KindedPointcut(kind, signature, bindings.getEnclosingAdvice()); + ret.copyLocationFrom(this); + return ret; + } + + public Pointcut parameterizeWith(Map typeVariableMap,World w) { + Pointcut ret = new KindedPointcut(kind, signature.parameterizeWith(typeVariableMap,w), munger ); + ret.copyLocationFrom(this); + return ret; + } + + public Shadow.Kind getKind() { + return kind; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ModifiersPattern.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ModifiersPattern.java new file mode 100644 index 000000000..7d304c6bc --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ModifiersPattern.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.patterns; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.Map; + +import org.aspectj.weaver.VersionedDataInputStream; + +public class ModifiersPattern extends PatternNode { + private int requiredModifiers; + private int forbiddenModifiers; + + public static final ModifiersPattern ANY = new ModifiersPattern(0, 0); + + public ModifiersPattern(int requiredModifiers, int forbiddenModifiers) { + this.requiredModifiers = requiredModifiers; + this.forbiddenModifiers = forbiddenModifiers; + } + + public String toString() { + if (this == ANY) return ""; + + String ret = Modifier.toString(requiredModifiers); + if (forbiddenModifiers == 0) return ret; + else return ret + " !" + Modifier.toString(forbiddenModifiers); + } + + public boolean equals(Object other) { + if (!(other instanceof ModifiersPattern)) return false; + ModifiersPattern o = (ModifiersPattern)other; + return o.requiredModifiers == this.requiredModifiers && + o.forbiddenModifiers == this.forbiddenModifiers; + } + public int hashCode() { + int result = 17; + result = 37*result + requiredModifiers; + result = 37*result + forbiddenModifiers; + return result; + } + + public boolean matches(int modifiers) { + return ((modifiers & requiredModifiers) == requiredModifiers) && + ((modifiers & forbiddenModifiers) == 0); + } + + + public static ModifiersPattern read(VersionedDataInputStream s) throws IOException { + int requiredModifiers = s.readShort(); + int forbiddenModifiers = s.readShort(); + if (requiredModifiers == 0 && forbiddenModifiers == 0) return ANY; + return new ModifiersPattern(requiredModifiers, forbiddenModifiers); + } + + /** + * @see org.aspectj.weaver.patterns.PatternNode#write(DataOutputStream) + */ + public void write(DataOutputStream s) throws IOException { + //s.writeByte(MODIFIERS_PATTERN); + s.writeShort(requiredModifiers); + s.writeShort(forbiddenModifiers); + } + + + private static Map modifierFlags = null; + + public static int getModifierFlag(String name) { + if (modifierFlags == null) { + modifierFlags = new HashMap(); + int flag = 1; + while (flag <= Modifier.STRICT) { + String flagName = Modifier.toString(flag); + modifierFlags.put(flagName, new Integer(flag)); + flag = flag << 1; + } + } + Integer flag = (Integer)modifierFlags.get(name); + if (flag == null) return -1; + return flag.intValue(); + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/NameBindingPointcut.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/NameBindingPointcut.java new file mode 100644 index 000000000..511ff6859 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/NameBindingPointcut.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.patterns; + +import java.util.List; + +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Test; +import org.aspectj.weaver.ast.Var; + +/** + * Common super type for Pointcuts that can bind formal parameters. + * + * @author Erik Hilsdale + * @author Jim Hugunin + */ +public abstract class NameBindingPointcut extends Pointcut { + + public NameBindingPointcut() { + super(); + } + + protected Test exposeStateForVar(Var var,TypePattern type, ExposedState state, World world) { + if (type instanceof BindingTypePattern) { + BindingTypePattern b = (BindingTypePattern)type; + state.set(b.getFormalIndex(), var); + } + ResolvedType myType = type.getExactType().resolve(world); + if (myType.isParameterizedType()) { + // unchecked warning already issued... + myType = (ResolvedType) myType.getRawType(); + } + return Test.makeInstanceof(var, myType.resolve(world)); + } + + public abstract List/**/ getBindingTypePatterns(); + public abstract List/**/ getBindingAnnotationTypePatterns(); + + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/NamePattern.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/NamePattern.java new file mode 100644 index 000000000..4a63c8daa --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/NamePattern.java @@ -0,0 +1,171 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; + +import org.aspectj.weaver.VersionedDataInputStream; + +public class NamePattern extends PatternNode { + char[] pattern; + int starCount = 0; + private int hashcode = -1; + + public static final NamePattern ELLIPSIS = new NamePattern(""); + public static final NamePattern ANY = new NamePattern("*"); + + public NamePattern(String name) { + this(name.toCharArray()); + } + + public NamePattern(char[] pattern) { + this.pattern = pattern; + + for (int i=0, len=pattern.length; i len2+1) return false; + + int i2=0; + for (int i1=0; i1 "); + boolean b = outOfStar(a1, a2, 0, 0, len1 - starCount, len2, starCount); +// System.err.println(b); + return b; + } + } + private static boolean outOfStar(final char[] pattern, final char[] target, + int pi, int ti, + int pLeft, int tLeft, + final int starsLeft) { + if (pLeft > tLeft) return false; + while (true) { + // invariant: if (tLeft > 0) then (ti < target.length && pi < pattern.length) + if (tLeft == 0) return true; + if (pLeft == 0) { + return (starsLeft > 0); + } + if (pattern[pi] == '*') { + return inStar(pattern, target, pi+1, ti, pLeft, tLeft, starsLeft-1); + } + if (target[ti] != pattern[pi]) { + return false; + } + pi++; ti++; pLeft--; tLeft--; + } + } + private static boolean inStar(final char[] pattern, final char[] target, + int pi, int ti, + final int pLeft, int tLeft, + int starsLeft) { + // invariant: pLeft > 0, so we know we'll run out of stars and find a real char in pattern + char patternChar = pattern[pi]; + while (patternChar == '*') { + starsLeft--; + patternChar = pattern[++pi]; + } + while (true) { + // invariant: if (tLeft > 0) then (ti < target.length) + if (pLeft > tLeft) return false; + if (target[ti] == patternChar) { + if (outOfStar(pattern, target, pi+1, ti+1, pLeft-1, tLeft-1, starsLeft)) return true; + } + ti++; tLeft--; + } + } + + public boolean matches(String other) { + return matches(other.toCharArray()); + } + + public String toString() { + return new String(pattern); + } + + public boolean equals(Object other) { + if (other instanceof NamePattern) { + NamePattern otherPat = (NamePattern)other; + if (otherPat.starCount!=this.starCount) return false; + if (otherPat.pattern.length!=this.pattern.length) return false; + for (int i = 0; i < this.pattern.length; i++) { + if (this.pattern[i]!=otherPat.pattern[i]) return false; + } + return true; + } + return false; + } + + public int hashCode() { + return hashcode; + } + + + public void write(DataOutputStream out) throws IOException { + out.writeUTF(new String(pattern)); + } + + public static NamePattern read(VersionedDataInputStream in) throws IOException { + String s = in.readUTF(); + if (s.length() == 0) return ELLIPSIS; + return new NamePattern(s); + } + /** + * Method maybeGetSimpleName. + * @return String + */ + public String maybeGetSimpleName() { + if (starCount == 0 && pattern.length > 0) return new String(pattern); + return null; + } + + /** + * Method isAny. + * @return boolean + */ + public boolean isAny() { + return starCount == 1 && pattern.length == 1; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/NotAnnotationTypePattern.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/NotAnnotationTypePattern.java new file mode 100644 index 000000000..02802946a --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/NotAnnotationTypePattern.java @@ -0,0 +1,120 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.AnnotatedElement; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; +import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; + +public class NotAnnotationTypePattern extends AnnotationTypePattern { + + AnnotationTypePattern negatedPattern; + + public NotAnnotationTypePattern(AnnotationTypePattern pattern) { + this.negatedPattern = pattern; + setLocation(pattern.getSourceContext(), pattern.getStart(), pattern.getEnd()); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.AnnotationTypePattern#matches(org.aspectj.weaver.AnnotatedElement) + */ + public FuzzyBoolean matches(AnnotatedElement annotated) { + return negatedPattern.matches(annotated).not(); + } + + public FuzzyBoolean matches(AnnotatedElement annotated,ResolvedType[] parameterAnnotations) { + return negatedPattern.matches(annotated,parameterAnnotations).not(); + } + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.AnnotationTypePattern#resolve(org.aspectj.weaver.World) + */ + public void resolve(World world) { + negatedPattern.resolve(world); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.AnnotationTypePattern#resolveBindings(org.aspectj.weaver.patterns.IScope, org.aspectj.weaver.patterns.Bindings, boolean) + */ + public AnnotationTypePattern resolveBindings(IScope scope, + Bindings bindings, boolean allowBinding) { + negatedPattern = negatedPattern.resolveBindings(scope,bindings,allowBinding); + return this; + } + + + public AnnotationTypePattern parameterizeWith(Map typeVariableMap,World w) { + AnnotationTypePattern newNegatedPattern = negatedPattern.parameterizeWith(typeVariableMap,w); + NotAnnotationTypePattern ret = new NotAnnotationTypePattern(newNegatedPattern); + ret.copyLocationFrom(this); + if (this.isForParameterAnnotationMatch()) ret.setForParameterAnnotationMatch(); + return ret; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.PatternNode#write(java.io.DataOutputStream) + */ + public void write(DataOutputStream s) throws IOException { + s.writeByte(AnnotationTypePattern.NOT); + negatedPattern.write(s); + writeLocation(s); + s.writeBoolean(isForParameterAnnotationMatch()); + } + + public static AnnotationTypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + AnnotationTypePattern ret = new NotAnnotationTypePattern(AnnotationTypePattern.read(s,context)); + ret.readLocation(context,s); + if (s.getMajorVersion()>=WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ160) { + if (s.readBoolean()) ret.setForParameterAnnotationMatch(); + } + return ret; + } + + public boolean equals(Object obj) { + if (!(obj instanceof NotAnnotationTypePattern)) return false; + NotAnnotationTypePattern other = (NotAnnotationTypePattern) obj; + return other.negatedPattern.equals(negatedPattern) && other.isForParameterAnnotationMatch()==isForParameterAnnotationMatch(); + } + + public int hashCode() { + int result = 17 + 37*negatedPattern.hashCode(); + result = 37*result +(isForParameterAnnotationMatch()?0:1); + return result; + } + + public String toString() { + return "!" + negatedPattern.toString(); + } + + public AnnotationTypePattern getNegatedPattern() { + return negatedPattern; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public Object traverse(PatternNodeVisitor visitor, Object data) { + Object ret = accept(visitor,data); + negatedPattern.traverse(visitor,ret); + return ret; + } + + public void setForParameterAnnotationMatch() { + negatedPattern.setForParameterAnnotationMatch(); + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/NotPointcut.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/NotPointcut.java new file mode 100644 index 000000000..4a131c9a8 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/NotPointcut.java @@ -0,0 +1,123 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Test; + +public class NotPointcut extends Pointcut { + private Pointcut body; + public NotPointcut(Pointcut negated) { + super(); + this.body = negated; + this.pointcutKind = NOT; + setLocation(negated.getSourceContext(), negated.getStart(),negated.getEnd()); // should that be at least start-1? + } + + public NotPointcut(Pointcut pointcut, int startPos) { + this(pointcut); + setLocation(pointcut.getSourceContext(), startPos, pointcut.getEnd()); + } + + public int couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS_BITS; + } + + public Pointcut getNegatedPointcut() { return body; } + + public FuzzyBoolean fastMatch(FastMatchInfo type) { + return body.fastMatch(type).not(); + } + + protected FuzzyBoolean matchInternal(Shadow shadow) { + return body.match(shadow).not(); + } + + public String toString() { + return "!" + body.toString(); + + } + + public boolean equals(Object other) { + if (!(other instanceof NotPointcut)) return false; + NotPointcut o = (NotPointcut)other; + return o.body.equals(body); + } + public int hashCode() { + return 37*23 + body.hashCode(); + } + + + public void resolveBindings(IScope scope, Bindings bindings) { + //Bindings old = bindings.copy(); + + //Bindings newBindings = new Bindings(bindings.size()); + + + body.resolveBindings(scope, null); + + //newBindings.checkEmpty(scope, "negation does not allow binding"); + //bindings.checkEquals(old, scope); + + } + + public void write(DataOutputStream s) throws IOException { + s.writeByte(Pointcut.NOT); + body.write(s); + writeLocation(s); + } + + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + NotPointcut ret = new NotPointcut(Pointcut.read(s, context)); + ret.readLocation(context, s); + return ret; + } + + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + return Test.makeNot(body.findResidue(shadow, state)); + } + + public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + Pointcut ret = new NotPointcut(body.concretize(inAspect, declaringType, bindings)); + ret.copyLocationFrom(this); + return ret; + } + + public Pointcut parameterizeWith(Map typeVariableMap,World w) { + Pointcut ret = new NotPointcut(body.parameterizeWith(typeVariableMap,w)); + ret.copyLocationFrom(this); + return ret; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public Object traverse(PatternNodeVisitor visitor, Object data) { + Object ret = accept(visitor,data); + this.body.traverse(visitor,ret); + return ret; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/NotTypePattern.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/NotTypePattern.java new file mode 100644 index 000000000..5ade60e45 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/NotTypePattern.java @@ -0,0 +1,154 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.AjAttribute; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; + +/** + * !TypePattern + * + *

any binding to formals is explicitly forbidden for any composite, ! is + * just the most obviously wrong case. + * + * @author Erik Hilsdale + * @author Jim Hugunin + */ +public class NotTypePattern extends TypePattern { + private TypePattern negatedPattern; + + public NotTypePattern(TypePattern pattern) { + super(false,false); //??? we override all methods that care about includeSubtypes + this.negatedPattern = pattern; + setLocation(pattern.getSourceContext(), pattern.getStart(), pattern.getEnd()); + } + + public TypePattern getNegatedPattern() { + return negatedPattern; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.TypePattern#couldEverMatchSameTypesAs(org.aspectj.weaver.patterns.TypePattern) + */ + protected boolean couldEverMatchSameTypesAs(TypePattern other) { + return true; + } + + public FuzzyBoolean matchesInstanceof(ResolvedType type) { + return negatedPattern.matchesInstanceof(type).not(); + } + + protected boolean matchesExactly(ResolvedType type) { + return (!negatedPattern.matchesExactly(type) && annotationPattern.matches(type).alwaysTrue()); + } + + protected boolean matchesExactly(ResolvedType type, ResolvedType annotatedType) { + return (!negatedPattern.matchesExactly(type,annotatedType) && annotationPattern.matches(annotatedType).alwaysTrue()); + } + + public boolean matchesStatically(ResolvedType type) { + return !negatedPattern.matchesStatically(type); + } + + public void setAnnotationTypePattern(AnnotationTypePattern annPatt) { + super.setAnnotationTypePattern(annPatt); + } + + public void setIsVarArgs(boolean isVarArgs) { + negatedPattern.setIsVarArgs(isVarArgs); + } + + + public void write(DataOutputStream s) throws IOException { + s.writeByte(TypePattern.NOT); + negatedPattern.write(s); + annotationPattern.write(s); + writeLocation(s); + } + + public static TypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + TypePattern ret = new NotTypePattern(TypePattern.read(s, context)); + if (s.getMajorVersion()>=AjAttribute.WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ150) { + ret.annotationPattern = AnnotationTypePattern.read(s,context); + } + ret.readLocation(context, s); + return ret; + } + + public TypePattern resolveBindings( + IScope scope, + Bindings bindings, + boolean allowBinding, boolean requireExactType) + { + if (requireExactType) return notExactType(scope); + negatedPattern = negatedPattern.resolveBindings(scope, bindings, false, false); + return this; + } + + public TypePattern parameterizeWith(Map typeVariableMap,World w) { + TypePattern newNegatedPattern = negatedPattern.parameterizeWith(typeVariableMap,w); + NotTypePattern ret = new NotTypePattern(newNegatedPattern); + ret.copyLocationFrom(this); + return ret; + } + + public String toString() { + StringBuffer buff = new StringBuffer(); + if (annotationPattern != AnnotationTypePattern.ANY) { + buff.append('('); + buff.append(annotationPattern.toString()); + buff.append(' '); + } + buff.append('!'); + buff.append(negatedPattern); + if (annotationPattern != AnnotationTypePattern.ANY) { + buff.append(')'); + } + return buff.toString(); + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + if (! (obj instanceof NotTypePattern)) return false; + return (negatedPattern.equals(((NotTypePattern)obj).negatedPattern)); + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return 17 + 37 * negatedPattern.hashCode(); + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public Object traverse(PatternNodeVisitor visitor, Object data) { + Object ret = accept(visitor,data); + negatedPattern.traverse(visitor, ret); + return ret; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/OrAnnotationTypePattern.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/OrAnnotationTypePattern.java new file mode 100644 index 000000000..9c70ac337 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/OrAnnotationTypePattern.java @@ -0,0 +1,123 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.AnnotatedElement; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; +import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; + +public class OrAnnotationTypePattern extends AnnotationTypePattern { + + private AnnotationTypePattern left; + private AnnotationTypePattern right; + + public OrAnnotationTypePattern(AnnotationTypePattern left, AnnotationTypePattern right) { + this.left = left; + this.right = right; + setLocation(left.getSourceContext(), left.getStart(), right.getEnd()); + } + + public FuzzyBoolean matches(AnnotatedElement annotated) { + return left.matches(annotated).or(right.matches(annotated)); + } + + public FuzzyBoolean matches(AnnotatedElement annotated, ResolvedType[] parameterAnnotations ) { + return left.matches(annotated,parameterAnnotations).or(right.matches(annotated,parameterAnnotations)); + } + + public void resolve(World world) { + left.resolve(world); + right.resolve(world); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.AnnotationTypePattern#resolveBindings(org.aspectj.weaver.patterns.IScope, org.aspectj.weaver.patterns.Bindings, boolean) + */ + public AnnotationTypePattern resolveBindings(IScope scope, + Bindings bindings, boolean allowBinding) { + left = left.resolveBindings(scope,bindings,allowBinding); + right =right.resolveBindings(scope,bindings,allowBinding); + return this; + } + + public AnnotationTypePattern parameterizeWith(Map typeVariableMap,World w) { + AnnotationTypePattern newLeft = left.parameterizeWith(typeVariableMap,w); + AnnotationTypePattern newRight = right.parameterizeWith(typeVariableMap,w); + OrAnnotationTypePattern ret = new OrAnnotationTypePattern(newLeft,newRight); + ret.copyLocationFrom(this); + if (isForParameterAnnotationMatch()) ret.setForParameterAnnotationMatch(); + return ret; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this,data); + } + + public Object traverse(PatternNodeVisitor visitor, Object data) { + Object ret = accept(visitor,data); + left.traverse(visitor,ret); + right.traverse(visitor,ret); + return ret; + } + + public static AnnotationTypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + AnnotationTypePattern p = new OrAnnotationTypePattern( + AnnotationTypePattern.read(s,context), + AnnotationTypePattern.read(s,context)); + p.readLocation(context,s); + if (s.getMajorVersion()>=WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ160) { + if (s.readBoolean()) p.setForParameterAnnotationMatch(); + } + return p; + } + + public void write(DataOutputStream s) throws IOException { + s.writeByte(AnnotationTypePattern.OR); + left.write(s); + right.write(s); + writeLocation(s); + s.writeBoolean(isForParameterAnnotationMatch()); + } + + public boolean equals(Object obj) { + if (!(obj instanceof OrAnnotationTypePattern)) return false; + OrAnnotationTypePattern other = (OrAnnotationTypePattern) obj; + return (left.equals(other.left) && right.equals(other.right)) && isForParameterAnnotationMatch()==other.isForParameterAnnotationMatch(); + } + + public int hashCode() { + int result = 17; + result = result*37 + left.hashCode(); + result = result*37 + right.hashCode(); + result = result*37 + (isForParameterAnnotationMatch()?0:1); + return result; + } + + public String toString() { + return "(" + left.toString() + " || " + right.toString() + ")"; + } + + public AnnotationTypePattern getLeft() { return left; } + public AnnotationTypePattern getRight() { return right; } + + public void setForParameterAnnotationMatch() { + left.setForParameterAnnotationMatch(); + right.setForParameterAnnotationMatch(); + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/OrPointcut.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/OrPointcut.java new file mode 100644 index 000000000..01637f3b6 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/OrPointcut.java @@ -0,0 +1,132 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Test; + +public class OrPointcut extends Pointcut { + Pointcut left, right; + private int couldMatchKinds; + + public OrPointcut(Pointcut left, Pointcut right) { + super(); + this.left = left; + this.right = right; + setLocation(left.getSourceContext(), left.getStart(), right.getEnd()); + this.pointcutKind = OR; + this.couldMatchKinds = left.couldMatchKinds() | right.couldMatchKinds(); + } + + public int couldMatchKinds() { + return couldMatchKinds; + } + + public FuzzyBoolean fastMatch(FastMatchInfo type) { + return left.fastMatch(type).or(right.fastMatch(type)); + } + + protected FuzzyBoolean matchInternal(Shadow shadow) { + FuzzyBoolean leftMatch = left.match(shadow); + if (leftMatch.alwaysTrue()) return leftMatch; + return leftMatch.or(right.match(shadow)); + } + + public String toString() { + return "(" + left.toString() + " || " + right.toString() + ")"; + } + + public boolean equals(Object other) { + if (!(other instanceof OrPointcut)) return false; + OrPointcut o = (OrPointcut)other; + return o.left.equals(left) && o.right.equals(right); + } + public int hashCode() { + int result = 31; + result = 37*result + left.hashCode(); + result = 37*result + right.hashCode(); + return result; + } + /** + * @see org.aspectj.weaver.patterns.Pointcut#resolveBindings(IScope, Bindings) + */ + public void resolveBindings(IScope scope, Bindings bindings) { + Bindings old = bindings == null ? null : bindings.copy(); + + left.resolveBindings(scope, bindings); + right.resolveBindings(scope, old); + if (bindings != null) bindings.checkEquals(old, scope); + + } + + public void write(DataOutputStream s) throws IOException { + s.writeByte(Pointcut.OR); + left.write(s); + right.write(s); + writeLocation(s); + } + + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + OrPointcut ret = new OrPointcut(Pointcut.read(s, context), Pointcut.read(s, context)); + ret.readLocation(context, s); + return ret; + + } + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + return Test.makeOr(left.findResidue(shadow, state), right.findResidue(shadow, state)); + } + + public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + Pointcut ret = new OrPointcut(left.concretize(inAspect, declaringType, bindings), + right.concretize(inAspect, declaringType, bindings)); + ret.copyLocationFrom(this); + return ret; + } + + public Pointcut parameterizeWith(Map typeVariableMap,World w) { + Pointcut ret = new OrPointcut(left.parameterizeWith(typeVariableMap,w), + right.parameterizeWith(typeVariableMap,w)); + ret.copyLocationFrom(this); + return ret; + } + + public Pointcut getLeft() { + return left; + } + + public Pointcut getRight() { + return right; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public Object traverse(PatternNodeVisitor visitor, Object data) { + Object ret = accept(visitor,data); + left.traverse(visitor, ret); + right.traverse(visitor,ret); + return ret; + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/OrTypePattern.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/OrTypePattern.java new file mode 100644 index 000000000..52a18d50a --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/OrTypePattern.java @@ -0,0 +1,184 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; + +/** + * left || right + * + *

any binding to formals is explicitly forbidden for any composite by the language + * + * @author Erik Hilsdale + * @author Jim Hugunin + */ +public class OrTypePattern extends TypePattern { + private TypePattern left, right; + + public OrTypePattern(TypePattern left, TypePattern right) { + super(false,false); //??? we override all methods that care about includeSubtypes + this.left = left; + this.right = right; + setLocation(left.getSourceContext(), left.getStart(), right.getEnd()); + } + + public TypePattern getRight() { + return right; + } + + public TypePattern getLeft() { + return left; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.TypePattern#couldEverMatchSameTypesAs(org.aspectj.weaver.patterns.TypePattern) + */ + protected boolean couldEverMatchSameTypesAs(TypePattern other) { + return true; // don't dive at the moment... + } + + public FuzzyBoolean matchesInstanceof(ResolvedType type) { + return left.matchesInstanceof(type).or(right.matchesInstanceof(type)); + } + + protected boolean matchesExactly(ResolvedType type) { + //??? if these had side-effects, this sort-circuit could be a mistake + return left.matchesExactly(type) || right.matchesExactly(type); + } + + protected boolean matchesExactly(ResolvedType type, ResolvedType annotatedType) { + //??? if these had side-effects, this sort-circuit could be a mistake + return left.matchesExactly(type,annotatedType) || right.matchesExactly(type,annotatedType); + } + + public boolean matchesStatically(ResolvedType type) { + return left.matchesStatically(type) || right.matchesStatically(type); + } + + public void setIsVarArgs(boolean isVarArgs) { + this.isVarArgs = isVarArgs; + left.setIsVarArgs(isVarArgs); + right.setIsVarArgs(isVarArgs); + } + + public void setAnnotationTypePattern(AnnotationTypePattern annPatt) { + if (annPatt == AnnotationTypePattern.ANY) return; + if (left.annotationPattern == AnnotationTypePattern.ANY) { + left.setAnnotationTypePattern(annPatt); + } else { + left.setAnnotationTypePattern( + new AndAnnotationTypePattern(left.annotationPattern,annPatt)); + } + if (right.annotationPattern == AnnotationTypePattern.ANY) { + right.setAnnotationTypePattern(annPatt); + } else { + right.setAnnotationTypePattern( + new AndAnnotationTypePattern(right.annotationPattern,annPatt)); + } + } + + public void write(DataOutputStream s) throws IOException { + s.writeByte(TypePattern.OR); + left.write(s); + right.write(s); + writeLocation(s); + } + + public static TypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + OrTypePattern ret = new OrTypePattern(TypePattern.read(s, context), TypePattern.read(s, context)); + ret.readLocation(context, s); + if (ret.left.isVarArgs && ret.right.isVarArgs) ret.isVarArgs = true; + return ret; + } + + public TypePattern resolveBindings( + IScope scope, + Bindings bindings, + boolean allowBinding, boolean requireExactType) + { + if (requireExactType) return notExactType(scope); + left = left.resolveBindings(scope, bindings, false, false); + right = right.resolveBindings(scope, bindings, false, false); + return this; + } + + public TypePattern parameterizeWith(Map typeVariableMap,World w) { + TypePattern newLeft = left.parameterizeWith(typeVariableMap,w); + TypePattern newRight = right.parameterizeWith(typeVariableMap,w); + OrTypePattern ret = new OrTypePattern(newLeft,newRight); + ret.copyLocationFrom(this); + return ret; + } + + public String toString() { + StringBuffer buff = new StringBuffer(); + if (annotationPattern != AnnotationTypePattern.ANY) { + buff.append('('); + buff.append(annotationPattern.toString()); + buff.append(' '); + } + buff.append('('); + buff.append(left.toString()); + buff.append(" || "); + buff.append(right.toString()); + buff.append(')'); + if (annotationPattern != AnnotationTypePattern.ANY) { + buff.append(')'); + } + return buff.toString(); + } + + public boolean isStarAnnotation() { + return left.isStarAnnotation() || right.isStarAnnotation(); + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + if (! (obj instanceof OrTypePattern)) return false; + OrTypePattern other = (OrTypePattern) obj; + return left.equals(other.left) && right.equals(other.right); + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + int ret = 17; + ret = ret + 37 * left.hashCode(); + ret = ret + 37 * right.hashCode(); + return ret; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public Object traverse(PatternNodeVisitor visitor, Object data) { + Object ret = accept(visitor,data); + left.traverse(visitor, ret); + right.traverse(visitor, ret); + return ret; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ParserException.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ParserException.java new file mode 100644 index 000000000..cce928740 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ParserException.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.patterns; + +import org.aspectj.weaver.IHasPosition; + + +public class ParserException extends RuntimeException { + private IHasPosition token; + + public ParserException(String message, IHasPosition token) { + super(message); + this.token = token; + } + + public IHasPosition getLocation() { + return token; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PatternNode.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PatternNode.java new file mode 100644 index 000000000..8cf9f3ffb --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PatternNode.java @@ -0,0 +1,89 @@ +/* ******************************************************************* + * 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 java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.weaver.IHasSourceLocation; +import org.aspectj.weaver.ISourceContext; + +public abstract class PatternNode implements IHasSourceLocation { + protected int start, end; + protected ISourceContext sourceContext; + + public PatternNode() { + super(); + start = end = -1; + } + + public int getStart() { + return start + (sourceContext != null ? sourceContext.getOffset() : 0); + } + + public int getEnd() { + return end + (sourceContext != null ? sourceContext.getOffset() : 0); + } + + public ISourceContext getSourceContext() { + return sourceContext; + } + + public String getFileName() { + return "unknown"; + } + + public void setLocation(ISourceContext sourceContext, int start, int end) { + this.sourceContext = sourceContext; + this.start = start; + this.end = end; + } + + public void copyLocationFrom(PatternNode other) { + this.start = other.start; + this.end = other.end; + this.sourceContext = other.sourceContext; + } + + public ISourceLocation getSourceLocation() { + //System.out.println("get context: " + this + " is " + sourceContext); + if (sourceContext == null) { + //System.err.println("no context: " + this); + return null; + } + return sourceContext.makeSourceLocation(this); + } + + public abstract void write(DataOutputStream s) throws IOException; + + + public void writeLocation(DataOutputStream s) throws IOException { + s.writeInt(start); + s.writeInt(end); + } + + public void readLocation(ISourceContext context, DataInputStream s) throws IOException { + start = s.readInt(); + end = s.readInt(); + this.sourceContext = context; + } + + public abstract Object accept(PatternNodeVisitor visitor, Object data); + + public Object traverse(PatternNodeVisitor visitor, Object data) { + return accept(visitor,data); + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PatternNodeVisitor.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PatternNodeVisitor.java new file mode 100644 index 000000000..3fedd718e --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PatternNodeVisitor.java @@ -0,0 +1,95 @@ +/******************************************************************************* + * 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 + * Adrian Colyer refactoring for traversal and grouping by kind + *******************************************************************************/ +package org.aspectj.weaver.patterns; + + +/** + * A Pointcut or TypePattern visitor + * + * @author Alexandre Vasseur + */ +public interface PatternNodeVisitor { + + // Annotation type patterns + Object visit(AndAnnotationTypePattern node, Object data); + Object visit(AnyAnnotationTypePattern node, Object data); + Object visit(EllipsisAnnotationTypePattern node, Object data); + Object visit(ExactAnnotationTypePattern node, Object data); + Object visit(BindingAnnotationTypePattern node, Object data); + Object visit(NotAnnotationTypePattern node, Object data); + Object visit(OrAnnotationTypePattern node, Object data); + Object visit(WildAnnotationTypePattern node, Object data); + Object visit(AnnotationPatternList node, Object data); + + // Regular type patterns + Object visit(AndTypePattern node, Object data); + Object visit(AnyTypePattern node, Object data); + Object visit(AnyWithAnnotationTypePattern node, Object data); + Object visit(EllipsisTypePattern node, Object data); + Object visit(ExactTypePattern node, Object data); + Object visit(BindingTypePattern node, Object data); + Object visit(NotTypePattern node, Object data); + Object visit(NoTypePattern node, Object data); + Object visit(OrTypePattern node, Object data); + Object visit(WildTypePattern node, Object data); + Object visit(TypePatternList node, Object data); + Object visit(HasMemberTypePattern node, Object data); + + // Pointcuts + Object visit(AndPointcut node, Object data); + Object visit(CflowPointcut node, Object data); + Object visit(ConcreteCflowPointcut node, Object data); + Object visit(HandlerPointcut node, Object data); + Object visit(IfPointcut node, Object data); + Object visit(KindedPointcut node, Object data); + Object visit(Pointcut.MatchesNothingPointcut node, Object data); + Object visit(AnnotationPointcut node, Object data); + Object visit(ArgsAnnotationPointcut node, Object data); + Object visit(ArgsPointcut node, Object data); + Object visit(ThisOrTargetAnnotationPointcut node, Object data); + Object visit(ThisOrTargetPointcut node, Object data); + Object visit(WithinAnnotationPointcut node, Object data); + Object visit(WithinCodeAnnotationPointcut node, Object data); + Object visit(NotPointcut node, Object data); + Object visit(OrPointcut node, Object data); + Object visit(ReferencePointcut node, Object data); + Object visit(WithinPointcut node, Object data); + Object visit(WithincodePointcut node, Object data); + + // Per-clauses + Object visit(PerCflow node, Object data); + Object visit(PerFromSuper node, Object data); + Object visit(PerObject node, Object data); + Object visit(PerSingleton node, Object data); + Object visit(PerTypeWithin node, Object data); + + + // Declares + Object visit(DeclareAnnotation node, Object data); + Object visit(DeclareErrorOrWarning node, Object data); + Object visit(DeclareParents node, Object data); + Object visit(DeclarePrecedence node, Object data); + Object visit(DeclareSoft node, Object data); + + // Miscellaneous patterns + Object visit(ModifiersPattern node, Object data); + Object visit(NamePattern node, Object data); + Object visit(SignaturePattern node, Object data); + Object visit(ThrowsPattern node, Object data); + Object visit(TypeVariablePattern node, Object data); + Object visit(TypeVariablePatternList node,Object data); + + // Catch-all + Object visit(PatternNode node, Object data); + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PatternParser.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PatternParser.java new file mode 100644 index 000000000..a1110eb9f --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PatternParser.java @@ -0,0 +1,1585 @@ +/* ******************************************************************* + * 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 many updates since.... + * ******************************************************************/ + + +package org.aspectj.weaver.patterns; + +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.Set; + +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.MemberKind; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.internal.tools.PointcutDesignatorHandlerBasedPointcut; +import org.aspectj.weaver.reflect.ReflectionWorld; +import org.aspectj.weaver.tools.ContextBasedMatcher; +import org.aspectj.weaver.tools.PointcutDesignatorHandler; + +//XXX doesn't handle errors for extra tokens very well (sometimes ignores) +public class PatternParser { + + private ITokenSource tokenSource; + private ISourceContext sourceContext; + + /** not thread-safe, but this class is not intended to be... */ + private boolean allowHasTypePatterns = false; + + /** extension handlers used in weaver tools API only */ + private Set pointcutDesignatorHandlers = Collections.EMPTY_SET; + private ReflectionWorld world; + + /** + * Constructor for PatternParser. + */ + public PatternParser(ITokenSource tokenSource) { + super(); + this.tokenSource = tokenSource; + this.sourceContext = tokenSource.getSourceContext(); + } + + /** only used by weaver tools API */ + public void setPointcutDesignatorHandlers(Set handlers, ReflectionWorld world) { + this.pointcutDesignatorHandlers = handlers; + this.world = world; + } + + public PerClause maybeParsePerClause() { + IToken tok = tokenSource.peek(); + if (tok == IToken.EOF) return null; + if (tok.isIdentifier()) { + String name = tok.getString(); + if (name.equals("issingleton")) { + return parsePerSingleton(); + } else if (name.equals("perthis")) { + return parsePerObject(true); + } else if (name.equals("pertarget")) { + return parsePerObject(false); + } else if (name.equals("percflow")) { + return parsePerCflow(false); + } else if (name.equals("percflowbelow")) { + return parsePerCflow(true); + } else if (name.equals("pertypewithin")) { // PTWIMPL Parse the pertypewithin clause + return parsePerTypeWithin(); + } else { + return null; + } + } + return null; + } + + private PerClause parsePerCflow(boolean isBelow) { + parseIdentifier(); + eat("("); + Pointcut entry = parsePointcut(); + eat(")"); + return new PerCflow(entry, isBelow); + } + + + private PerClause parsePerObject(boolean isThis) { + parseIdentifier(); + eat("("); + Pointcut entry = parsePointcut(); + eat(")"); + return new PerObject(entry, isThis); + } + + private PerClause parsePerTypeWithin() { + parseIdentifier(); + eat("("); + TypePattern withinTypePattern = parseTypePattern(); + eat(")"); + return new PerTypeWithin(withinTypePattern); + } + + + private PerClause parsePerSingleton() { + parseIdentifier(); + eat("("); + eat(")"); + return new PerSingleton(); + } + + + public Declare parseDeclare() { + int startPos = tokenSource.peek().getStart(); + + eatIdentifier("declare"); + String kind = parseIdentifier(); + Declare ret; + if (kind.equals("error")) { + eat(":"); + ret = parseErrorOrWarning(true); + } else if (kind.equals("warning")) { + eat(":"); + ret = parseErrorOrWarning(false); + } else if (kind.equals("precedence")) { + eat(":"); + ret = parseDominates(); + } else if (kind.equals("dominates")) { + throw new ParserException("name changed to declare precedence", tokenSource.peek(-2)); + } else if (kind.equals("parents")) { + ret = parseParents(); + } else if (kind.equals("soft")) { + eat(":"); + ret = parseSoft(); + } else { + throw new ParserException("expected one of error, warning, parents, soft, precedence, @type, @method, @constructor, @field", + tokenSource.peek(-1)); + } + int endPos = tokenSource.peek(-1).getEnd(); + ret.setLocation(sourceContext, startPos, endPos); + return ret; + } + + public Declare parseDeclareAnnotation() { + int startPos = tokenSource.peek().getStart(); + + eatIdentifier("declare"); + eat("@"); + String kind = parseIdentifier(); + eat(":"); + Declare ret; + if (kind.equals("type")) { + ret = parseDeclareAtType(); + } else if (kind.equals("method")) { + ret = parseDeclareAtMethod(true); + } else if (kind.equals("field")) { + ret = parseDeclareAtField(); + } else if (kind.equals("constructor")) { + ret = parseDeclareAtMethod(false); + } else { + throw new ParserException("one of type, method, field, constructor",tokenSource.peek(-1)); + } + eat(";"); + int endPos = tokenSource.peek(-1).getEnd(); + ret.setLocation(sourceContext, startPos, endPos); + return ret; + + } + + public DeclareAnnotation parseDeclareAtType() { + allowHasTypePatterns = true; + TypePattern p = parseTypePattern(); + allowHasTypePatterns = false; + return new DeclareAnnotation(DeclareAnnotation.AT_TYPE,p); + } + + public DeclareAnnotation parseDeclareAtMethod(boolean isMethod) { + SignaturePattern sp = parseMethodOrConstructorSignaturePattern(); + boolean isConstructorPattern = (sp.getKind() == Member.CONSTRUCTOR); + if (isMethod && isConstructorPattern) { + throw new ParserException("method signature pattern",tokenSource.peek(-1)); + } + if (!isMethod && !isConstructorPattern) { + throw new ParserException("constructor signature pattern",tokenSource.peek(-1)); + } + if (isConstructorPattern) return new DeclareAnnotation(DeclareAnnotation.AT_CONSTRUCTOR,sp); + else return new DeclareAnnotation(DeclareAnnotation.AT_METHOD,sp); + } + + public DeclareAnnotation parseDeclareAtField() { + return new DeclareAnnotation(DeclareAnnotation.AT_FIELD,parseFieldSignaturePattern()); + } + + public DeclarePrecedence parseDominates() { + List l = new ArrayList(); + do { + l.add(parseTypePattern()); + } while (maybeEat(",")); + + return new DeclarePrecedence(l); + } + + private Declare parseParents() { + /* + * simplified design requires use of raw types for declare parents, no generic spec. allowed + * String[] typeParameters = maybeParseSimpleTypeVariableList(); + */ + eat(":"); + allowHasTypePatterns = true; + TypePattern p = parseTypePattern(false,false); + allowHasTypePatterns = false; + IToken t = tokenSource.next(); + if (!(t.getString().equals("extends") || t.getString().equals("implements"))) { + throw new ParserException("extends or implements", t); + } + boolean isExtends = t.getString().equals("extends"); + + List l = new ArrayList(); + do { + l.add(parseTypePattern()); + } while (maybeEat(",")); + + //XXX somewhere in the chain we need to enforce that we have only ExactTypePatterns + + DeclareParents decp = new DeclareParents(p, l,isExtends); + return decp; + } + + private Declare parseSoft() { + TypePattern p = parseTypePattern(); + eat(":"); + Pointcut pointcut = parsePointcut(); + return new DeclareSoft(p, pointcut); + } + + + + private Declare parseErrorOrWarning(boolean isError) { + Pointcut pointcut = parsePointcut(); + eat(":"); + String message = parsePossibleStringSequence(true); + return new DeclareErrorOrWarning(isError, pointcut, message); + } + + public Pointcut parsePointcut() { + Pointcut p = parseAtomicPointcut(); + if (maybeEat("&&")) { + p = new AndPointcut(p, parseNotOrPointcut()); + } + + if (maybeEat("||")) { + p = new OrPointcut(p, parsePointcut()); + } + + return p; + } + + private Pointcut parseNotOrPointcut() { + Pointcut p = parseAtomicPointcut(); + if (maybeEat("&&")) { + p = new AndPointcut(p, parsePointcut()); + } + return p; + } + + private Pointcut parseAtomicPointcut() { + if (maybeEat("!")) { + int startPos = tokenSource.peek(-1).getStart(); + Pointcut p = new NotPointcut(parseAtomicPointcut(), startPos); + return p; + } + if (maybeEat("(")) { + Pointcut p = parsePointcut(); + eat(")"); + return p; + } + if (maybeEat("@")) { + int startPos = tokenSource.peek().getStart(); + Pointcut p = parseAnnotationPointcut(); + int endPos = tokenSource.peek(-1).getEnd(); + p.setLocation(sourceContext, startPos, endPos); + return p; + } + int startPos = tokenSource.peek().getStart(); + Pointcut p = parseSinglePointcut(); + int endPos = tokenSource.peek(-1).getEnd(); + p.setLocation(sourceContext, startPos, endPos); + return p; + } + + + + public Pointcut parseSinglePointcut() { + int start = tokenSource.getIndex(); + IToken t = tokenSource.peek(); + Pointcut p = t.maybeGetParsedPointcut(); + if (p != null) { + tokenSource.next(); + return p; + } + + String kind = parseIdentifier(); +// IToken possibleTypeVariableToken = tokenSource.peek(); +// String[] typeVariables = maybeParseSimpleTypeVariableList(); + if (kind.equals("execution") || kind.equals("call") || + kind.equals("get") || kind.equals("set")) { + p = parseKindedPointcut(kind); + } else if (kind.equals("args")) { + p = parseArgsPointcut(); + } else if (kind.equals("this")) { + p = parseThisOrTargetPointcut(kind); + } else if (kind.equals("target")) { + p = parseThisOrTargetPointcut(kind); + } else if (kind.equals("within")) { + p = parseWithinPointcut(); + } else if (kind.equals("withincode")) { + p = parseWithinCodePointcut(); + } else if (kind.equals("cflow")) { + p = parseCflowPointcut(false); + } else if (kind.equals("cflowbelow")) { + p = parseCflowPointcut(true); + } else if (kind.equals("adviceexecution")) { + eat("("); + eat(")"); + p = new KindedPointcut(Shadow.AdviceExecution, + new SignaturePattern(Member.ADVICE, ModifiersPattern.ANY, + TypePattern.ANY, TypePattern.ANY, NamePattern.ANY, + TypePatternList.ANY, + ThrowsPattern.ANY, + AnnotationTypePattern.ANY)); + } else if (kind.equals("handler")) { + eat("("); + TypePattern typePat = parseTypePattern(false,false); + eat(")"); + p = new HandlerPointcut(typePat); + } else if (kind.equals("lock") || kind.equals("unlock")) { + p = parseMonitorPointcut(kind); + } else if (kind.equals("initialization")) { + eat("("); + SignaturePattern sig = parseConstructorSignaturePattern(); + eat(")"); + p = new KindedPointcut(Shadow.Initialization, sig); + } else if (kind.equals("staticinitialization")) { + eat("("); + TypePattern typePat = parseTypePattern(false,false); + eat(")"); + p = new KindedPointcut(Shadow.StaticInitialization, + new SignaturePattern(Member.STATIC_INITIALIZATION, ModifiersPattern.ANY, + TypePattern.ANY, typePat, NamePattern.ANY, TypePatternList.EMPTY, + ThrowsPattern.ANY,AnnotationTypePattern.ANY)); + } else if (kind.equals("preinitialization")) { + eat("("); + SignaturePattern sig = parseConstructorSignaturePattern(); + eat(")"); + p = new KindedPointcut(Shadow.PreInitialization, sig); + } else if (kind.equals("if")) { + // @style support allows if(), if(true), if(false) + eat("("); + if (maybeEatIdentifier("true")) { + eat(")"); + p = new IfPointcut.IfTruePointcut(); + } else if (maybeEatIdentifier("false")) { + eat(")"); + p = new IfPointcut.IfFalsePointcut(); + } else { + eat(")"); + // TODO - Alex has some token stuff going on here to get a readable name in place of ""... + p = new IfPointcut(""); + } + } + else { + boolean matchedByExtensionDesignator = false; + // see if a registered handler wants to parse it, otherwise + // treat as a reference pointcut + for (Iterator iter = this.pointcutDesignatorHandlers.iterator(); iter.hasNext();) { + PointcutDesignatorHandler pcd = (PointcutDesignatorHandler) iter.next(); + if (pcd.getDesignatorName().equals(kind)) { + p = parseDesignatorPointcut(pcd); + matchedByExtensionDesignator = true; + } + + } + if (!matchedByExtensionDesignator) { + tokenSource.setIndex(start); + p = parseReferencePointcut(); + } + } + return p; + } + + private void assertNoTypeVariables(String[] tvs, String errorMessage,IToken token) { + if ( tvs != null ) throw new ParserException(errorMessage,token); + } + + public Pointcut parseAnnotationPointcut() { + int start = tokenSource.getIndex(); + IToken t = tokenSource.peek(); + String kind = parseIdentifier(); + IToken possibleTypeVariableToken = tokenSource.peek(); + String[] typeVariables = maybeParseSimpleTypeVariableList(); + if (typeVariables != null) { + String message = "("; + assertNoTypeVariables(typeVariables, message, possibleTypeVariableToken); + } + tokenSource.setIndex(start); + if (kind.equals("annotation")) { + return parseAtAnnotationPointcut(); + } else if (kind.equals("args")) { + return parseArgsAnnotationPointcut(); + } else if (kind.equals("this") || kind.equals("target")) { + return parseThisOrTargetAnnotationPointcut(); + } else if (kind.equals("within")) { + return parseWithinAnnotationPointcut(); + } else if (kind.equals("withincode")) { + return parseWithinCodeAnnotationPointcut(); + } throw new ParserException("pointcut name", t); + } + + private Pointcut parseAtAnnotationPointcut() { + parseIdentifier(); + eat("("); + if (maybeEat(")")) { + throw new ParserException("@AnnotationName or parameter", tokenSource.peek()); + } + ExactAnnotationTypePattern type = parseAnnotationNameOrVarTypePattern(); + eat(")"); + return new AnnotationPointcut(type); + } + + + private SignaturePattern parseConstructorSignaturePattern() { + SignaturePattern ret = parseMethodOrConstructorSignaturePattern(); + if (ret.getKind() == Member.CONSTRUCTOR) return ret; + + throw new ParserException("constructor pattern required, found method pattern", + ret); + } + + + private Pointcut parseWithinCodePointcut() { + //parseIdentifier(); + eat("("); + SignaturePattern sig = parseMethodOrConstructorSignaturePattern(); + eat(")"); + return new WithincodePointcut(sig); + } + + private Pointcut parseCflowPointcut(boolean isBelow) { + //parseIdentifier(); + eat("("); + Pointcut entry = parsePointcut(); + eat(")"); + return new CflowPointcut(entry, isBelow, null); + } + + /** + * Method parseWithinPointcut. + * @return Pointcut + */ + private Pointcut parseWithinPointcut() { + //parseIdentifier(); + eat("("); + TypePattern type = parseTypePattern(); + eat(")"); + return new WithinPointcut(type); + } + + + /** + * Method parseThisOrTargetPointcut. + * @return Pointcut + */ + private Pointcut parseThisOrTargetPointcut(String kind) { + eat("("); + TypePattern type = parseTypePattern(); + eat(")"); + return new ThisOrTargetPointcut(kind.equals("this"), type); + } + + private Pointcut parseThisOrTargetAnnotationPointcut() { + String kind = parseIdentifier(); + eat("("); + if (maybeEat(")")) { + throw new ParserException("expecting @AnnotationName or parameter, but found ')'", tokenSource.peek()); + } + ExactAnnotationTypePattern type = parseAnnotationNameOrVarTypePattern(); + eat(")"); + return new ThisOrTargetAnnotationPointcut(kind.equals("this"),type); + } + + private Pointcut parseWithinAnnotationPointcut() { + /* String kind = */parseIdentifier(); + eat("("); + if (maybeEat(")")) { + throw new ParserException("expecting @AnnotationName or parameter, but found ')'", tokenSource.peek()); + } + AnnotationTypePattern type = parseAnnotationNameOrVarTypePattern(); + eat(")"); + return new WithinAnnotationPointcut(type); + } + + private Pointcut parseWithinCodeAnnotationPointcut() { + /*String kind = */parseIdentifier(); + eat("("); + if (maybeEat(")")) { + throw new ParserException("expecting @AnnotationName or parameter, but found ')'", tokenSource.peek()); + } + ExactAnnotationTypePattern type = parseAnnotationNameOrVarTypePattern(); + eat(")"); + return new WithinCodeAnnotationPointcut(type); + } + + /** + * Method parseArgsPointcut. + * @return Pointcut + */ + private Pointcut parseArgsPointcut() { + //parseIdentifier(); + TypePatternList arguments = parseArgumentsPattern(false); + return new ArgsPointcut(arguments); + } + + private Pointcut parseArgsAnnotationPointcut() { + parseIdentifier(); + AnnotationPatternList arguments = parseArgumentsAnnotationPattern(); + return new ArgsAnnotationPointcut(arguments); + } + + private Pointcut parseReferencePointcut() { + TypePattern onType = parseTypePattern(); + NamePattern name = null; + if (onType.typeParameters.size() > 0) { + eat("."); + name = parseNamePattern(); + } else { + name = tryToExtractName(onType); + } + if (name == null) { + throw new ParserException("name pattern", tokenSource.peek()); + } + if (onType.toString().equals("")) { + onType = null; + } + + String simpleName = name.maybeGetSimpleName(); + if (simpleName == null) { + throw new ParserException("(",tokenSource.peek(-1)); + } + + TypePatternList arguments = parseArgumentsPattern(false); + return new ReferencePointcut(onType, simpleName, arguments); + } + + private Pointcut parseDesignatorPointcut(PointcutDesignatorHandler pcdHandler) { + eat("("); + int parenCount = 1; + StringBuffer pointcutBody = new StringBuffer(); + while (parenCount > 0) { + if (maybeEat("(")) { + parenCount++; + pointcutBody.append("("); + } else if (maybeEat(")")) { + parenCount--; + if (parenCount > 0) { + pointcutBody.append(")"); + } + } else { + pointcutBody.append(nextToken().getString()); + } + } + ContextBasedMatcher pcExpr = pcdHandler.parse(pointcutBody.toString()); + return new PointcutDesignatorHandlerBasedPointcut(pcExpr,world); + } + + + public List parseDottedIdentifier() { + List ret = new ArrayList(); + ret.add(parseIdentifier()); + while (maybeEat(".")) { + ret.add(parseIdentifier()); + } + return ret; + } + + + + private KindedPointcut parseKindedPointcut(String kind) { + eat("("); + SignaturePattern sig; + + Shadow.Kind shadowKind = null; + if (kind.equals("execution")) { + sig = parseMethodOrConstructorSignaturePattern(); + if (sig.getKind() == Member.METHOD) { + shadowKind = Shadow.MethodExecution; + } else if (sig.getKind() == Member.CONSTRUCTOR) { + shadowKind = Shadow.ConstructorExecution; + } + } else if (kind.equals("call")) { + sig = parseMethodOrConstructorSignaturePattern(); + if (sig.getKind() == Member.METHOD) { + shadowKind = Shadow.MethodCall; + } else if (sig.getKind() == Member.CONSTRUCTOR) { + shadowKind = Shadow.ConstructorCall; + } + } else if (kind.equals("get")) { + sig = parseFieldSignaturePattern(); + shadowKind = Shadow.FieldGet; + } else if (kind.equals("set")) { + sig = parseFieldSignaturePattern(); + shadowKind = Shadow.FieldSet; + } else { + throw new ParserException("bad kind: " + kind, tokenSource.peek()); + } + eat(")"); + return new KindedPointcut(shadowKind, sig); + } + + /** Covers the 'lock()' and 'unlock()' pointcuts */ + private KindedPointcut parseMonitorPointcut(String kind) { + eat("("); +// TypePattern type = TypePattern.ANY; + eat(")"); + + if (kind.equals("lock")) { + return new KindedPointcut(Shadow.SynchronizationLock, + new SignaturePattern(Member.MONITORENTER, ModifiersPattern.ANY, + TypePattern.ANY, + TypePattern.ANY, +// type, + NamePattern.ANY, + TypePatternList.ANY, + ThrowsPattern.ANY, + AnnotationTypePattern.ANY)); + } else { + return new KindedPointcut(Shadow.SynchronizationUnlock, + new SignaturePattern(Member.MONITORENTER, ModifiersPattern.ANY, + TypePattern.ANY, + TypePattern.ANY, +// type, + NamePattern.ANY, + TypePatternList.ANY, + ThrowsPattern.ANY, + AnnotationTypePattern.ANY)); + } + } + + public TypePattern parseTypePattern() { + return parseTypePattern(false,false); + } + + public TypePattern parseTypePattern(boolean insideTypeParameters,boolean parameterAnnotationsPossible) { + TypePattern p = parseAtomicTypePattern(insideTypeParameters,parameterAnnotationsPossible); + if (maybeEat("&&")) { + p = new AndTypePattern(p, parseNotOrTypePattern(insideTypeParameters,parameterAnnotationsPossible)); + } + + if (maybeEat("||")) { + p = new OrTypePattern(p, parseTypePattern(insideTypeParameters,parameterAnnotationsPossible)); + } + return p; + } + + private TypePattern parseNotOrTypePattern(boolean insideTypeParameters,boolean parameterAnnotationsPossible) { + TypePattern p = parseAtomicTypePattern(insideTypeParameters,parameterAnnotationsPossible); + if (maybeEat("&&")) { + p = new AndTypePattern(p, parseTypePattern(insideTypeParameters,parameterAnnotationsPossible)); + } + return p; + } + + + // Need to differentiate in here between two kinds of annotation pattern - depending on where the ( is + + private TypePattern parseAtomicTypePattern(boolean insideTypeParameters,boolean parameterAnnotationsPossible) { + AnnotationTypePattern ap = maybeParseAnnotationPattern(); // might be parameter annotation pattern or type annotation pattern + if (maybeEat("!")) { + //int startPos = tokenSource.peek(-1).getStart(); + //??? we lose source location for true start of !type + + // An annotation, if processed, is outside of the Not - so here we have to build + // an And pattern containing the annotation and the not as left and right children + // *unless* the annotation pattern was just 'Any' then we can skip building the + // And and just return the Not directly (pr228980) + TypePattern p = null; + TypePattern tp = parseAtomicTypePattern(insideTypeParameters,parameterAnnotationsPossible); + if (!(ap instanceof AnyAnnotationTypePattern)) { + p = new NotTypePattern(tp); + p = new AndTypePattern(setAnnotationPatternForTypePattern(TypePattern.ANY,ap,false),p); + } else { + p = new NotTypePattern(tp); + } + return p; + } + if (maybeEat("(")) { + TypePattern p = parseTypePattern(insideTypeParameters,false); + if ((p instanceof NotTypePattern) && !(ap instanceof AnyAnnotationTypePattern)) { + // dont set the annotation on it, we don't want the annotation to be + // considered as part of the not, it is outside the not (pr228980) + TypePattern tp = setAnnotationPatternForTypePattern(TypePattern.ANY, ap, parameterAnnotationsPossible); + p = new AndTypePattern(tp,p); + } else { + p = setAnnotationPatternForTypePattern(p,ap,parameterAnnotationsPossible); + } + eat(")"); + boolean isVarArgs = maybeEat("..."); + if (isVarArgs) p.setIsVarArgs(isVarArgs); + boolean isIncludeSubtypes = maybeEat("+"); + if (isIncludeSubtypes) p.includeSubtypes = true; // need the test because (A+) should not set subtypes to false! + return p; + } + int startPos = tokenSource.peek().getStart(); + TypePattern p = parseSingleTypePattern(insideTypeParameters); + int endPos = tokenSource.peek(-1).getEnd(); + p = setAnnotationPatternForTypePattern(p,ap,false); + p.setLocation(sourceContext, startPos, endPos); + return p; + } + + + private TypePattern setAnnotationPatternForTypePattern(TypePattern t, AnnotationTypePattern ap,boolean parameterAnnotationsPattern) { + TypePattern ret = t; + if (parameterAnnotationsPattern) ap.setForParameterAnnotationMatch(); + if (ap != AnnotationTypePattern.ANY) { + if (t == TypePattern.ANY) { + ret = new WildTypePattern(new NamePattern[] {NamePattern.ANY},false,0,false,null); + } + if (t.annotationPattern == AnnotationTypePattern.ANY) { + ret.setAnnotationTypePattern(ap); + } else { + ret.setAnnotationTypePattern(new AndAnnotationTypePattern(ap,t.annotationPattern)); //??? + } + } + return ret; + } + + public AnnotationTypePattern maybeParseAnnotationPattern() { + AnnotationTypePattern ret = AnnotationTypePattern.ANY; + AnnotationTypePattern nextPattern = null; + while ((nextPattern = maybeParseSingleAnnotationPattern()) != null) { + if (ret == AnnotationTypePattern.ANY) { + ret = nextPattern; + } else { + ret = new AndAnnotationTypePattern(ret,nextPattern); + } + } + return ret; + } + + // PVAL cope with annotation values at other places in this code + public AnnotationTypePattern maybeParseSingleAnnotationPattern() { + AnnotationTypePattern ret = null; + Map values = null; + // LALR(2) - fix by making "!@" a single token + int startIndex = tokenSource.getIndex(); + if (maybeEat("!")) { + if (maybeEat("@")) { + if (maybeEat("(")) { + TypePattern p = parseTypePattern(); + ret = new NotAnnotationTypePattern(new WildAnnotationTypePattern(p)); + eat(")"); + return ret; + } else { + TypePattern p = parseSingleTypePattern(); + if (maybeEatAdjacent("(")) { + values = parseAnnotationValues(); + eat(")"); + ret = new NotAnnotationTypePattern(new WildAnnotationTypePattern(p,values)); + } else { + ret = new NotAnnotationTypePattern(new WildAnnotationTypePattern(p)); + } + return ret; + } + } else { + tokenSource.setIndex(startIndex); // not for us! + return ret; + } + } + if (maybeEat("@")) { + if (maybeEat("(")) { + TypePattern p = parseTypePattern(); + ret = new WildAnnotationTypePattern(p); + eat(")"); + return ret; + } else { + TypePattern p = parseSingleTypePattern(); + if (maybeEatAdjacent("(")) { + values = parseAnnotationValues(); + eat(")"); + ret = new WildAnnotationTypePattern(p,values); + } else { + ret = new WildAnnotationTypePattern(p); + } + return ret; + } + } else { + tokenSource.setIndex(startIndex); // not for us! + return ret; + } + } + + // Parse annotation values. In an expression in @A(a=b,c=d) this method will be + // parsing the a=b,c=d.) + public Map/*String,String*/ parseAnnotationValues() { + Map values = new HashMap(); + boolean seenDefaultValue = false; + do { + String possibleKeyString = parseAnnotationNameValuePattern(); + if (possibleKeyString==null) { + throw new ParserException("expecting simple literal ",tokenSource.peek(-1)); + } + // did they specify just a single entry 'v' or a keyvalue pair 'k=v' + if (maybeEat("=")) { + // it was a key! + String valueString = parseAnnotationNameValuePattern(); + if (valueString==null) { + throw new ParserException("expecting simple literal ",tokenSource.peek(-1)); + } + values.put(possibleKeyString,valueString); + } else { + if (seenDefaultValue) { + throw new ParserException("cannot specify two default values",tokenSource.peek(-1)); + } + seenDefaultValue = true; + values.put("value",possibleKeyString); + } + } while (maybeEat(",")); // keep going whilst there are ',' + return values; + } + + public TypePattern parseSingleTypePattern() { + return parseSingleTypePattern(false); + } + + public TypePattern parseSingleTypePattern(boolean insideTypeParameters) { + if (insideTypeParameters && maybeEat("?")) return parseGenericsWildcardTypePattern(); + if (allowHasTypePatterns) { + if (maybeEatIdentifier("hasmethod")) return parseHasMethodTypePattern(); + if (maybeEatIdentifier("hasfield")) return parseHasFieldTypePattern(); + } + + List names = parseDottedNamePattern(); + + int dim = 0; + while (maybeEat("[")) { + eat("]"); + dim++; + } + + TypePatternList typeParameters = maybeParseTypeParameterList(); + int endPos = tokenSource.peek(-1).getEnd(); + + boolean isVarArgs = maybeEat("..."); + + boolean includeSubtypes = maybeEat("+"); + + //??? what about the source location of any's???? + if (names.size() == 1 && ((NamePattern)names.get(0)).isAny() && + dim == 0 && !isVarArgs && typeParameters == null ) return TypePattern.ANY; + + // Notice we increase the dimensions if varargs is set. this is to allow type matching to + // succeed later: The actual signature at runtime of a method declared varargs is an array type of + // the original declared type (so Integer... becomes Integer[] in the bytecode). So, here for the + // pattern 'Integer...' we create a WildTypePattern 'Integer[]' with varargs set. If this matches + // during shadow matching, we confirm that the varargs flags match up before calling it a successful + // match. + return new WildTypePattern(names, includeSubtypes, dim+(isVarArgs?1:0), endPos,isVarArgs,typeParameters); + } + + public TypePattern parseHasMethodTypePattern() { + int startPos = tokenSource.peek(-1).getStart(); + eat("("); + SignaturePattern sp = parseMethodOrConstructorSignaturePattern(); + eat(")"); + int endPos = tokenSource.peek(-1).getEnd(); + HasMemberTypePattern ret = new HasMemberTypePattern(sp); + ret.setLocation(sourceContext, startPos, endPos); + return ret; + } + + public TypePattern parseHasFieldTypePattern() { + int startPos = tokenSource.peek(-1).getStart(); + eat("("); + SignaturePattern sp = parseFieldSignaturePattern(); + eat(")"); + int endPos = tokenSource.peek(-1).getEnd(); + HasMemberTypePattern ret = new HasMemberTypePattern(sp); + ret.setLocation(sourceContext, startPos, endPos); + return ret; + } + + public TypePattern parseGenericsWildcardTypePattern() { + List names = new ArrayList(); + names.add(new NamePattern("?")); + TypePattern upperBound = null; + TypePattern[] additionalInterfaceBounds = new TypePattern[0]; + TypePattern lowerBound = null; + if (maybeEatIdentifier("extends")) { + upperBound = parseTypePattern(false,false); + additionalInterfaceBounds = maybeParseAdditionalInterfaceBounds(); + } + if (maybeEatIdentifier("super")) { + lowerBound = parseTypePattern(false,false); + } + int endPos = tokenSource.peek(-1).getEnd(); + return new WildTypePattern(names,false,0,endPos,false,null,upperBound,additionalInterfaceBounds,lowerBound); + } + +// private AnnotationTypePattern completeAnnotationPattern(AnnotationTypePattern p) { +// if (maybeEat("&&")) { +// return new AndAnnotationTypePattern(p,parseNotOrAnnotationPattern()); +// } +// if (maybeEat("||")) { +// return new OrAnnotationTypePattern(p,parseAnnotationTypePattern()); +// } +// return p; +// } +// +// protected AnnotationTypePattern parseAnnotationTypePattern() { +// AnnotationTypePattern ap = parseAtomicAnnotationPattern(); +// if (maybeEat("&&")) { +// ap = new AndAnnotationTypePattern(ap, parseNotOrAnnotationPattern()); +// } +// +// if (maybeEat("||")) { +// ap = new OrAnnotationTypePattern(ap, parseAnnotationTypePattern()); +// } +// return ap; +// } +// +// private AnnotationTypePattern parseNotOrAnnotationPattern() { +// AnnotationTypePattern p = parseAtomicAnnotationPattern(); +// if (maybeEat("&&")) { +// p = new AndAnnotationTypePattern(p,parseAnnotationTypePattern()); +// } +// return p; +// } + + + protected ExactAnnotationTypePattern parseAnnotationNameOrVarTypePattern() { + ExactAnnotationTypePattern p = null; + int startPos = tokenSource.peek().getStart(); + if (maybeEat("@")) { + throw new ParserException("@Foo form was deprecated in AspectJ 5 M2: annotation name or var ",tokenSource.peek(-1)); + } + p = parseSimpleAnnotationName(); + int endPos = tokenSource.peek(-1).getEnd(); + p.setLocation(sourceContext,startPos,endPos); + // For optimized syntax that allows binding directly to annotation values (pr234943) + if (maybeEat("(")) { + String formalName = parseIdentifier(); + p = new ExactAnnotationFieldTypePattern(p, formalName); + eat(")"); + } + return p; + } + + /** + * @return + */ + private ExactAnnotationTypePattern parseSimpleAnnotationName() { + // the @ has already been eaten... + ExactAnnotationTypePattern p; + StringBuffer annotationName = new StringBuffer(); + annotationName.append(parseIdentifier()); + while (maybeEat(".")) { + annotationName.append('.'); + annotationName.append(parseIdentifier()); + } + UnresolvedType type = UnresolvedType.forName(annotationName.toString()); + p = new ExactAnnotationTypePattern(type,null); + return p; + } + +// private AnnotationTypePattern parseAtomicAnnotationPattern() { +// if (maybeEat("!")) { +// //int startPos = tokenSource.peek(-1).getStart(); +// //??? we lose source location for true start of !type +// AnnotationTypePattern p = new NotAnnotationTypePattern(parseAtomicAnnotationPattern()); +// return p; +// } +// if (maybeEat("(")) { +// AnnotationTypePattern p = parseAnnotationTypePattern(); +// eat(")"); +// return p; +// } +// int startPos = tokenSource.peek().getStart(); +// eat("@"); +// StringBuffer annotationName = new StringBuffer(); +// annotationName.append(parseIdentifier()); +// while (maybeEat(".")) { +// annotationName.append('.'); +// annotationName.append(parseIdentifier()); +// } +// UnresolvedType type = UnresolvedType.forName(annotationName.toString()); +// AnnotationTypePattern p = new ExactAnnotationTypePattern(type); +// int endPos = tokenSource.peek(-1).getEnd(); +// p.setLocation(sourceContext, startPos, endPos); +// return p; +// } + + + public List parseDottedNamePattern() { + List names = new ArrayList(); + StringBuffer buf = new StringBuffer(); + IToken previous = null; + boolean justProcessedEllipsis = false; // Remember if we just dealt with an ellipsis (PR61536) + boolean justProcessedDot = false; + boolean onADot = false; + + while (true) { + IToken tok = null; + int startPos = tokenSource.peek().getStart(); + String afterDot = null; + while (true) { + if (previous !=null && previous.getString().equals(".")) justProcessedDot = true; + tok = tokenSource.peek(); + onADot = (tok.getString().equals(".")); + if (previous != null) { + if (!isAdjacent(previous, tok)) break; + } + if (tok.getString() == "*" || (tok.isIdentifier() && tok.getString()!="...")) { + buf.append(tok.getString()); + } else if (tok.getString()=="...") { + break; + } else if (tok.getLiteralKind() != null) { + //System.err.println("literal kind: " + tok.getString()); + String s = tok.getString(); + int dot = s.indexOf('.'); + if (dot != -1) { + buf.append(s.substring(0, dot)); + afterDot = s.substring(dot+1); + previous = tokenSource.next(); + break; + } + buf.append(s); // ??? so-so + } else { + break; + } + previous = tokenSource.next(); + //XXX need to handle floats and other fun stuff + } + int endPos = tokenSource.peek(-1).getEnd(); + if (buf.length() == 0 && names.isEmpty()) { + throw new ParserException("name pattern", tok); + } + + if (buf.length() == 0 && justProcessedEllipsis) { + throw new ParserException("name pattern cannot finish with ..", tok); + } + if (buf.length() == 0 && justProcessedDot && !onADot) { + throw new ParserException("name pattern cannot finish with .", tok); + } + + if (buf.length() == 0) { + names.add(NamePattern.ELLIPSIS); + justProcessedEllipsis = true; + } else { + checkLegalName(buf.toString(), previous); + NamePattern ret = new NamePattern(buf.toString()); + ret.setLocation(sourceContext, startPos, endPos); + names.add(ret); + justProcessedEllipsis = false; + } + + if (afterDot == null) { + buf.setLength(0); + // no elipsis or dotted name part + if (!maybeEat(".")) break; + // go on + else previous = tokenSource.peek(-1); + } else { + buf.setLength(0); + buf.append(afterDot); + afterDot = null; + } + } + //System.err.println("parsed: " + names); + return names; + } + + // supported form 'a.b.c.d' or just 'a' + public String parseAnnotationNameValuePattern() { + StringBuffer buf = new StringBuffer(); + IToken tok; +// int startPos = + tokenSource.peek().getStart(); + boolean dotOK = false; + int depth = 0; + while (true) { + tok = tokenSource.peek(); + // keep going until we hit ')' or '=' or ',' + if (tok.getString()==")" && depth==0) break; + if (tok.getString()=="=" && depth==0) break; + if (tok.getString()=="," && depth==0) break; + + // keep track of nested brackets + if (tok.getString()=="(") depth++; + if (tok.getString()==")") depth--; + if (tok.getString()=="{") depth++; + if (tok.getString()=="}") depth--; + + if (tok.getString()=="." && !dotOK) { + throw new ParserException("dot not expected",tok); + } + buf.append(tok.getString()); + tokenSource.next(); + dotOK=true; + } + if (buf.length()==0) return null; + else return buf.toString(); + } + + + public NamePattern parseNamePattern() { + StringBuffer buf = new StringBuffer(); + IToken previous = null; + IToken tok; + int startPos = tokenSource.peek().getStart(); + while (true) { + tok = tokenSource.peek(); + if (previous != null) { + if (!isAdjacent(previous, tok)) break; + } + if (tok.getString() == "*" || tok.isIdentifier()) { + buf.append(tok.getString()); + } else if (tok.getLiteralKind() != null) { + //System.err.println("literal kind: " + tok.getString()); + String s = tok.getString(); + if (s.indexOf('.') != -1) break; + buf.append(s); // ??? so-so + } else { + break; + } + previous = tokenSource.next(); + //XXX need to handle floats and other fun stuff + } + int endPos = tokenSource.peek(-1).getEnd(); + if (buf.length() == 0) { + throw new ParserException("name pattern", tok); + } + + checkLegalName(buf.toString(), previous); + NamePattern ret = new NamePattern(buf.toString()); + ret.setLocation(sourceContext, startPos, endPos); + return ret; + } + + private void checkLegalName(String s, IToken tok) { + char ch = s.charAt(0); + if (!(ch == '*' || Character.isJavaIdentifierStart(ch))) { + throw new ParserException("illegal identifier start (" + ch + ")", tok); + } + + for (int i=1, len=s.length(); i < len; i++) { + ch = s.charAt(i); + if (!(ch == '*' || Character.isJavaIdentifierPart(ch))) { + throw new ParserException("illegal identifier character (" + ch + ")", tok); + } + } + + } + + + private boolean isAdjacent(IToken first, IToken second) { + return first.getEnd() == second.getStart()-1; + } + + + public ModifiersPattern parseModifiersPattern() { + int requiredFlags = 0; + int forbiddenFlags = 0; + int start; + while (true) { + start = tokenSource.getIndex(); + boolean isForbidden = false; + isForbidden = maybeEat("!"); + IToken t = tokenSource.next(); + int flag = ModifiersPattern.getModifierFlag(t.getString()); + if (flag == -1) break; + if (isForbidden) forbiddenFlags |= flag; + else requiredFlags |= flag; + } + + tokenSource.setIndex(start); + if (requiredFlags == 0 && forbiddenFlags == 0) { + return ModifiersPattern.ANY; + } else { + return new ModifiersPattern(requiredFlags, forbiddenFlags); + } + } + + public TypePatternList parseArgumentsPattern(boolean parameterAnnotationsPossible) { + List patterns = new ArrayList(); + eat("("); + + // () + if (maybeEat(")")) { + return new TypePatternList(); + } + + do { + if (maybeEat(".")) { // .. + eat("."); + patterns.add(TypePattern.ELLIPSIS); + } else { + patterns.add(parseTypePattern(false,parameterAnnotationsPossible)); + } + } while (maybeEat(",")); + eat(")"); + return new TypePatternList(patterns); + } + + public AnnotationPatternList parseArgumentsAnnotationPattern() { + List patterns = new ArrayList(); + eat("("); + if (maybeEat(")")) { + return new AnnotationPatternList(); + } + + do { + if (maybeEat(".")) { + eat("."); + patterns.add(AnnotationTypePattern.ELLIPSIS); + } else if (maybeEat("*")) { + patterns.add(AnnotationTypePattern.ANY); + } else { + patterns.add(parseAnnotationNameOrVarTypePattern()); + } + } while (maybeEat(",")); + eat(")"); + return new AnnotationPatternList(patterns); + } + + + public ThrowsPattern parseOptionalThrowsPattern() { + IToken t = tokenSource.peek(); + if (t.isIdentifier() && t.getString().equals("throws")) { + tokenSource.next(); + List required = new ArrayList(); + List forbidden = new ArrayList(); + do { + boolean isForbidden = maybeEat("!"); + //???might want an error for a second ! without a paren + TypePattern p = parseTypePattern(); + if (isForbidden) forbidden.add(p); + else required.add(p); + } while (maybeEat(",")); + return new ThrowsPattern(new TypePatternList(required), new TypePatternList(forbidden)); + } + return ThrowsPattern.ANY; + } + + + public SignaturePattern parseMethodOrConstructorSignaturePattern() { + int startPos = tokenSource.peek().getStart(); + AnnotationTypePattern annotationPattern = maybeParseAnnotationPattern(); + ModifiersPattern modifiers = parseModifiersPattern(); + TypePattern returnType = parseTypePattern(false,false); + + TypePattern declaringType; + NamePattern name = null; + MemberKind kind; + // here we can check for 'new' + if (maybeEatNew(returnType)) { + kind = Member.CONSTRUCTOR; + if (returnType.toString().length() == 0) { + declaringType = TypePattern.ANY; + } else { + declaringType = returnType; + } + returnType = TypePattern.ANY; + name = NamePattern.ANY; + } else { + kind = Member.METHOD; + IToken nameToken = tokenSource.peek(); + declaringType = parseTypePattern(false,false); + if (maybeEat(".")) { + nameToken = tokenSource.peek(); + name = parseNamePattern(); + } else { + name = tryToExtractName(declaringType); + if (declaringType.toString().equals("")) { + declaringType = TypePattern.ANY; + } + } + if (name == null) { + throw new ParserException("name pattern", tokenSource.peek()); + } + String simpleName = name.maybeGetSimpleName(); + //XXX should add check for any Java keywords + if (simpleName != null && simpleName.equals("new")) { + throw new ParserException("method name (not constructor)", + nameToken); + } + } + + TypePatternList parameterTypes = parseArgumentsPattern(true); + + ThrowsPattern throwsPattern = parseOptionalThrowsPattern(); + SignaturePattern ret = new SignaturePattern(kind, modifiers, returnType, declaringType, name, parameterTypes, throwsPattern, annotationPattern); + int endPos = tokenSource.peek(-1).getEnd(); + ret.setLocation(sourceContext, startPos, endPos); + return ret; + } + + private boolean maybeEatNew(TypePattern returnType) { + if (returnType instanceof WildTypePattern) { + WildTypePattern p = (WildTypePattern)returnType; + if (p.maybeExtractName("new")) return true; + } + int start = tokenSource.getIndex(); + if (maybeEat(".")) { + String id = maybeEatIdentifier(); + if (id != null && id.equals("new")) return true; + tokenSource.setIndex(start); + } + + return false; + } + + + public SignaturePattern parseFieldSignaturePattern() { + int startPos = tokenSource.peek().getStart(); + + // TypePatternList followMe = TypePatternList.ANY; + + AnnotationTypePattern annotationPattern = maybeParseAnnotationPattern(); + ModifiersPattern modifiers = parseModifiersPattern(); + TypePattern returnType = parseTypePattern(); + TypePattern declaringType = parseTypePattern(); + NamePattern name; + //System.err.println("parsed field: " + declaringType.toString()); + if (maybeEat(".")) { + name = parseNamePattern(); + } else { + name = tryToExtractName(declaringType); + if (name == null) throw new ParserException("name pattern",tokenSource.peek()); + if (declaringType.toString().equals("")) { + declaringType = TypePattern.ANY; + } + } + SignaturePattern ret = new SignaturePattern(Member.FIELD, modifiers, returnType, + declaringType, name, TypePatternList.ANY, ThrowsPattern.ANY,annotationPattern); + + int endPos = tokenSource.peek(-1).getEnd(); + ret.setLocation(sourceContext, startPos, endPos); + return ret; + } + + + private NamePattern tryToExtractName(TypePattern nextType) { + if (nextType == TypePattern.ANY) { + return NamePattern.ANY; + } else if (nextType instanceof WildTypePattern) { + WildTypePattern p = (WildTypePattern)nextType; + return p.extractName(); + } else { + return null; + } + } + + /** + * Parse type variable declarations for a generic method or at the start of a signature pointcut to identify + * type variable names in a generic type. + * @param includeParameterizedTypes + * @return + */ + public TypeVariablePatternList maybeParseTypeVariableList() { + if (!maybeEat("<")) return null; + List typeVars = new ArrayList(); + TypeVariablePattern t = parseTypeVariable(); + typeVars.add(t); + while (maybeEat(",")) { + TypeVariablePattern nextT = parseTypeVariable(); + typeVars.add(nextT); + } + eat(">"); + TypeVariablePattern[] tvs = new TypeVariablePattern[typeVars.size()]; + typeVars.toArray(tvs); + return new TypeVariablePatternList(tvs); + } + + // of the form execution - allows identifiers only + public String[] maybeParseSimpleTypeVariableList() { + if (!maybeEat("<")) return null; + List typeVarNames = new ArrayList(); + do { + typeVarNames.add(parseIdentifier()); + } while (maybeEat(",")); + eat(">","',' or '>'"); + String[] tvs = new String[typeVarNames.size()]; + typeVarNames.toArray(tvs); + return tvs; + } + + public TypePatternList maybeParseTypeParameterList() { + if (!maybeEat("<")) return null; + List typePats = new ArrayList(); + do { + TypePattern tp = parseTypePattern(true,false); + typePats.add(tp); + } while(maybeEat(",")); + eat(">"); + TypePattern[] tps = new TypePattern[typePats.size()]; + typePats.toArray(tps); + return new TypePatternList(tps); + } + + public TypeVariablePattern parseTypeVariable() { + TypePattern upperBound = null; + TypePattern[] additionalInterfaceBounds = null; + TypePattern lowerBound = null; + String typeVariableName = parseIdentifier(); + if (maybeEatIdentifier("extends")) { + upperBound = parseTypePattern(); + additionalInterfaceBounds = maybeParseAdditionalInterfaceBounds(); + } else if (maybeEatIdentifier("super")) { + lowerBound = parseTypePattern(); + } + return new TypeVariablePattern(typeVariableName,upperBound,additionalInterfaceBounds,lowerBound); + } + + private TypePattern[] maybeParseAdditionalInterfaceBounds() { + List boundsList = new ArrayList(); + while (maybeEat("&")) { + TypePattern tp = parseTypePattern(); + boundsList.add(tp); + } + if (boundsList.size() == 0) return null; + TypePattern[] ret = new TypePattern[boundsList.size()]; + boundsList.toArray(ret); + return ret; + } + + public String parsePossibleStringSequence(boolean shouldEnd) { + StringBuffer result = new StringBuffer(); + + IToken token = tokenSource.next(); + if (token.getLiteralKind()==null) { + throw new ParserException("string",token); + } + while (token.getLiteralKind().equals("string")) { + result.append(token.getString()); + boolean plus = maybeEat("+"); + if (!plus) break; + token = tokenSource.next(); + if (token.getLiteralKind()==null) { + throw new ParserException("string",token); + } + } + eatIdentifier(";"); + IToken t = tokenSource.next(); + if (shouldEnd && t!=IToken.EOF) { + throw new ParserException(";",token); + } + // bug 125027: since we've eaten the ";" we need to set the index + // to be one less otherwise the end position isn't set correctly. + int currentIndex = tokenSource.getIndex(); + tokenSource.setIndex(currentIndex-1); + + return result.toString(); + + } + + public String parseStringLiteral() { + IToken token = tokenSource.next(); + String literalKind = token.getLiteralKind(); + if (literalKind == "string") { + return token.getString(); + } + + throw new ParserException("string", token); + } + + public String parseIdentifier() { + IToken token = tokenSource.next(); + if (token.isIdentifier()) return token.getString(); + throw new ParserException("identifier", token); + } + + public void eatIdentifier(String expectedValue) { + IToken next = tokenSource.next(); + if (!next.getString().equals(expectedValue)) { + throw new ParserException(expectedValue, next); + } + } + + public boolean maybeEatIdentifier(String expectedValue) { + IToken next = tokenSource.peek(); + if (next.getString().equals(expectedValue)) { + tokenSource.next(); + return true; + } else { + return false; + } + } + + public void eat(String expectedValue) { + eat(expectedValue,expectedValue); + } + + private void eat(String expectedValue,String expectedMessage) { + IToken next = nextToken(); + if (next.getString() != expectedValue) { + if (expectedValue.equals(">") && next.getString().startsWith(">")) { + // handle problem of >> and >>> being lexed as single tokens + pendingRightArrows = BasicToken.makeLiteral(next.getString().substring(1).intern(), "string", next.getStart()+1, next.getEnd()); + return; + } + throw new ParserException(expectedMessage, next); + } + } + + private IToken pendingRightArrows; + private IToken nextToken() { + if (pendingRightArrows != null) { + IToken ret = pendingRightArrows; + pendingRightArrows = null; + return ret; + } else { + return tokenSource.next(); + } + } + + public boolean maybeEatAdjacent(String token) { + IToken next = tokenSource.peek(); + if (next.getString() == token) { + if (isAdjacent(tokenSource.peek(-1),next)) { + tokenSource.next(); + return true; + } + } + return false; + } + + public boolean maybeEat(String token) { + IToken next = tokenSource.peek(); + if (next.getString() == token) { + tokenSource.next(); + return true; + } else { + return false; + } + } + + public String maybeEatIdentifier() { + IToken next = tokenSource.peek(); + if (next.isIdentifier()) { + tokenSource.next(); + return next.getString(); + } else { + return null; + } + } + + public boolean peek(String token) { + IToken next = tokenSource.peek(); + return next.getString() == token; + } + + public void checkEof() { + IToken last = tokenSource.next(); + if (last != IToken.EOF) { + throw new ParserException("unexpected pointcut element", last); + } + } + + public PatternParser(String data) { + this(BasicTokenSource.makeTokenSource(data,null)); + } + + public PatternParser(String data, ISourceContext context) { + this(BasicTokenSource.makeTokenSource(data,context)); + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PerCflow.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PerCflow.java new file mode 100644 index 000000000..7cc0a242b --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PerCflow.java @@ -0,0 +1,171 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.Advice; +import org.aspectj.weaver.AjcMemberMaker; +import org.aspectj.weaver.CrosscuttingMembers; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.NameMangler; +import org.aspectj.weaver.ResolvedMemberImpl; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Expr; +import org.aspectj.weaver.ast.Test; + +public class PerCflow extends PerClause { + private final boolean isBelow; + private final Pointcut entry; + + public PerCflow(Pointcut entry, boolean isBelow) { + this.entry = entry; + this.isBelow = isBelow; + } + + // ----- + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public int couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS_BITS; + } + + public FuzzyBoolean fastMatch(FastMatchInfo type) { + return FuzzyBoolean.MAYBE; + } + + protected FuzzyBoolean matchInternal(Shadow shadow) { + return FuzzyBoolean.YES; + } + + public void resolveBindings(IScope scope, Bindings bindings) { + // assert bindings == null; + entry.resolve(scope); + } + + public Pointcut parameterizeWith(Map typeVariableMap, World w) { + PerCflow ret = new PerCflow(entry.parameterizeWith(typeVariableMap, w), isBelow); + ret.copyLocationFrom(this); + return ret; + } + + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + Expr myInstance = Expr.makeCallExpr(AjcMemberMaker.perCflowAspectOfMethod(inAspect), Expr.NONE, inAspect); + state.setAspectInstance(myInstance); + return Test.makeCall(AjcMemberMaker.perCflowHasAspectMethod(inAspect), Expr.NONE); + } + + public PerClause concretize(ResolvedType inAspect) { + PerCflow ret = new PerCflow(entry, isBelow); + ret.inAspect = inAspect; + if (inAspect.isAbstract()) + return ret; + + Member cflowStackField = new ResolvedMemberImpl(Member.FIELD, inAspect, Modifier.STATIC | Modifier.PUBLIC | Modifier.FINAL, + UnresolvedType.forName(NameMangler.CFLOW_STACK_TYPE), NameMangler.PERCFLOW_FIELD_NAME, UnresolvedType.NONE); + + World world = inAspect.getWorld(); + + CrosscuttingMembers xcut = inAspect.crosscuttingMembers; + + Collection previousCflowEntries = xcut.getCflowEntries(); + Pointcut concreteEntry = entry.concretize(inAspect, inAspect, 0, null); // IntMap + // . + // EMPTY + // ) + // ; + List innerCflowEntries = new ArrayList(xcut.getCflowEntries()); + innerCflowEntries.removeAll(previousCflowEntries); + + xcut.addConcreteShadowMunger(Advice.makePerCflowEntry(world, concreteEntry, isBelow, cflowStackField, inAspect, + innerCflowEntries)); + + // ATAJ: add a munger to add the aspectOf(..) to the @AJ aspects + if (inAspect.isAnnotationStyleAspect() && !inAspect.isAbstract()) { + inAspect.crosscuttingMembers.addLateTypeMunger(inAspect.getWorld().getWeavingSupport().makePerClauseAspect(inAspect, + getKind())); + } + + // ATAJ inline around advice support - don't use a late munger to allow + // around inling for itself + if (inAspect.isAnnotationStyleAspect() && !inAspect.getWorld().isXnoInline()) { + inAspect.crosscuttingMembers.addTypeMunger(inAspect.getWorld().getWeavingSupport() + .createAccessForInlineMunger(inAspect)); + } + + return ret; + } + + public void write(DataOutputStream s) throws IOException { + PERCFLOW.write(s); + entry.write(s); + s.writeBoolean(isBelow); + writeLocation(s); + } + + public static PerClause readPerClause(VersionedDataInputStream s, ISourceContext context) throws IOException { + PerCflow ret = new PerCflow(Pointcut.read(s, context), s.readBoolean()); + ret.readLocation(context, s); + return ret; + } + + public PerClause.Kind getKind() { + return PERCFLOW; + } + + public Pointcut getEntry() { + return entry; + } + + public String toString() { + return "percflow(" + inAspect + " on " + entry + ")"; + } + + public String toDeclarationString() { + if (isBelow) + return "percflowbelow(" + entry + ")"; + return "percflow(" + entry + ")"; + } + + public boolean equals(Object other) { + if (!(other instanceof PerCflow)) + return false; + PerCflow pc = (PerCflow) other; + return (pc.isBelow && isBelow) && ((pc.inAspect == null) ? (inAspect == null) : pc.inAspect.equals(inAspect)) + && ((pc.entry == null) ? (entry == null) : pc.entry.equals(entry)); + } + + public int hashCode() { + int result = 17; + result = 37 * result + (isBelow ? 0 : 1); + result = 37 * result + ((inAspect == null) ? 0 : inAspect.hashCode()); + result = 37 * result + ((entry == null) ? 0 : entry.hashCode()); + return result; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PerClause.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PerClause.java new file mode 100644 index 000000000..62b2b1b85 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PerClause.java @@ -0,0 +1,91 @@ +/* ******************************************************************* + * 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 java.io.IOException; + +import org.aspectj.util.TypeSafeEnum; +import org.aspectj.weaver.*; + +// PTWIMPL New kind added to this class, can be (de)serialized +public abstract class PerClause extends Pointcut { + protected ResolvedType inAspect; + + public static PerClause readPerClause(VersionedDataInputStream s, ISourceContext context) throws IOException { + Kind kind = Kind.read(s); + if (kind == SINGLETON) return PerSingleton.readPerClause(s, context); + else if (kind == PERCFLOW) return PerCflow.readPerClause(s, context); + else if (kind == PEROBJECT) return PerObject.readPerClause(s, context); + else if (kind == FROMSUPER) return PerFromSuper.readPerClause(s, context); + else if (kind == PERTYPEWITHIN) return PerTypeWithin.readPerClause(s,context); + + throw new BCException("unknown kind: " + kind); + } + + public final Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + throw new RuntimeException("unimplemented: wrong concretize"); + } + + public abstract PerClause concretize(ResolvedType inAspect); + + public abstract PerClause.Kind getKind(); + + public abstract String toDeclarationString(); + + public static class Kind extends TypeSafeEnum { + public Kind(String name, int key) { super(name, key); } + + public static Kind read(VersionedDataInputStream s) throws IOException { + int key = s.readByte(); + switch(key) { + case 1: return SINGLETON; + case 2: return PERCFLOW; + case 3: return PEROBJECT; + case 4: return FROMSUPER; + case 5: return PERTYPEWITHIN; + } + throw new BCException("weird kind " + key); + } + } + + public static final Kind SINGLETON = new Kind("issingleton", 1); + public static final Kind PERCFLOW = new Kind("percflow", 2); + public static final Kind PEROBJECT = new Kind("perobject", 3); + public static final Kind FROMSUPER = new Kind("fromsuper", 4); + public static final Kind PERTYPEWITHIN = new Kind("pertypewithin",5); + + public static class KindAnnotationPrefix extends TypeSafeEnum { + private KindAnnotationPrefix(String name, int key) { + super(name, key); + } + + public String extractPointcut(String perClause) { + int from = getName().length(); + int to = perClause.length()-1; + if (!perClause.startsWith(getName()) + || !perClause.endsWith(")") + || from > perClause.length()) { + throw new RuntimeException("cannot read perclause " + perClause); + } + + return perClause.substring(from, to); + } + + public static final KindAnnotationPrefix PERCFLOW = new KindAnnotationPrefix("percflow(", 1); + public static final KindAnnotationPrefix PERCFLOWBELOW = new KindAnnotationPrefix("percflowbelow(", 2); + public static final KindAnnotationPrefix PERTHIS = new KindAnnotationPrefix("perthis(", 3); + public static final KindAnnotationPrefix PERTARGET = new KindAnnotationPrefix("pertarget(", 4); + public static final KindAnnotationPrefix PERTYPEWITHIN = new KindAnnotationPrefix("pertypewithin(", 5); + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PerFromSuper.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PerFromSuper.java new file mode 100644 index 000000000..13c81fe93 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PerFromSuper.java @@ -0,0 +1,135 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.util.Map; + +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Test; + +public class PerFromSuper extends PerClause { + private PerClause.Kind kind; + + public PerFromSuper(PerClause.Kind kind) { + this.kind = kind; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this,data); + } + + public int couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS_BITS; + } + + public FuzzyBoolean fastMatch(FastMatchInfo type) { + throw new RuntimeException("unimplemented"); + } + + protected FuzzyBoolean matchInternal(Shadow shadow) { + throw new RuntimeException("unimplemented"); + } + + public void resolveBindings(IScope scope, Bindings bindings) { + // this method intentionally left blank + } + + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + throw new RuntimeException("unimplemented"); + } + + + public PerClause concretize(ResolvedType inAspect) { + PerClause p = lookupConcretePerClause(inAspect.getSuperclass()); + if (p == null) { + inAspect.getWorld().getMessageHandler().handleMessage( + MessageUtil.error(WeaverMessages.format(WeaverMessages.MISSING_PER_CLAUSE,inAspect.getSuperclass()), getSourceLocation()) + ); + return new PerSingleton().concretize(inAspect);// AV: fallback on something else NPE in AJDT + } else { + if (p.getKind() != kind) { + inAspect.getWorld().getMessageHandler().handleMessage( + MessageUtil.error(WeaverMessages.format(WeaverMessages.WRONG_PER_CLAUSE,kind,p.getKind()), + getSourceLocation()) + ); + } + return p.concretize(inAspect); + } + } + + public Pointcut parameterizeWith(Map typeVariableMap,World w) { + return this; + } + + + + public PerClause lookupConcretePerClause(ResolvedType lookupType) { + PerClause ret = lookupType.getPerClause(); + if (ret == null) return null; + if (ret instanceof PerFromSuper) { + return lookupConcretePerClause(lookupType.getSuperclass()); + } + return ret; + } + + + public void write(DataOutputStream s) throws IOException { + FROMSUPER.write(s); + kind.write(s); + writeLocation(s); + } + + public static PerClause readPerClause(VersionedDataInputStream s, ISourceContext context) throws IOException { + PerFromSuper ret = new PerFromSuper(Kind.read(s)); + ret.readLocation(context, s); + return ret; + } + + public String toString() { + return "perFromSuper(" + kind + ", " + inAspect + ")"; + } + + public String toDeclarationString() { + return ""; + } + + public PerClause.Kind getKind() { + return kind; + } + + public boolean equals(Object other) { + if (!(other instanceof PerFromSuper)) return false; + PerFromSuper pc = (PerFromSuper)other; + return pc.kind.equals(kind) + && ((pc.inAspect == null) ? (inAspect == null) : pc.inAspect.equals(inAspect)); + } + + public int hashCode() { + int result = 17; + result = 37*result + kind.hashCode(); + result = 37*result + ((inAspect == null) ? 0 : inAspect.hashCode()); + return result; + } + + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PerObject.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PerObject.java new file mode 100644 index 000000000..ab28c8fae --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PerObject.java @@ -0,0 +1,187 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.Advice; +import org.aspectj.weaver.AjcMemberMaker; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.PerObjectInterfaceTypeMunger; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.ResolvedTypeMunger; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Expr; +import org.aspectj.weaver.ast.Test; +import org.aspectj.weaver.ast.Var; + +public class PerObject extends PerClause { + private final boolean isThis; + private final Pointcut entry; + + private static final int thisKindSet; + private static final int targetKindSet; + + static { + int thisFlags = Shadow.ALL_SHADOW_KINDS_BITS; + int targFlags = Shadow.ALL_SHADOW_KINDS_BITS; + for (int i = 0; i < Shadow.SHADOW_KINDS.length; i++) { + Shadow.Kind kind = Shadow.SHADOW_KINDS[i]; + if (kind.neverHasThis()) + thisFlags -= kind.bit; + if (kind.neverHasTarget()) + targFlags -= kind.bit; + } + thisKindSet = thisFlags; + targetKindSet = targFlags; + } + + public PerObject(Pointcut entry, boolean isThis) { + this.entry = entry; + this.isThis = isThis; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public int couldMatchKinds() { + return isThis ? thisKindSet : targetKindSet; + } + + // ----- + public FuzzyBoolean fastMatch(FastMatchInfo type) { + return FuzzyBoolean.MAYBE; + } + + protected FuzzyBoolean matchInternal(Shadow shadow) { + // System.err.println("matches " + this + " ? " + shadow + ", " + + // shadow.hasTarget()); + // ??? could probably optimize this better by testing could match + if (isThis) + return FuzzyBoolean.fromBoolean(shadow.hasThis()); + else + return FuzzyBoolean.fromBoolean(shadow.hasTarget()); + } + + public void resolveBindings(IScope scope, Bindings bindings) { + // assert bindings == null; + entry.resolve(scope); + } + + public Pointcut parameterizeWith(Map typeVariableMap, World w) { + PerObject ret = new PerObject(entry.parameterizeWith(typeVariableMap, w), isThis); + ret.copyLocationFrom(this); + return ret; + } + + private Var getVar(Shadow shadow) { + return isThis ? shadow.getThisVar() : shadow.getTargetVar(); + } + + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + Expr myInstance = Expr.makeCallExpr(AjcMemberMaker.perObjectAspectOfMethod(inAspect), new Expr[] { getVar(shadow) }, + inAspect); + state.setAspectInstance(myInstance); + return Test.makeCall(AjcMemberMaker.perObjectHasAspectMethod(inAspect), new Expr[] { getVar(shadow) }); + } + + public PerClause concretize(ResolvedType inAspect) { + PerObject ret = new PerObject(entry, isThis); + + ret.inAspect = inAspect; + if (inAspect.isAbstract()) + return ret; + + World world = inAspect.getWorld(); + + Pointcut concreteEntry = entry.concretize(inAspect, inAspect, 0, null); + // concreteEntry = new AndPointcut(this, concreteEntry); + // concreteEntry.state = Pointcut.CONCRETE; + inAspect.crosscuttingMembers.addConcreteShadowMunger(Advice.makePerObjectEntry(world, concreteEntry, isThis, inAspect)); + + // FIXME AV - don't use lateMunger here due to test + // "inheritance, around advice and abstract pointcuts" + // see #75442 thread. Issue with weaving order. + ResolvedTypeMunger munger = new PerObjectInterfaceTypeMunger(inAspect, concreteEntry); + inAspect.crosscuttingMembers.addLateTypeMunger(world.getWeavingSupport().concreteTypeMunger(munger, inAspect)); + + // ATAJ: add a munger to add the aspectOf(..) to the @AJ aspects + if (inAspect.isAnnotationStyleAspect() && !inAspect.isAbstract()) { + inAspect.crosscuttingMembers.addLateTypeMunger(inAspect.getWorld().getWeavingSupport().makePerClauseAspect(inAspect, + getKind())); + } + + // ATAJ inline around advice support - don't use a late munger to allow + // around inling for itself + if (inAspect.isAnnotationStyleAspect() && !inAspect.getWorld().isXnoInline()) { + inAspect.crosscuttingMembers.addTypeMunger(world.getWeavingSupport().createAccessForInlineMunger(inAspect)); + } + + return ret; + } + + public void write(DataOutputStream s) throws IOException { + PEROBJECT.write(s); + entry.write(s); + s.writeBoolean(isThis); + writeLocation(s); + } + + public static PerClause readPerClause(VersionedDataInputStream s, ISourceContext context) throws IOException { + PerClause ret = new PerObject(Pointcut.read(s, context), s.readBoolean()); + ret.readLocation(context, s); + return ret; + } + + public PerClause.Kind getKind() { + return PEROBJECT; + } + + public boolean isThis() { + return isThis; + } + + public String toString() { + return "per" + (isThis ? "this" : "target") + "(" + entry + ")"; + } + + public String toDeclarationString() { + return toString(); + } + + public Pointcut getEntry() { + return entry; + } + + public boolean equals(Object other) { + if (!(other instanceof PerObject)) + return false; + PerObject pc = (PerObject) other; + return (pc.isThis && isThis) && ((pc.inAspect == null) ? (inAspect == null) : pc.inAspect.equals(inAspect)) + && ((pc.entry == null) ? (entry == null) : pc.entry.equals(entry)); + } + + public int hashCode() { + int result = 17; + result = 37 * result + (isThis ? 0 : 1); + result = 37 * result + ((inAspect == null) ? 0 : inAspect.hashCode()); + result = 37 * result + ((entry == null) ? 0 : entry.hashCode()); + return result; + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PerSingleton.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PerSingleton.java new file mode 100644 index 000000000..e91e9f45d --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PerSingleton.java @@ -0,0 +1,173 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.AjcMemberMaker; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Expr; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Test; + +public class PerSingleton extends PerClause { + + private ResolvedMember perSingletonAspectOfMethod; + + public PerSingleton() { + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public int couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS_BITS; + } + + public FuzzyBoolean fastMatch(FastMatchInfo type) { + return FuzzyBoolean.YES; + } + + protected FuzzyBoolean matchInternal(Shadow shadow) { + return FuzzyBoolean.YES; + } + + public void resolveBindings(IScope scope, Bindings bindings) { + // this method intentionally left blank + } + + public Pointcut parameterizeWith(Map typeVariableMap, World w) { + return this; + } + + public Test findResidueInternal(Shadow shadow, ExposedState state) { + // TODO: the commented code is for slow Aspects.aspectOf() style - keep + // or remove + // + // Expr myInstance = + // Expr.makeCallExpr(AjcMemberMaker.perSingletonAspectOfMethod(inAspect), + // Expr.NONE, inAspect); + // + // state.setAspectInstance(myInstance); + // + // // we have no test + // // a NoAspectBoundException will be thrown if we need an instance of + // this + // // aspect before we are bound + // return Literal.TRUE; + // if (!Ajc5MemberMaker.isSlowAspect(inAspect)) { + if (perSingletonAspectOfMethod == null) { + // Build this just once + perSingletonAspectOfMethod = AjcMemberMaker.perSingletonAspectOfMethod(inAspect); + } + Expr myInstance = Expr.makeCallExpr(perSingletonAspectOfMethod, Expr.NONE, inAspect); + + state.setAspectInstance(myInstance); + + // we have no test + // a NoAspectBoundException will be thrown if we need an instance of + // this + // aspect before we are bound + return Literal.TRUE; + // } else { + // CallExpr callAspectOf =Expr.makeCallExpr( + // Ajc5MemberMaker.perSingletonAspectOfMethod(inAspect), + // new Expr[]{ + // Expr.makeStringConstantExpr(inAspect.getName(), inAspect), + // //FieldGet is using ResolvedType and I don't need that here + // new FieldGetOn(Member.ajClassField, shadow.getEnclosingType()) + // }, + // inAspect + // ); + // Expr castedCallAspectOf = new CastExpr(callAspectOf, + // inAspect.getName()); + // state.setAspectInstance(castedCallAspectOf); + // return Literal.TRUE; + // } + } + + public PerClause concretize(ResolvedType inAspect) { + PerSingleton ret = new PerSingleton(); + + ret.copyLocationFrom(this); + + World world = inAspect.getWorld(); + + ret.inAspect = inAspect; + + // ATAJ: add a munger to add the aspectOf(..) to the @AJ aspects + if (inAspect.isAnnotationStyleAspect() && !inAspect.isAbstract()) { + // TODO will those change be ok if we add a serializable aspect ? + // dig: + // "can't be Serializable/Cloneable unless -XserializableAspects" + if (getKind() == SINGLETON) { // pr149560 + inAspect.crosscuttingMembers.addTypeMunger(world.getWeavingSupport().makePerClauseAspect(inAspect, getKind())); + } else { + inAspect.crosscuttingMembers.addLateTypeMunger(world.getWeavingSupport().makePerClauseAspect(inAspect, getKind())); + } + } + + // ATAJ inline around advice support + if (inAspect.isAnnotationStyleAspect() && !inAspect.getWorld().isXnoInline()) { + inAspect.crosscuttingMembers.addTypeMunger(world.getWeavingSupport().createAccessForInlineMunger(inAspect)); + } + + return ret; + } + + public void write(DataOutputStream s) throws IOException { + SINGLETON.write(s); + writeLocation(s); + } + + public static PerClause readPerClause(VersionedDataInputStream s, ISourceContext context) throws IOException { + PerSingleton ret = new PerSingleton(); + ret.readLocation(context, s); + return ret; + } + + public PerClause.Kind getKind() { + return SINGLETON; + } + + public String toString() { + return "persingleton(" + inAspect + ")"; + } + + public String toDeclarationString() { + return ""; + } + + public boolean equals(Object other) { + if (!(other instanceof PerSingleton)) + return false; + PerSingleton pc = (PerSingleton) other; + return ((pc.inAspect == null) ? (inAspect == null) : pc.inAspect.equals(inAspect)); + } + + public int hashCode() { + int result = 17; + result = 37 * result + ((inAspect == null) ? 0 : inAspect.hashCode()); + return result; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PerThisOrTargetPointcutVisitor.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PerThisOrTargetPointcutVisitor.java new file mode 100644 index 000000000..7d62ae8ab --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PerThisOrTargetPointcutVisitor.java @@ -0,0 +1,216 @@ +/******************************************************************************* + * 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.patterns; + +import org.aspectj.weaver.ResolvedPointcutDefinition; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; + +/** + * A visitor that turns a pointcut into a type pattern equivalent for a perthis or pertarget matching: - pertarget(target(Foo)) => + * Foo+ (this one is a special case..) - pertarget(execution(* Foo.do()) => Foo - perthis(call(* Foo.do()) => * - perthis(!call(* + * Foo.do()) => * (see how the ! has been absorbed here..) + * + * @author Alexandre Vasseur + */ +public class PerThisOrTargetPointcutVisitor extends AbstractPatternNodeVisitor { + + /** A maybe marker */ + private final static TypePattern MAYBE = new TypePatternMayBe(); + + private final boolean m_isTarget; + private final ResolvedType m_fromAspectType; + + public PerThisOrTargetPointcutVisitor(boolean isTarget, ResolvedType fromAspectType) { + m_isTarget = isTarget; + m_fromAspectType = fromAspectType; + } + + public TypePattern getPerTypePointcut(Pointcut perClausePointcut) { + return (TypePattern) perClausePointcut.accept(this, perClausePointcut); + } + + // -- visitor methods, all is like Identity visitor except when it comes to transform pointcuts + + public Object visit(WithinPointcut node, Object data) { + if (m_isTarget) { + // pertarget(.. && within(Foo)) => true + // pertarget(.. && !within(Foo)) => true as well ! + return MAYBE; + } else { + return node.getTypePattern(); + } + } + + public Object visit(WithincodePointcut node, Object data) { + if (m_isTarget) { + // pertarget(.. && withincode(* Foo.do())) => true + // pertarget(.. && !withincode(* Foo.do())) => true as well ! + return MAYBE; + } else { + return node.getSignature().getDeclaringType(); + } + } + + public Object visit(WithinAnnotationPointcut node, Object data) { + if (m_isTarget) { + return MAYBE; + } else { + return new AnyWithAnnotationTypePattern(node.getAnnotationTypePattern()); + } + } + + public Object visit(WithinCodeAnnotationPointcut node, Object data) { + if (m_isTarget) { + return MAYBE; + } else { + return MAYBE;// FIXME AV - can we optimize ? perthis(@withincode(Foo)) = hasmethod(..) + } + } + + public Object visit(KindedPointcut node, Object data) { + if (node.getKind().equals(Shadow.AdviceExecution)) { + return MAYBE;// TODO AV - can we do better ? + } else if (node.getKind().equals(Shadow.ConstructorExecution) || node.getKind().equals(Shadow.Initialization) + || node.getKind().equals(Shadow.MethodExecution) || node.getKind().equals(Shadow.PreInitialization) + || node.getKind().equals(Shadow.StaticInitialization)) { + return node.getSignature().getDeclaringType(); + } else if (node.getKind().equals(Shadow.ConstructorCall) || node.getKind().equals(Shadow.FieldGet) + || node.getKind().equals(Shadow.FieldSet) || node.getKind().equals(Shadow.MethodCall)) { + if (m_isTarget) { + return node.getSignature().getDeclaringType(); + } else { + return MAYBE; + } + } else if (node.getKind().equals(Shadow.ExceptionHandler)) { + return MAYBE; + } else { + throw new ParserException("Undetermined - should not happen: " + node.getKind().getSimpleName(), null); + } + } + + public Object visit(AndPointcut node, Object data) { + return new AndTypePattern(getPerTypePointcut(node.left), getPerTypePointcut(node.right)); + } + + public Object visit(OrPointcut node, Object data) { + return new OrTypePattern(getPerTypePointcut(node.left), getPerTypePointcut(node.right)); + } + + public Object visit(NotPointcut node, Object data) { + // TypePattern negated = getPerTypePointcut(node.getNegatedPointcut()); + // if (MAYBE.equals(negated)) { + // return MAYBE; + // } + // return new NotTypePattern(negated); + // AMC - the only safe thing to return here is maybe... + // see for example pr114054 + return MAYBE; + } + + public Object visit(ThisOrTargetAnnotationPointcut node, Object data) { + if (m_isTarget && !node.isThis()) { + return new AnyWithAnnotationTypePattern(node.getAnnotationTypePattern()); + } else if (!m_isTarget && node.isThis()) { + return new AnyWithAnnotationTypePattern(node.getAnnotationTypePattern()); + } else { + // perthis(@target(Foo)) + return MAYBE; + } + } + + public Object visit(ThisOrTargetPointcut node, Object data) { + if ((m_isTarget && !node.isThis()) || (!m_isTarget && node.isThis())) { + String pointcutString = node.getType().toString(); + // see pr115788 "" means there was a problem resolving types - that will be reported so dont blow up + // the parser here.. + if (pointcutString.equals("")) { + return new NoTypePattern(); + } + // pertarget(target(Foo)) => Foo+ for type pattern matching + // perthis(this(Foo)) => Foo+ for type pattern matching + // TODO AV - we do like a deep copy by parsing it again.. quite dirty, would need a clean deep copy + TypePattern copy = new PatternParser(pointcutString.replace('$', '.')).parseTypePattern(); + // TODO AV - see dirty replace from $ to . here as inner classes are with $ instead (#108488) + copy.includeSubtypes = true; + return copy; + } else { + // perthis(target(Foo)) => maybe + return MAYBE; + } + } + + public Object visit(ReferencePointcut node, Object data) { + // && pc_ref() + // we know there is no support for binding in perClause: perthis(pc_ref(java.lang.String)) + // TODO AV - may need some work for generics.. + + ResolvedPointcutDefinition pointcutDec; + ResolvedType searchStart = m_fromAspectType; + if (node.onType != null) { + searchStart = node.onType.resolve(m_fromAspectType.getWorld()); + if (searchStart.isMissing()) { + return MAYBE;// this should not happen since concretize will fails but just in case.. + } + } + pointcutDec = searchStart.findPointcut(node.name); + + return getPerTypePointcut(pointcutDec.getPointcut()); + } + + public Object visit(IfPointcut node, Object data) { + return TypePattern.ANY; + } + + public Object visit(HandlerPointcut node, Object data) { + // quiet unexpected since a KindedPointcut but do as if... + return MAYBE; + } + + public Object visit(CflowPointcut node, Object data) { + return MAYBE; + } + + public Object visit(ConcreteCflowPointcut node, Object data) { + return MAYBE; + } + + public Object visit(ArgsPointcut node, Object data) { + return MAYBE; + } + + public Object visit(ArgsAnnotationPointcut node, Object data) { + return MAYBE; + } + + public Object visit(AnnotationPointcut node, Object data) { + return MAYBE; + } + + public Object visit(Pointcut.MatchesNothingPointcut node, Object data) { + // a small hack since the usual MatchNothing has its toString = "" which is not parseable back + // while I use back parsing for check purpose. + return new NoTypePattern() { + public String toString() { + return "false"; + } + }; + } + + /** + * A MayBe type pattern that acts as ANY except that !MAYBE = MAYBE + * + * @author Alexandre Vasseur + */ + private static class TypePatternMayBe extends AnyTypePattern { + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PerTypeWithin.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PerTypeWithin.java new file mode 100644 index 000000000..9c1fd655b --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PerTypeWithin.java @@ -0,0 +1,230 @@ +/* ******************************************************************* + * 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.patterns; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Map; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.Message; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.Advice; +import org.aspectj.weaver.AjcMemberMaker; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.PerTypeWithinTargetTypeMunger; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.ResolvedTypeMunger; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Expr; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Test; + +// PTWIMPL Represents a parsed pertypewithin() +public class PerTypeWithin extends PerClause { + + private TypePattern typePattern; + + // Any shadow could be considered within a pertypewithin() type pattern + private static final int kindSet = Shadow.ALL_SHADOW_KINDS_BITS; + + public TypePattern getTypePattern() { + return typePattern; + } + + public PerTypeWithin(TypePattern p) { + typePattern = p; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public int couldMatchKinds() { + return kindSet; + } + + public Pointcut parameterizeWith(Map typeVariableMap, World w) { + PerTypeWithin ret = new PerTypeWithin(typePattern.parameterizeWith(typeVariableMap, w)); + ret.copyLocationFrom(this); + return ret; + } + + // ----- + public FuzzyBoolean fastMatch(FastMatchInfo info) { + if (typePattern.annotationPattern instanceof AnyAnnotationTypePattern) { + return isWithinType(info.getType()); + } + return FuzzyBoolean.MAYBE; + } + + protected FuzzyBoolean matchInternal(Shadow shadow) { + ResolvedType enclosingType = shadow.getIWorld().resolve(shadow.getEnclosingType(), true); + if (enclosingType.isMissing()) { + // PTWIMPL ?? Add a proper message + IMessage msg = new Message("Cant find type pertypewithin matching...", shadow.getSourceLocation(), true, + new ISourceLocation[] { getSourceLocation() }); + shadow.getIWorld().getMessageHandler().handleMessage(msg); + } + + // See pr106554 - we can't put advice calls in an interface when the + // advice is defined + // in a pertypewithin aspect - the JPs only exist in the static + // initializer and can't + // call the localAspectOf() method. + if (enclosingType.isInterface()) + return FuzzyBoolean.NO; + + typePattern.resolve(shadow.getIWorld()); + return isWithinType(enclosingType); + } + + public void resolveBindings(IScope scope, Bindings bindings) { + typePattern = typePattern.resolveBindings(scope, bindings, false, false); + } + + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + // Member ptwField = + // AjcMemberMaker.perTypeWithinField(shadow.getEnclosingType + // (),inAspect); + + Expr myInstance = Expr.makeCallExpr(AjcMemberMaker.perTypeWithinLocalAspectOf(shadow.getEnclosingType(), inAspect/* + * shadow. + * getEnclosingType + * ( ) + */), + Expr.NONE, inAspect); + state.setAspectInstance(myInstance); + + // this worked at one point + // Expr myInstance = + // Expr.makeFieldGet(ptwField,shadow.getEnclosingType() + // .resolve(shadow.getIWorld()));//inAspect); + // state.setAspectInstance(myInstance); + + // return Test.makeFieldGetCall(ptwField,null,Expr.NONE); + // cflowField, cflowCounterIsValidMethod, Expr.NONE + + // This is what is in the perObject variant of this ... + // Expr myInstance = + // Expr.makeCallExpr(AjcMemberMaker.perTypeWithinAspectOfMethod(inAspect) + // , + // new Expr[] {getVar(shadow)}, inAspect); + // state.setAspectInstance(myInstance); + // return + // Test.makeCall(AjcMemberMaker.perTypeWithinHasAspectMethod(inAspect), + // new Expr[] { getVar(shadow) }); + // + + return match(shadow).alwaysTrue() ? Literal.TRUE : Literal.FALSE; + } + + public PerClause concretize(ResolvedType inAspect) { + PerTypeWithin ret = new PerTypeWithin(typePattern); + ret.copyLocationFrom(this); + ret.inAspect = inAspect; + if (inAspect.isAbstract()) + return ret; + + World world = inAspect.getWorld(); + + SignaturePattern sigpat = new SignaturePattern(Member.STATIC_INITIALIZATION, ModifiersPattern.ANY, TypePattern.ANY, + TypePattern.ANY,// typePattern, + NamePattern.ANY, TypePatternList.ANY, ThrowsPattern.ANY, AnnotationTypePattern.ANY); + + Pointcut staticInitStar = new KindedPointcut(Shadow.StaticInitialization, sigpat); + Pointcut withinTp = new WithinPointcut(typePattern); + Pointcut andPcut = new AndPointcut(staticInitStar, withinTp); + // We want the pointcut to be 'staticinitialization(*) && + // within(' - + // we *cannot* shortcut this to staticinitialization() + // because it + // doesnt mean the same thing. + + // This munger will initialize the aspect instance field in the matched + // type + + inAspect.crosscuttingMembers.addConcreteShadowMunger(Advice.makePerTypeWithinEntry(world, andPcut, inAspect)); + + ResolvedTypeMunger munger = new PerTypeWithinTargetTypeMunger(inAspect, ret); + inAspect.crosscuttingMembers.addTypeMunger(world.getWeavingSupport().concreteTypeMunger(munger, inAspect)); + + // ATAJ: add a munger to add the aspectOf(..) to the @AJ aspects + if (inAspect.isAnnotationStyleAspect() && !inAspect.isAbstract()) { + inAspect.crosscuttingMembers.addLateTypeMunger(world.getWeavingSupport().makePerClauseAspect(inAspect, getKind())); + } + + // ATAJ inline around advice support - don't use a late munger to allow + // around inling for itself + if (inAspect.isAnnotationStyleAspect() && !world.isXnoInline()) { + inAspect.crosscuttingMembers.addTypeMunger(world.getWeavingSupport().createAccessForInlineMunger(inAspect)); + } + + return ret; + + } + + public void write(DataOutputStream s) throws IOException { + PERTYPEWITHIN.write(s); + typePattern.write(s); + writeLocation(s); + } + + public static PerClause readPerClause(VersionedDataInputStream s, ISourceContext context) throws IOException { + PerClause ret = new PerTypeWithin(TypePattern.read(s, context)); + ret.readLocation(context, s); + return ret; + } + + public PerClause.Kind getKind() { + return PERTYPEWITHIN; + } + + public String toString() { + return "pertypewithin(" + typePattern + ")"; + } + + public String toDeclarationString() { + return toString(); + } + + private FuzzyBoolean isWithinType(ResolvedType type) { + while (type != null) { + if (typePattern.matchesStatically(type)) { + return FuzzyBoolean.YES; + } + type = type.getDeclaringType(); + } + return FuzzyBoolean.NO; + } + + public boolean equals(Object other) { + if (!(other instanceof PerTypeWithin)) + return false; + PerTypeWithin pc = (PerTypeWithin) other; + return ((pc.inAspect == null) ? (inAspect == null) : pc.inAspect.equals(inAspect)) + && ((pc.typePattern == null) ? (typePattern == null) : pc.typePattern.equals(typePattern)); + } + + public int hashCode() { + int result = 17; + result = 37 * result + ((inAspect == null) ? 0 : inAspect.hashCode()); + result = 37 * result + ((typePattern == null) ? 0 : typePattern.hashCode()); + return result; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/Pointcut.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/Pointcut.java new file mode 100644 index 000000000..784b3df17 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/Pointcut.java @@ -0,0 +1,396 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.util.TypeSafeEnum; +import org.aspectj.weaver.Advice; +import org.aspectj.weaver.AdviceKind; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.Checker; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.PoliceExtensionUse; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.ShadowMunger; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Test; + +/** + * The lifecycle of Pointcuts is modeled by Pointcut.State. It has three things: + * + *

Creation -- SYMBOLIC -- then resolve(IScope) -- RESOLVED -- concretize(...) -- CONCRETE + * + * @author Erik Hilsdale + * @author Jim Hugunin + * + * A day in the life of a pointcut.... - AMC. + * ========================================== + * + * Pointcuts are created by the PatternParser, which is called by ajdt to + * parse a pointcut from the PseudoTokens AST node (which in turn are part + * of a PointcutDesignator AST node). + * + * Pointcuts are resolved by ajdt when an AdviceDeclaration or a + * PointcutDeclaration has its statements resolved. This happens as + * part of completeTypeBindings in the AjLookupEnvironment which is + * called after the diet parse phase of the compiler. Named pointcuts, + * and references to named pointcuts are instances of ReferencePointcut. + * + * At the end of the compilation process, the pointcuts are serialized + * (write method) into attributes in the class file. + * + * When the weaver loads the class files, it unpacks the attributes + * and deserializes the pointcuts (read). All aspects are added to the + * world, by calling addOrReplaceAspect on + * the crosscutting members set of the world. When aspects are added or + * replaced, the crosscutting members in the aspect are extracted as + * ShadowMungers (each holding a pointcut). The ShadowMungers are + * concretized, which concretizes the pointcuts. At this stage + * ReferencePointcuts are replaced by their declared content. + * + * During weaving, the weaver processes type by type. It first culls + * potentially matching ShadowMungers by calling the fastMatch method + * on their pointcuts. Only those that might match make it through to + * the next phase. At the next phase, all of the shadows within the + * type are created and passed to the pointcut for matching (match). + * + * When the actual munging happens, matched pointcuts are asked for + * their residue (findResidue) - the runtime test if any. Because of + * negation, findResidue may be called on pointcuts that could never + * match the shadow. + * + */ +public abstract class Pointcut extends PatternNode { + public static final class State extends TypeSafeEnum { + public State(String name, int key) { + super(name, key); + } + } + + /** + * ATAJ the name of the formal for which we don't want any warning when unbound since + * we consider them as implicitly bound. f.e. JoinPoint for @AJ advices + */ + public String[] m_ignoreUnboundBindingForNames = new String[0]; + + public static final State SYMBOLIC = new State("symbolic", 0); + public static final State RESOLVED = new State("resolved", 1); + public static final State CONCRETE = new State("concrete", 2); + + protected byte pointcutKind; + + public State state; + + protected int lastMatchedShadowId; + private FuzzyBoolean lastMatchedShadowResult; + private String[] typeVariablesInScope = new String[0]; + + protected boolean hasBeenParameterized = false; + + /** + * Constructor for Pattern. + */ + public Pointcut() { + super(); + this.state = SYMBOLIC; + } + + + /** + * Could I match any shadows in the code defined within this type? + */ + public abstract FuzzyBoolean fastMatch(FastMatchInfo info); + + /** + * The set of ShadowKinds that this Pointcut could possibly match - + * an int whose bits are set according to the Kinds specified in Shadow.java + */ + public abstract int couldMatchKinds(); + + public String[] getTypeVariablesInScope() { + return typeVariablesInScope; + } + + public void setTypeVariablesInScope(String[] typeVars) { + this.typeVariablesInScope = typeVars; + } + + /** + * Do I really match this shadow? + * XXX implementors need to handle state + */ + public final FuzzyBoolean match(Shadow shadow) { + if (shadow.shadowId == lastMatchedShadowId) return lastMatchedShadowResult; + FuzzyBoolean ret; + // this next test will prevent a lot of un-needed matching going on.... + if (shadow.getKind().isSet(couldMatchKinds())) { + ret = matchInternal(shadow); + } else { + ret = FuzzyBoolean.NO; + } + lastMatchedShadowId = shadow.shadowId; + lastMatchedShadowResult = ret; + return ret; + } + + protected abstract FuzzyBoolean matchInternal(Shadow shadow); + + public static final byte KINDED = 1; + public static final byte WITHIN = 2; + public static final byte THIS_OR_TARGET = 3; + public static final byte ARGS = 4; + public static final byte AND = 5; + public static final byte OR = 6; + public static final byte NOT = 7; + public static final byte REFERENCE = 8; + public static final byte IF = 9; + public static final byte CFLOW = 10; + public static final byte WITHINCODE = 12; + public static final byte HANDLER = 13; + public static final byte IF_TRUE = 14; + public static final byte IF_FALSE = 15; + public static final byte ANNOTATION = 16; + public static final byte ATWITHIN = 17; + public static final byte ATWITHINCODE = 18; + public static final byte ATTHIS_OR_TARGET = 19; + + public static final byte NONE = 20; // DO NOT CHANGE OR REORDER THIS SEQUENCE, THIS VALUE CAN BE PUT OUT BY ASPECTJ1.2.1 + + public static final byte ATARGS = 21; + public static final byte USER_EXTENSION = 22; + + public byte getPointcutKind() { return pointcutKind; } + + // internal, only called from resolve + protected abstract void resolveBindings(IScope scope, Bindings bindings); + + /** + * Returns this pointcut mutated + */ + public final Pointcut resolve(IScope scope) { + assertState(SYMBOLIC); + Bindings bindingTable = new Bindings(scope.getFormalCount()); + IScope bindingResolutionScope = scope; + if (typeVariablesInScope.length > 0) { + bindingResolutionScope = new ScopeWithTypeVariables(typeVariablesInScope,scope); + } + this.resolveBindings(bindingResolutionScope, bindingTable); + bindingTable.checkAllBound(bindingResolutionScope); + this.state = RESOLVED; + return this; + } + + /** + * Returns a new pointcut + * Only used by test cases + */ + public final Pointcut concretize(ResolvedType inAspect, ResolvedType declaringType, int arity) { + Pointcut ret = concretize(inAspect, declaringType, IntMap.idMap(arity)); + // copy the unbound ignore list + ret.m_ignoreUnboundBindingForNames = m_ignoreUnboundBindingForNames; + return ret; + } + + + //XXX this is the signature we're moving to + public final Pointcut concretize(ResolvedType inAspect, ResolvedType declaringType, int arity, ShadowMunger advice) { + //if (state == CONCRETE) return this; //??? + IntMap map = IntMap.idMap(arity); + map.setEnclosingAdvice(advice); + map.setConcreteAspect(inAspect); + return concretize(inAspect, declaringType, map); + } + + public boolean isDeclare(ShadowMunger munger) { + if (munger == null) return false; // ??? Is it actually an error if we get a null munger into this method. + if (munger instanceof Checker) return true; + if (((Advice)munger).getKind().equals(AdviceKind.Softener)) return true; + return false; + } + + + public final Pointcut concretize(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + //!!! add this test -- assertState(RESOLVED); + Pointcut ret = this.concretize1(inAspect, declaringType, bindings); + if (shouldCopyLocationForConcretize()) ret.copyLocationFrom(this); + ret.state = CONCRETE; + // copy the unbound ignore list + ret.m_ignoreUnboundBindingForNames = m_ignoreUnboundBindingForNames; + return ret; + } + + + protected boolean shouldCopyLocationForConcretize() { + return true; + } + + + /** + * Resolves and removes ReferencePointcuts, replacing with basic ones + * + * @param inAspect the aspect to resolve relative to + * @param bindings a Map from formal index in the current lexical context + * -> formal index in the concrete advice that will run + * + * This must always return a new Pointcut object (even if the concretized + * Pointcut is identical to the resolved one). That behavior is + * assumed in many places. + * XXX fix implementors to handle state + */ + protected abstract Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings); + + + //XXX implementors need to handle state + /** + * This can be called from NotPointcut even for Pointcuts that + * don't match the shadow + */ + public final Test findResidue(Shadow shadow, ExposedState state) { +// if (shadow.shadowId == lastMatchedShadowId) return lastMatchedShadowResidue; + Test ret = findResidueInternal(shadow,state); +// lastMatchedShadowResidue = ret; + lastMatchedShadowId = shadow.shadowId; + return ret; + } + + protected abstract Test findResidueInternal(Shadow shadow,ExposedState state); + + //XXX we're not sure whether or not this is needed + //XXX currently it's unused we're keeping it around as a stub + public void postRead(ResolvedType enclosingType) {} + + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + byte kind = s.readByte(); + Pointcut ret; + + switch(kind) { + case KINDED: ret = KindedPointcut.read(s, context); break; + case WITHIN: ret = WithinPointcut.read(s, context); break; + case THIS_OR_TARGET: ret = ThisOrTargetPointcut.read(s, context); break; + case ARGS: ret = ArgsPointcut.read(s, context); break; + case AND: ret = AndPointcut.read(s, context); break; + case OR: ret = OrPointcut.read(s, context); break; + case NOT: ret = NotPointcut.read(s, context); break; + case REFERENCE: ret = ReferencePointcut.read(s, context); break; + case IF: ret = IfPointcut.read(s, context); break; + case CFLOW: ret = CflowPointcut.read(s, context); break; + case WITHINCODE: ret = WithincodePointcut.read(s, context); break; + case HANDLER: ret = HandlerPointcut.read(s, context); break; + case IF_TRUE: ret = IfPointcut.makeIfTruePointcut(RESOLVED); break; + case IF_FALSE: ret = IfPointcut.makeIfFalsePointcut(RESOLVED); break; + case ANNOTATION: ret = AnnotationPointcut.read(s, context); break; + case ATWITHIN: ret = WithinAnnotationPointcut.read(s, context); break; + case ATWITHINCODE: ret = WithinCodeAnnotationPointcut.read(s, context); break; + case ATTHIS_OR_TARGET: ret = ThisOrTargetAnnotationPointcut.read(s, context); break; + case ATARGS: ret = ArgsAnnotationPointcut.read(s,context); break; + case NONE: ret = makeMatchesNothing(RESOLVED); break; + default: + throw new BCException("unknown kind: " + kind); + } + ret.state = RESOLVED; + ret.pointcutKind = kind; + return ret; + + } + + public void check(ISourceContext ctx,World world) { + // this is a quick visitor... + PoliceExtensionUse pointcutPolice = new PoliceExtensionUse(world,this); + this.accept(pointcutPolice, null); + if (pointcutPolice.synchronizationDesignatorEncountered()) + world.setSynchronizationPointcutsInUse(); + } + + + //public void prepare(Shadow shadow) {} + + // ---- test method + + public static Pointcut fromString(String str) { + PatternParser parser = new PatternParser(str); + return parser.parsePointcut(); + } + + static class MatchesNothingPointcut extends Pointcut { + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + return Literal.FALSE; // can only get here if an earlier error occurred + } + + public int couldMatchKinds() { + return Shadow.NO_SHADOW_KINDS_BITS; + } + + public FuzzyBoolean fastMatch(FastMatchInfo type) { + return FuzzyBoolean.NO; + } + + protected FuzzyBoolean matchInternal(Shadow shadow) { + return FuzzyBoolean.NO; + } + + public void resolveBindings(IScope scope, Bindings bindings) { + } + + public void postRead(ResolvedType enclosingType) { + } + + public Pointcut concretize1( + ResolvedType inAspect, + ResolvedType declaringType, + IntMap bindings) { + return makeMatchesNothing(state); + } + + + public void write(DataOutputStream s) throws IOException { + s.writeByte(NONE); + } + + public String toString() { return ""; } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public Pointcut parameterizeWith(Map typeVariableMap,World w) { + return this; + } + } + + //public static Pointcut MatchesNothing = new MatchesNothingPointcut(); + //??? there could possibly be some good optimizations to be done at this point + public static Pointcut makeMatchesNothing(State state) { + Pointcut ret = new MatchesNothingPointcut(); + ret.state = state; + return ret; + } + + + public void assertState(State state) { + if (this.state != state) { + throw new BCException("expected state: " + state + " got: " + this.state); + } + } + + public abstract Pointcut parameterizeWith(Map typeVariableMap,World w); + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PointcutEvaluationExpenseComparator.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PointcutEvaluationExpenseComparator.java new file mode 100644 index 000000000..8c1c07f20 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PointcutEvaluationExpenseComparator.java @@ -0,0 +1,114 @@ +/* ******************************************************************* + * 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 java.util.Comparator; + +import org.aspectj.weaver.Shadow; + +public class PointcutEvaluationExpenseComparator implements Comparator { + + private static final int MATCHES_NOTHING = -1; + private static final int WITHIN = 1; + private static final int ATWITHIN = 2; + private static final int STATICINIT = 3; + private static final int ADVICEEXECUTION = 4; + private static final int HANDLER = 5; + private static final int GET_OR_SET = 6; + private static final int WITHINCODE = 7; + private static final int ATWITHINCODE = 8; + private static final int EXE_INIT_PREINIT = 9; + private static final int CALL = 10; + private static final int ANNOTATION = 11; + private static final int THIS_OR_TARGET = 12; + private static final int AT_THIS_OR_TARGET = 13; + private static final int ARGS = 14; + private static final int AT_ARGS = 15; + private static final int CFLOW = 16; + private static final int IF = 17; + private static final int OTHER = 20; + + /** + * Compare 2 pointcuts based on an estimate of how expensive they may be + * to evaluate. + * + * within + * @within + * staticinitialization [make sure this has a fast match method] + * adviceexecution + * handler + * get, set + * withincode + * @withincode + * execution, initialization, preinitialization + * call + * @annotation + * this, target + * @this, @target + * args + * @args + * cflow, cflowbelow + * if + */ + public int compare(Object o1, Object o2) { + Pointcut p1 = (Pointcut) o1; + Pointcut p2 = (Pointcut) o2; + + // important property for a well-defined comparator + if (p1.equals(p2)) return 0; + int result = getScore(p1) - getScore(p2); + if (result == 0) { + // they have the same evaluation expense, but are not 'equal' + // sort by hashCode + result = p1.hashCode() - p2.hashCode(); + if (result == 0) /*not allowed if ne*/ return -1; + } + return result; + } + + // a higher score means a more expensive evaluation + private int getScore(Pointcut p) { + if (p.couldMatchKinds()==Shadow.NO_SHADOW_KINDS_BITS) return MATCHES_NOTHING; + if (p instanceof WithinPointcut) return WITHIN; + if (p instanceof WithinAnnotationPointcut) return ATWITHIN; + if (p instanceof KindedPointcut) { + KindedPointcut kp = (KindedPointcut) p; + Shadow.Kind kind = kp.getKind(); + if (kind == Shadow.AdviceExecution) { + return ADVICEEXECUTION; + } else if ((kind == Shadow.ConstructorCall) || (kind == Shadow.MethodCall)) { + return CALL; + } else if ((kind == Shadow.ConstructorExecution) || (kind == Shadow.MethodExecution) || + (kind == Shadow.Initialization) || (kind == Shadow.PreInitialization)) { + return EXE_INIT_PREINIT; + } else if (kind == Shadow.ExceptionHandler) { + return HANDLER; + } else if ((kind == Shadow.FieldGet) || (kind == Shadow.FieldSet)) { + return GET_OR_SET; + } else if (kind == Shadow.StaticInitialization) { + return STATICINIT; + } else return OTHER; + } + if (p instanceof AnnotationPointcut) return ANNOTATION; + if (p instanceof ArgsPointcut) return ARGS; + if (p instanceof ArgsAnnotationPointcut) return AT_ARGS; + if (p instanceof CflowPointcut) return CFLOW; + if (p instanceof HandlerPointcut) return HANDLER; + if (p instanceof IfPointcut) return IF; + if (p instanceof ThisOrTargetPointcut) return THIS_OR_TARGET; + if (p instanceof ThisOrTargetAnnotationPointcut) return AT_THIS_OR_TARGET; + if (p instanceof WithincodePointcut) return WITHINCODE; + if (p instanceof WithinCodeAnnotationPointcut) return ATWITHINCODE; + if (p instanceof NotPointcut) return getScore(((NotPointcut)p).getNegatedPointcut()); + if (p instanceof AndPointcut) return getScore(((AndPointcut)p).getLeft()); + if (p instanceof OrPointcut) return getScore(((OrPointcut)p).getLeft()); + return OTHER; + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PointcutRewriter.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PointcutRewriter.java new file mode 100644 index 000000000..7adce3928 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/PointcutRewriter.java @@ -0,0 +1,440 @@ +/* ******************************************************************* + * 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 java.util.Iterator; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.patterns.Pointcut.MatchesNothingPointcut; + +/** + * @author colyer + * + * Performs term rewriting for pointcut expressions. + * + */ +public class PointcutRewriter { + + private static final boolean WATCH_PROGRESS = false; + + /** + * Set forcerewrite if you want to override the checking for something already in DNF (useful for + * some testing) + * Repeated processing of something already in DNF is expensive (it ends up being done for + * every pointcut on every incremental compile) - so let's not do it if we don't have to. + * See pr113257 + */ + public Pointcut rewrite(Pointcut pc,boolean forceRewrite) { + Pointcut result = pc;//checkPC(result); + if (forceRewrite || !isDNF(pc)) { + if (WATCH_PROGRESS) System.out.println("Initial pointcut is ==> " + format(pc)); + result = distributeNot(result);//checkPC(result); + if (WATCH_PROGRESS) System.out.println("Distributing NOT gives ==> " + format(result)); + result = pullUpDisjunctions(result);//checkPC(result); + if (WATCH_PROGRESS) System.out.println("Pull up disjunctions gives ==> " + format(result)); + } else { + if (WATCH_PROGRESS) System.out.println("Not distributing NOTs or pulling up disjunctions, already DNF ==> "+format(pc)); + } + result = simplifyAnds(result); //checkPC(result); + if (WATCH_PROGRESS) System.out.println("Simplifying ANDs gives ==> " + format(result)); + result = removeNothings(result); //checkPC(result); + if (WATCH_PROGRESS) System.out.println("Removing nothings gives ==> " + format(result)); + result = sortOrs(result); //checkPC(result); + if (WATCH_PROGRESS) System.out.println("Sorting ORs gives ==> " + format(result)); + return result; + } + +// /** +// * Checks pointcuts - used for debugging. +// * - this variant checks if the context has been lost, since +// * that can indicate an NPE will happen later reporting a message (pr162657). +// * Not finished, but helped locate the problem ;) +// */ +// private void checkPC(Pointcut pc) { +// if (isNot(pc)) { +// NotPointcut npc = (NotPointcut)pc; +// checkPC(npc.getNegatedPointcut()); +// if (npc.getSourceContext()==null) { +// System.out.println("Lost context for "+npc); +// throw new RuntimeException("Lost context"); +// } +// } else if (isOr(pc)) { +// OrPointcut opc = (OrPointcut)pc; +// checkPC(opc.getLeft()); +// checkPC(opc.getRight()); +// if (opc.getSourceContext()==null) { +// System.out.println("Lost context for "+opc); +// throw new RuntimeException("Lost context"); +// } +// } else if (isAnd(pc)) { +// AndPointcut apc = (AndPointcut)pc; +// checkPC(apc.getLeft()); +// checkPC(apc.getRight()); +// if (apc.getSourceContext()==null) { +// System.out.println("Lost context for "+apc); +// throw new RuntimeException("Lost context"); +// } +// } else { +// if (pc.getSourceContext()==null) { +// System.out.println("Lost context for "+pc); +// throw new RuntimeException("Lost context"); +// } +// } +// } + + public Pointcut rewrite(Pointcut pc) { + return rewrite(pc,false); + } + + /** + * Check if a pointcut is in DNF - if it is then it should be lots of 'ORs' up the + * top with 'ANDs' beneath them. + */ + private boolean isDNF(Pointcut pc) { + return isDNFHelper(pc,true); + } + + /** + * Helper function for determining DNFness. Records when we have crossed the point of allowing ORs. + */ + private boolean isDNFHelper(Pointcut pc,boolean canStillHaveOrs) { + if (isAnd(pc)) { + AndPointcut ap = (AndPointcut)pc; + return isDNFHelper(ap.getLeft(),false) && isDNFHelper(ap.getRight(),false); + } else if (isOr(pc)) { + if (!canStillHaveOrs) return false; + OrPointcut op = (OrPointcut)pc; + return isDNFHelper(op.getLeft(),true) && isDNFHelper(op.getRight(),true); + } else if (isNot(pc)) { + return isDNFHelper(((NotPointcut)pc).getNegatedPointcut(),canStillHaveOrs); + } else { + return true; + } + } + + /** + * Allows formatting of the output pointcut for debugging... + */ + public static String format(Pointcut p) { + String s = p.toString(); + // Regex param needs '(' and '*' changing to '.' +// s = s.replaceAll("persingleton.pkg1.monitoring.ErrorMonitoring.","M"); +// s = s.replaceAll("args.BindingTypePattern.java.lang.Throwable, 0.","Z"); +// s = s.replaceAll("within.pkg1.monitoring.DoMonitorErrors+.","X"); +// s=s.replaceAll("within.pkg1.monitoring....","Y"); +// s=s.replaceAll("if.true.","N"); + return s; + } + + + + // !!X => X + // !(X && Y) => !X || !Y + // !(X || Y) => !X && !Y + private Pointcut distributeNot(Pointcut pc) { + if (isNot(pc)) { + NotPointcut npc = (NotPointcut) pc; + Pointcut notBody = distributeNot(npc.getNegatedPointcut()); + if (isNot(notBody)) { + // !!X => X + return ((NotPointcut)notBody).getNegatedPointcut(); + } else if (isAnd(notBody)) { + // !(X && Y) => !X || !Y + AndPointcut apc = (AndPointcut) notBody; + Pointcut newLeft = distributeNot(new NotPointcut(apc.getLeft(),npc.getStart())); + Pointcut newRight = distributeNot(new NotPointcut(apc.getRight(),npc.getStart())); + return new OrPointcut(newLeft,newRight); + } else if (isOr(notBody)) { + // !(X || Y) => !X && !Y + OrPointcut opc = (OrPointcut) notBody; + Pointcut newLeft = distributeNot(new NotPointcut(opc.getLeft(),npc.getStart())); + Pointcut newRight = distributeNot(new NotPointcut(opc.getRight(),npc.getStart())); + return new AndPointcut(newLeft,newRight); + } else { + return new NotPointcut(notBody,npc.getStart()); + } + } else if (isAnd(pc)) { + AndPointcut apc = (AndPointcut) pc; + Pointcut left = distributeNot(apc.getLeft()); + Pointcut right = distributeNot(apc.getRight()); + return new AndPointcut(left,right); + } else if (isOr(pc)) { + OrPointcut opc = (OrPointcut) pc; + Pointcut left = distributeNot(opc.getLeft()); + Pointcut right = distributeNot(opc.getRight()); + return new OrPointcut(left,right); + } else { + return pc; + } + } + + // A && (B || C) => (A && B) || (A && C) + // (A || B) && C => (A && C) || (B && C) + private Pointcut pullUpDisjunctions(Pointcut pc) { + if (isNot(pc)) { + NotPointcut npc = (NotPointcut)pc; + return new NotPointcut(pullUpDisjunctions(npc.getNegatedPointcut())); + } else if (isAnd(pc)) { + AndPointcut apc = (AndPointcut) pc; + // dive into left and right here... + Pointcut left = pullUpDisjunctions(apc.getLeft()); + Pointcut right = pullUpDisjunctions(apc.getRight()); + if (isOr(left) && !isOr(right)) { + // (A || B) && C => (A && C) || (B && C) + Pointcut leftLeft = ((OrPointcut)left).getLeft(); + Pointcut leftRight = ((OrPointcut)left).getRight(); + return pullUpDisjunctions( + new OrPointcut( + new AndPointcut(leftLeft,right), + new AndPointcut(leftRight,right)) + ); + } else if (isOr(right) && !isOr(left)) { + // A && (B || C) => (A && B) || (A && C) + Pointcut rightLeft = ((OrPointcut)right).getLeft(); + Pointcut rightRight = ((OrPointcut)right).getRight(); + return pullUpDisjunctions( + new OrPointcut( + new AndPointcut(left,rightLeft), + new AndPointcut(left,rightRight)) + ); + } else if (isOr(right) && isOr(left)) { + // (A || B) && (C || D) => (A && C) || (A && D) || (B && C) || (B && D) + Pointcut A = pullUpDisjunctions(((OrPointcut)left).getLeft()); + Pointcut B = pullUpDisjunctions(((OrPointcut)left).getRight()); + Pointcut C = pullUpDisjunctions(((OrPointcut)right).getLeft()); + Pointcut D = pullUpDisjunctions(((OrPointcut)right).getRight()); + Pointcut newLeft = new OrPointcut( + new AndPointcut(A,C), + new AndPointcut(A,D) + ); + Pointcut newRight = new OrPointcut( + new AndPointcut(B,C), + new AndPointcut(B,D) + ); + return pullUpDisjunctions(new OrPointcut(newLeft,newRight)); + } else { + return new AndPointcut(left,right); + } + } else if (isOr(pc)){ + OrPointcut opc = (OrPointcut) pc; + return new OrPointcut(pullUpDisjunctions(opc.getLeft()), + pullUpDisjunctions(opc.getRight())); + } else { + return pc; + } + } + + /** + * Returns a NOTted form of the pointcut p - we cope with already NOTted pointcuts. + */ + public Pointcut not(Pointcut p) { + if (isNot(p)) { + return ((NotPointcut)p).getNegatedPointcut(); + } + return new NotPointcut(p); + } + + /** + * Passed an array of pointcuts, returns an AND tree with them in. + */ + public Pointcut createAndsFor(Pointcut[] ps) { + if (ps.length==1) return ps[0]; // dumb case + if (ps.length==2) { // recursion exit case + return new AndPointcut(ps[0],ps[1]); + } + // otherwise ... + Pointcut[] subset = new Pointcut[ps.length-1]; + for (int i = 1; i < ps.length; i++) { + subset[i-1]=ps[i]; + } + return new AndPointcut(ps[0],createAndsFor(subset)); + } + + + // NOT: execution(* TP.*(..)) => within(TP) && execution(* *(..)) + // since this breaks when the pattern matches an interface + // NOT: withincode(* TP.*(..)) => within(TP) && withincode(* *(..)) + // since this is not correct when an aspect makes an ITD +// private Pointcut splitOutWithins(Pointcut pc) { +// if (isExecution(pc)) { +// KindedPointcut kpc = (KindedPointcut) pc; +// SignaturePattern sp = kpc.signature; +// TypePattern within = sp.getDeclaringType(); +// if (isAnyType(within)) return pc; +// SignaturePattern simplified = removeDeclaringTypePattern(sp); +// return new AndPointcut(new WithinPointcut(within), +// new KindedPointcut(kpc.kind,simplified)); +// } else if (isNot(pc)) { +// return new NotPointcut(splitOutWithins(((NotPointcut)pc).getNegatedPointcut())); +// } else if (isAnd(pc)) { +// AndPointcut apc = (AndPointcut) pc; +// return new AndPointcut(splitOutWithins(apc.getLeft()), +// splitOutWithins(apc.getRight())); +// } else if (isOr(pc)) { +// OrPointcut opc = (OrPointcut) pc; +// return new OrPointcut(splitOutWithins(opc.getLeft()), +// splitOutWithins(opc.getRight())); +// } else { +// return pc; +// } +// } + +// private SignaturePattern removeDeclaringTypePattern(SignaturePattern sp) { +// return new SignaturePattern( +// sp.getKind(), +// sp.getModifiers(), +// sp.getReturnType(), +// TypePattern.ANY, +// sp.getName(), +// sp.getParameterTypes(), +// sp.getThrowsPattern(), +// sp.getAnnotationPattern() +// ); +// } + + // this finds the root of each && tree and then aggregates all of the branches + // into a sorted set: + // - duplicates are removed + // - A && !A is replaced by a matchesNothingPointcut + // - the kind(s) matched by the set are evaluated + // - elements are sorted by evaluation complexity + // - the result is written out with the least expensive branch leftmost + private Pointcut simplifyAnds(Pointcut pc) { + if (isNot(pc)) { + NotPointcut npc = (NotPointcut) pc; + Pointcut notBody = npc.getNegatedPointcut(); + if (isNot(notBody)) { + // !!X => X + return simplifyAnds(((NotPointcut)notBody).getNegatedPointcut()); + } else { + return new NotPointcut(simplifyAnds(npc.getNegatedPointcut())); + } + } else if (isOr(pc)) { + OrPointcut opc = (OrPointcut) pc; + return new OrPointcut(simplifyAnds(opc.getLeft()),simplifyAnds(opc.getRight())); + } else if (isAnd(pc)) { + return simplifyAnd((AndPointcut)pc); + } else { + return pc; + } + } + + private Pointcut simplifyAnd(AndPointcut apc) { + SortedSet nodes = new TreeSet(new PointcutEvaluationExpenseComparator()); + collectAndNodes(apc,nodes); + // look for A and !A, or IfFalse + for (Iterator iter = nodes.iterator(); iter.hasNext();) { + Pointcut element = (Pointcut) iter.next(); + if (element instanceof NotPointcut) { + Pointcut body = ((NotPointcut)element).getNegatedPointcut(); + if (nodes.contains(body)) return Pointcut.makeMatchesNothing(body.state); + } + if (element instanceof IfPointcut) { + if (((IfPointcut)element).alwaysFalse()) return Pointcut.makeMatchesNothing(element.state); + } + // If it can't match anything, the whole AND can't match anything + if (element.couldMatchKinds()==Shadow.NO_SHADOW_KINDS_BITS) return element; + } + if (apc.couldMatchKinds()==Shadow.NO_SHADOW_KINDS_BITS) return Pointcut.makeMatchesNothing(apc.state); + // write out with cheapest on left + Iterator iter = nodes.iterator(); + Pointcut result = (Pointcut) iter.next(); + while(iter.hasNext()) { + Pointcut right = (Pointcut) iter.next(); + result = new AndPointcut(result,right); + } + return result; + } + + private Pointcut sortOrs(Pointcut pc) { + SortedSet nodes = new TreeSet(new PointcutEvaluationExpenseComparator()); + collectOrNodes(pc,nodes); + // write out with cheapest on left + Iterator iter = nodes.iterator(); + Pointcut result = (Pointcut) iter.next(); + while(iter.hasNext()) { + Pointcut right = (Pointcut) iter.next(); + result = new OrPointcut(result,right); + } + return result; + } + + /** + * Removes MATCHES_NOTHING pointcuts + */ + private Pointcut removeNothings(Pointcut pc) { + if (isAnd(pc)) { + AndPointcut apc = (AndPointcut)pc; + Pointcut right = removeNothings(apc.getRight()); + Pointcut left = removeNothings(apc.getLeft()); + if (left instanceof MatchesNothingPointcut || right instanceof MatchesNothingPointcut) return new MatchesNothingPointcut(); + return new AndPointcut(left,right); + } else if (isOr(pc)) { + OrPointcut opc = (OrPointcut)pc; + Pointcut right = removeNothings(opc.getRight()); + Pointcut left = removeNothings(opc.getLeft()); + if (left instanceof MatchesNothingPointcut + && !(right instanceof MatchesNothingPointcut)) { + return right; + } else if (right instanceof MatchesNothingPointcut + && !(left instanceof MatchesNothingPointcut)) { + return left; + } else if (!(left instanceof MatchesNothingPointcut) + && !(right instanceof MatchesNothingPointcut)) { + return new OrPointcut(left,right); + } else if (left instanceof MatchesNothingPointcut + && right instanceof MatchesNothingPointcut) { + return new MatchesNothingPointcut(); + } + } + return pc; + } + + private void collectAndNodes(AndPointcut apc,Set nodesSoFar) { + Pointcut left = apc.getLeft(); + Pointcut right = apc.getRight(); + if (isAnd(left)) { + collectAndNodes((AndPointcut)left,nodesSoFar); + } else { + nodesSoFar.add(left); + } + if (isAnd(right)) { + collectAndNodes((AndPointcut)right,nodesSoFar); + } else { + nodesSoFar.add(right); + } + } + + private void collectOrNodes(Pointcut pc, Set nodesSoFar) { + if (isOr(pc)) { + OrPointcut opc = (OrPointcut) pc; + collectOrNodes(opc.getLeft(),nodesSoFar); + collectOrNodes(opc.getRight(),nodesSoFar); + } else { + nodesSoFar.add(pc); + } + } + + private boolean isNot(Pointcut pc) { + return (pc instanceof NotPointcut); + } + + private boolean isAnd(Pointcut pc) { + return (pc instanceof AndPointcut); + } + + private boolean isOr(Pointcut pc) { + return (pc instanceof OrPointcut); + } + +} \ No newline at end of file diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ReferencePointcut.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ReferencePointcut.java new file mode 100644 index 000000000..6ac7d0f97 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ReferencePointcut.java @@ -0,0 +1,407 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.Map; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedPointcutDefinition; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.ShadowMunger; +import org.aspectj.weaver.TypeVariable; +import org.aspectj.weaver.TypeVariableReference; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Test; + +/** + */ + +//XXX needs check that arguments contains no WildTypePatterns +public class ReferencePointcut extends Pointcut { + public UnresolvedType onType; + public TypePattern onTypeSymbolic; + public String name; + public TypePatternList arguments; + + /** + * if this is non-null then when the pointcut is concretized the result will be parameterized too. + */ + private Map typeVariableMap; + + //public ResolvedPointcut binding; + + public ReferencePointcut(TypePattern onTypeSymbolic, String name, TypePatternList arguments) { + this.onTypeSymbolic = onTypeSymbolic; + this.name = name; + this.arguments = arguments; + this.pointcutKind = REFERENCE; + } + + public ReferencePointcut(UnresolvedType onType, String name, TypePatternList arguments) { + this.onType = onType; + this.name = name; + this.arguments = arguments; + this.pointcutKind = REFERENCE; + } + + public int couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS_BITS; + } + + + //??? do either of these match methods make any sense??? + public FuzzyBoolean fastMatch(FastMatchInfo type) { + return FuzzyBoolean.MAYBE; + } + + /** + * Do I really match this shadow? + */ + protected FuzzyBoolean matchInternal(Shadow shadow) { + return FuzzyBoolean.NO; + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + if (onType != null) { + buf.append(onType); + buf.append("."); +// for (int i=0, len=fromType.length; i < len; i++) { +// buf.append(fromType[i]); +// buf.append("."); +// } + } + buf.append(name); + buf.append(arguments.toString()); + return buf.toString(); + } + + + public void write(DataOutputStream s) throws IOException { + //XXX ignores onType + s.writeByte(Pointcut.REFERENCE); + if (onType != null) { + s.writeBoolean(true); + onType.write(s); + } else { + s.writeBoolean(false); + } + + s.writeUTF(name); + arguments.write(s); + writeLocation(s); + } + + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + UnresolvedType onType = null; + if (s.readBoolean()) { + onType = UnresolvedType.read(s); + } + ReferencePointcut ret = new ReferencePointcut(onType, s.readUTF(), + TypePatternList.read(s, context)); + ret.readLocation(context, s); + return ret; + } + + public void resolveBindings(IScope scope, Bindings bindings) { + if (onTypeSymbolic != null) { + onType = onTypeSymbolic.resolveExactType(scope, bindings); + // in this case we've already signalled an error + if (ResolvedType.isMissing(onType)) return; + } + + ResolvedType searchType; + if (onType != null) { + searchType = scope.getWorld().resolve(onType); + } else { + searchType = scope.getEnclosingType(); + } + if (searchType.isTypeVariableReference()) { + searchType = ((TypeVariableReference)searchType).getTypeVariable().getUpperBound().resolve(scope.getWorld()); + } + + + arguments.resolveBindings(scope, bindings, true, true); + //XXX ensure that arguments has no ..'s in it + + // check that I refer to a real pointcut declaration and that I match + + ResolvedPointcutDefinition pointcutDef = searchType.findPointcut(name); + // if we're not a static reference, then do a lookup of outers + if (pointcutDef == null && onType == null) { + while (true) { + UnresolvedType declaringType = searchType.getDeclaringType(); + if (declaringType == null) break; + searchType = declaringType.resolve(scope.getWorld()); + pointcutDef = searchType.findPointcut(name); + if (pointcutDef != null) { + // make this a static reference + onType = searchType; + break; + } + } + } + + if (pointcutDef == null) { + scope.message(IMessage.ERROR, this, "can't find referenced pointcut " + name); + return; + } + + // check visibility + if (!pointcutDef.isVisible(scope.getEnclosingType())) { + scope.message(IMessage.ERROR, this, "pointcut declaration " + pointcutDef + " is not accessible"); + return; + } + + if (Modifier.isAbstract(pointcutDef.getModifiers())) { + if (onType != null && !onType.isTypeVariableReference()) { + scope.message(IMessage.ERROR, this, + "can't make static reference to abstract pointcut"); + return; + } else if (!searchType.isAbstract()) { + scope.message(IMessage.ERROR, this, + "can't use abstract pointcut in concrete context"); + return; + } + } + + + ResolvedType[] parameterTypes = + scope.getWorld().resolve(pointcutDef.getParameterTypes()); + + if (parameterTypes.length != arguments.size()) { + scope.message(IMessage.ERROR, this, "incompatible number of arguments to pointcut, expected " + + parameterTypes.length + " found " + arguments.size()); + return; + } + + //if (onType == null) onType = pointcutDef.getDeclaringType(); + if (onType != null) { + if (onType.isParameterizedType()) { + // build a type map mapping type variable names in the generic type to + // the type parameters presented + typeVariableMap = new HashMap(); + ResolvedType underlyingGenericType = ((ResolvedType) onType).getGenericType(); + TypeVariable[] tVars = underlyingGenericType.getTypeVariables(); + ResolvedType[] typeParams = ((ResolvedType)onType).getResolvedTypeParameters(); + for (int i = 0; i < tVars.length; i++) { + typeVariableMap.put(tVars[i].getName(),typeParams[i]); + } + } else if (onType.isGenericType()) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.CANT_REFERENCE_POINTCUT_IN_RAW_TYPE), + getSourceLocation())); + } + } + + for (int i=0,len=arguments.size(); i < len; i++) { + TypePattern p = arguments.get(i); + //we are allowed to bind to pointcuts which use subtypes as this is type safe + if (typeVariableMap != null) { + p = p.parameterizeWith(typeVariableMap,scope.getWorld()); + } + if (p == TypePattern.NO) { + scope.message(IMessage.ERROR, this, + "bad parameter to pointcut reference"); + return; + } + + boolean reportProblem = false; + if (parameterTypes[i].isTypeVariableReference() && p.getExactType().isTypeVariableReference()) { + UnresolvedType One = ((TypeVariableReference)parameterTypes[i]).getTypeVariable().getFirstBound(); + UnresolvedType Two = ((TypeVariableReference)p.getExactType()).getTypeVariable().getFirstBound(); + reportProblem = !One.resolve(scope.getWorld()).isAssignableFrom(Two.resolve(scope.getWorld())); + } else { + reportProblem = !p.matchesSubtypes(parameterTypes[i]) && + !p.getExactType().equals(UnresolvedType.OBJECT); + } + if (reportProblem) { + scope.message(IMessage.ERROR, this, "incompatible type, expected " + + parameterTypes[i].getName() + " found " + p +". Check the type specified in your pointcut"); + return; + } + } + + } + + public void postRead(ResolvedType enclosingType) { + arguments.postRead(enclosingType); + } + + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + throw new RuntimeException("shouldn't happen"); + } + + + //??? This is not thread safe, but this class is not designed for multi-threading + private boolean concretizing = false; + // declaring type is the type that declared the member referencing this pointcut. + // If it declares a matching private pointcut, then that pointcut should be used + // and not one in a subtype that happens to have the same name. + public Pointcut concretize1(ResolvedType searchStart, ResolvedType declaringType, IntMap bindings) { + if (concretizing) { + //Thread.currentThread().dumpStack(); + searchStart.getWorld().getMessageHandler().handleMessage( + MessageUtil.error(WeaverMessages.format(WeaverMessages.CIRCULAR_POINTCUT,this), + getSourceLocation())); + return Pointcut.makeMatchesNothing(Pointcut.CONCRETE); + } + + try { + concretizing = true; + + ResolvedPointcutDefinition pointcutDec; + if (onType != null) { + searchStart = onType.resolve(searchStart.getWorld()); + if (searchStart.isMissing()) { + return Pointcut.makeMatchesNothing(Pointcut.CONCRETE); + } + + if (onType.isTypeVariableReference()) { + // need to replace on type with the binding for the type variable + // in the declaring type + if (declaringType.isParameterizedType()) { + TypeVariable[] tvs = declaringType.getGenericType().getTypeVariables(); + String typeVariableName = ((TypeVariableReference)onType).getTypeVariable().getName(); + for (int i = 0; i < tvs.length; i++) { + if (tvs[i].getName().equals(typeVariableName)) { + ResolvedType realOnType = declaringType.getTypeParameters()[i].resolve(declaringType.getWorld()); + onType = realOnType; + searchStart = realOnType; + break; + } + } + } + } + + } + + if (declaringType == null) declaringType = searchStart; + pointcutDec = declaringType.findPointcut(name); + boolean foundMatchingPointcut = (pointcutDec != null && pointcutDec.isPrivate()); + if (!foundMatchingPointcut) { + pointcutDec = searchStart.findPointcut(name); + if (pointcutDec == null) { + searchStart.getWorld().getMessageHandler().handleMessage( + MessageUtil.error(WeaverMessages.format(WeaverMessages.CANT_FIND_POINTCUT,name,searchStart.getName()), + getSourceLocation()) + ); + return Pointcut.makeMatchesNothing(Pointcut.CONCRETE); + } + } + + if (pointcutDec.isAbstract()) { + //Thread.currentThread().dumpStack(); + ShadowMunger enclosingAdvice = bindings.getEnclosingAdvice(); + searchStart.getWorld().showMessage(IMessage.ERROR, + WeaverMessages.format(WeaverMessages.ABSTRACT_POINTCUT,pointcutDec), + getSourceLocation(), + (null == enclosingAdvice) ? null : enclosingAdvice.getSourceLocation()); + return Pointcut.makeMatchesNothing(Pointcut.CONCRETE); + } + + //System.err.println("start: " + searchStart); +// ResolvedType[] parameterTypes = searchStart.getWorld().resolve(pointcutDec.getParameterTypes()); + + TypePatternList arguments = this.arguments.resolveReferences(bindings); + + IntMap newBindings = new IntMap(); + for (int i=0,len=arguments.size(); i < len; i++) { + TypePattern p = arguments.get(i); + if (p == TypePattern.NO) continue; + // we are allowed to bind to pointcuts which use subtypes as this is type safe + // this will be checked in ReferencePointcut.resolveBindings(). Can't check it here + // as we don't know about any new parents added via decp. + if (p instanceof BindingTypePattern) { + newBindings.put(i, ((BindingTypePattern)p).getFormalIndex()); + } + } + + if (searchStart.isParameterizedType()) { + // build a type map mapping type variable names in the generic type to + // the type parameters presented + typeVariableMap = new HashMap(); + ResolvedType underlyingGenericType = searchStart.getGenericType(); + TypeVariable[] tVars = underlyingGenericType.getTypeVariables(); + ResolvedType[] typeParams = searchStart.getResolvedTypeParameters(); + for (int i = 0; i < tVars.length; i++) { + typeVariableMap.put(tVars[i].getName(),typeParams[i]); + } + } + + newBindings.copyContext(bindings); + newBindings.pushEnclosingDefinition(pointcutDec); + try { + Pointcut ret = pointcutDec.getPointcut(); + if (typeVariableMap != null && !hasBeenParameterized) { + ret = ret.parameterizeWith(typeVariableMap,searchStart.getWorld()); + ret.hasBeenParameterized=true; + } + return ret.concretize(searchStart, declaringType, newBindings); + } finally { + newBindings.popEnclosingDefinitition(); + } + + } finally { + concretizing = false; + } + } + + /** + * make a version of this pointcut with any refs to typeVariables replaced by their entry in the map. + * Tricky thing is, we can't do this at the point in time this method will be called, so we make a + * version that will parameterize the pointcut it ultimately resolves to. + */ + public Pointcut parameterizeWith(Map typeVariableMap,World w) { + ReferencePointcut ret = new ReferencePointcut(onType,name,arguments); + ret.onTypeSymbolic = onTypeSymbolic; + ret.typeVariableMap = typeVariableMap; + return ret; + } + + // We want to keep the original source location, not the reference location + protected boolean shouldCopyLocationForConcretize() { + return false; + } + + public boolean equals(Object other) { + if (!(other instanceof ReferencePointcut)) return false; + if (this == other) return true; + ReferencePointcut o = (ReferencePointcut)other; + return o.name.equals(name) && o.arguments.equals(arguments) + && ((o.onType == null) ? (onType == null) : o.onType.equals(onType)); + } + public int hashCode() { + int result = 17; + result = 37*result + ((onType == null) ? 0 : onType.hashCode()); + result = 37*result + arguments.hashCode(); + result = 37*result + name.hashCode(); + return result; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ScopeWithTypeVariables.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ScopeWithTypeVariables.java new file mode 100644 index 000000000..537fb2c22 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ScopeWithTypeVariables.java @@ -0,0 +1,130 @@ +/* ******************************************************************* + * 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.patterns; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.IMessageHandler; +import org.aspectj.bridge.IMessage.Kind; +import org.aspectj.weaver.IHasPosition; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.TypeVariable; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.UnresolvedTypeVariableReferenceType; +import org.aspectj.weaver.World; + +/** + * A scope that also considers type variables when looking up a type. + * + */ +public class ScopeWithTypeVariables implements IScope { + + private IScope delegateScope; + private String[] typeVariableNames; + private UnresolvedTypeVariableReferenceType[] typeVarTypeXs; + + public ScopeWithTypeVariables(String[] typeVarNames, IScope delegate) { + this.delegateScope = delegate; + this.typeVariableNames = typeVarNames; + this.typeVarTypeXs = new UnresolvedTypeVariableReferenceType[typeVarNames.length]; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.IScope#lookupType(java.lang.String, org.aspectj.weaver.IHasPosition) + */ + public UnresolvedType lookupType(String name, IHasPosition location) { + for (int i = 0; i < typeVariableNames.length; i++) { + if (typeVariableNames[i].equals(name)) { + if (typeVarTypeXs[i] == null) { + typeVarTypeXs[i] = new UnresolvedTypeVariableReferenceType(new TypeVariable(name)); + } + return typeVarTypeXs[i]; + } + } + return delegateScope.lookupType(name, location); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.IScope#getWorld() + */ + public World getWorld() { + return delegateScope.getWorld(); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.IScope#getEnclosingType() + */ + public ResolvedType getEnclosingType() { + return delegateScope.getEnclosingType(); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.IScope#getMessageHandler() + */ + public IMessageHandler getMessageHandler() { + return delegateScope.getMessageHandler(); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.IScope#lookupFormal(java.lang.String) + */ + public FormalBinding lookupFormal(String name) { + return delegateScope.lookupFormal(name); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.IScope#getFormal(int) + */ + public FormalBinding getFormal(int i) { + return delegateScope.getFormal(i); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.IScope#getFormalCount() + */ + public int getFormalCount() { + return delegateScope.getFormalCount(); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.IScope#getImportedPrefixes() + */ + public String[] getImportedPrefixes() { + return delegateScope.getImportedPrefixes(); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.IScope#getImportedNames() + */ + public String[] getImportedNames() { + return delegateScope.getImportedNames(); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.IScope#message(org.aspectj.bridge.IMessage.Kind, org.aspectj.weaver.IHasPosition, java.lang.String) + */ + public void message(Kind kind, IHasPosition location, String message) { + delegateScope.message(kind, location, message); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.IScope#message(org.aspectj.bridge.IMessage.Kind, org.aspectj.weaver.IHasPosition, org.aspectj.weaver.IHasPosition, java.lang.String) + */ + public void message(Kind kind, IHasPosition location1, + IHasPosition location2, String message) { + delegateScope.message(kind,location1,location2,message); + } + + public void message(IMessage aMessage) { + delegateScope.message(aMessage); + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/SignaturePattern.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/SignaturePattern.java new file mode 100644 index 000000000..0643b480c --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/SignaturePattern.java @@ -0,0 +1,754 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.AjAttribute; +import org.aspectj.weaver.AjcMemberMaker; +import org.aspectj.weaver.AnnotationTargetKind; +import org.aspectj.weaver.ConcreteTypeMunger; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.JoinPointSignature; +import org.aspectj.weaver.Member; +import org.aspectj.weaver.MemberKind; +import org.aspectj.weaver.NewFieldTypeMunger; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; + + +public class SignaturePattern extends PatternNode { + private MemberKind kind; + private ModifiersPattern modifiers; + private TypePattern returnType; + private TypePattern declaringType; + private NamePattern name; + private TypePatternList parameterTypes; + private ThrowsPattern throwsPattern; + private AnnotationTypePattern annotationPattern; + private transient int hashcode = -1; + + public SignaturePattern(MemberKind kind, ModifiersPattern modifiers, + TypePattern returnType, TypePattern declaringType, + NamePattern name, TypePatternList parameterTypes, + ThrowsPattern throwsPattern, + AnnotationTypePattern annotationPattern) { + this.kind = kind; + this.modifiers = modifiers; + this.returnType = returnType; + this.name = name; + this.declaringType = declaringType; + this.parameterTypes = parameterTypes; + this.throwsPattern = throwsPattern; + this.annotationPattern = annotationPattern; + } + + + public SignaturePattern resolveBindings(IScope scope, Bindings bindings) { + if (returnType != null) { + returnType = returnType.resolveBindings(scope, bindings, false, false); + checkForIncorrectTargetKind(returnType,scope,false); + } + if (declaringType != null) { + declaringType = declaringType.resolveBindings(scope, bindings, false, false); + checkForIncorrectTargetKind(declaringType,scope,false); + } + if (parameterTypes != null) { + parameterTypes = parameterTypes.resolveBindings(scope, bindings, false, false); + checkForIncorrectTargetKind(parameterTypes,scope,false,true); + } + if (throwsPattern != null) { + throwsPattern = throwsPattern.resolveBindings(scope, bindings); + if (throwsPattern.getForbidden().getTypePatterns().length > 0 + || throwsPattern.getRequired().getTypePatterns().length > 0) { + checkForIncorrectTargetKind(throwsPattern,scope,false); + } + } + if (annotationPattern != null) { + annotationPattern = annotationPattern.resolveBindings(scope,bindings,false); + checkForIncorrectTargetKind(annotationPattern,scope,true); + } + hashcode =-1; + return this; + } + private void checkForIncorrectTargetKind(PatternNode patternNode, IScope scope, boolean targetsOtherThanTypeAllowed) { + checkForIncorrectTargetKind(patternNode, scope, targetsOtherThanTypeAllowed, false); + + } + + // bug 115252 - adding an xlint warning if the annnotation target type is + // wrong. This logic, or similar, may have to be applied elsewhere in the case + // of pointcuts which don't go through SignaturePattern.resolveBindings(..) + private void checkForIncorrectTargetKind(PatternNode patternNode, IScope scope, boolean targetsOtherThanTypeAllowed, boolean parameterTargettingAnnotationsAllowed) { + // return if we're not in java5 mode, if the unmatchedTargetKind Xlint + // warning has been turned off, or if the patternNode is * + if (!scope.getWorld().isInJava5Mode() + || scope.getWorld().getLint().unmatchedTargetKind == null + || (patternNode instanceof AnyTypePattern)) { + return; + } + if (patternNode instanceof ExactAnnotationTypePattern) { + ResolvedType resolvedType = ((ExactAnnotationTypePattern)patternNode).getAnnotationType().resolve(scope.getWorld()); + if (targetsOtherThanTypeAllowed) { + AnnotationTargetKind[] targetKinds = resolvedType.getAnnotationTargetKinds(); + if (targetKinds == null) return; + reportUnmatchedTargetKindMessage(targetKinds,patternNode,scope,true); + } else if (!targetsOtherThanTypeAllowed && !resolvedType.canAnnotationTargetType()) { + // everything is incorrect since we've already checked whether we have the TYPE target annotation + AnnotationTargetKind[] targetKinds = resolvedType.getAnnotationTargetKinds(); + if (targetKinds == null) return; + reportUnmatchedTargetKindMessage(targetKinds,patternNode,scope,false); + } + } else { + TypePatternVisitor visitor = new TypePatternVisitor(scope,targetsOtherThanTypeAllowed,parameterTargettingAnnotationsAllowed); + patternNode.traverse(visitor,null); + if (visitor.containedIncorrectTargetKind()) { + Set keys = visitor.getIncorrectTargetKinds().keySet(); + for (Iterator iter = keys.iterator(); iter.hasNext();) { + PatternNode node = (PatternNode)iter.next(); + AnnotationTargetKind[] targetKinds = (AnnotationTargetKind[]) visitor.getIncorrectTargetKinds().get(node); + reportUnmatchedTargetKindMessage(targetKinds,node,scope,false); + } + } + } + } + + private void reportUnmatchedTargetKindMessage( + AnnotationTargetKind[] annotationTargetKinds, + PatternNode node, + IScope scope, + boolean checkMatchesMemberKindName) { + StringBuffer targetNames = new StringBuffer("{"); + for (int i = 0; i < annotationTargetKinds.length; i++) { + AnnotationTargetKind targetKind = annotationTargetKinds[i]; + if (checkMatchesMemberKindName && kind.getName().equals(targetKind.getName())) { + return; + } + if (i < (annotationTargetKinds.length - 1)) { + targetNames.append("ElementType." + targetKind.getName() + ","); + } else { + targetNames.append("ElementType." + targetKind.getName() + "}"); + } + } + scope.getWorld().getLint().unmatchedTargetKind.signal(new String[] {node.toString(),targetNames.toString()}, getSourceLocation(), new ISourceLocation[0]); + } + + /** + * Class which visits the nodes in the TypePattern tree until an + * ExactTypePattern is found. Once this is found it creates a new + * ExactAnnotationTypePattern and checks whether the targetKind + * (created via the @Target annotation) matches ElementType.TYPE if + * this is the only target kind which is allowed, or matches the + * signature pattern kind if there is no restriction. + */ + private class TypePatternVisitor extends AbstractPatternNodeVisitor { + + private IScope scope; + private Map incorrectTargetKinds /* PatternNode -> AnnotationTargetKind[] */ = new HashMap(); + private boolean targetsOtherThanTypeAllowed; + private boolean parameterTargettingAnnotationsAllowed; + + /** + * @param requiredTarget - the signature pattern Kind + * @param scope + * @param parameterTargettingAnnotationsAllowed + */ + public TypePatternVisitor(IScope scope, boolean targetsOtherThanTypeAllowed, boolean parameterTargettingAnnotationsAllowed) { + this.scope = scope; + this.targetsOtherThanTypeAllowed = targetsOtherThanTypeAllowed; + this.parameterTargettingAnnotationsAllowed = parameterTargettingAnnotationsAllowed; + } + + public Object visit(WildAnnotationTypePattern node, Object data) { + node.getTypePattern().accept(this,data); + return node; + } + + /** + * Do the ExactAnnotationTypePatterns have the incorrect target? + */ + public Object visit(ExactAnnotationTypePattern node, Object data) { + ResolvedType resolvedType = node.getAnnotationType().resolve(scope.getWorld()); + if (targetsOtherThanTypeAllowed) { + AnnotationTargetKind[] targetKinds = resolvedType.getAnnotationTargetKinds(); + if (targetKinds == null) return data; + List incorrectTargets = new ArrayList(); + for (int i = 0; i < targetKinds.length; i++) { + if (targetKinds[i].getName().equals(kind.getName()) || + (targetKinds[i].getName().equals("PARAMETER") && node.isForParameterAnnotationMatch()) + ) { + return data; + } + incorrectTargets.add(targetKinds[i]); + } + if (incorrectTargets.isEmpty()) return data; + AnnotationTargetKind[] kinds = new AnnotationTargetKind[incorrectTargets.size()]; + incorrectTargetKinds.put(node,(AnnotationTargetKind[]) incorrectTargets.toArray(kinds)); + } else if (!targetsOtherThanTypeAllowed && !resolvedType.canAnnotationTargetType()) { + AnnotationTargetKind[] targetKinds = resolvedType.getAnnotationTargetKinds(); + if (targetKinds == null) return data; + // exception here is if parameter annotations are allowed + if (parameterTargettingAnnotationsAllowed) { + for (int i = 0; i < targetKinds.length; i++) { + AnnotationTargetKind annotationTargetKind = targetKinds[i]; + if (annotationTargetKind.getName().equals("PARAMETER") && node.isForParameterAnnotationMatch()) return data; + } + } + incorrectTargetKinds.put(node,targetKinds); + } + return data; + } + + public Object visit(ExactTypePattern node, Object data) { + ExactAnnotationTypePattern eatp = new ExactAnnotationTypePattern(node.getExactType().resolve(scope.getWorld()),null); + eatp.accept(this,data); + return data; + } + + public Object visit(AndTypePattern node, Object data) { + node.getLeft().accept(this,data); + node.getRight().accept(this,data); + return node; + } + + public Object visit(OrTypePattern node, Object data) { + node.getLeft().accept(this,data); + node.getRight().accept(this,data); + return node; + } + + public Object visit(AnyWithAnnotationTypePattern node, Object data) { + node.getAnnotationPattern().accept(this,data); + return node; + } + + public boolean containedIncorrectTargetKind() { + return (incorrectTargetKinds.size() != 0); + } + + public Map getIncorrectTargetKinds() { + return incorrectTargetKinds; + } + } + + + public void postRead(ResolvedType enclosingType) { + if (returnType != null) { + returnType.postRead(enclosingType); + } + if (declaringType != null) { + declaringType.postRead(enclosingType); + } + if (parameterTypes != null) { + parameterTypes.postRead(enclosingType); + } + } + + /** + * return a copy of this signature pattern in which every type variable reference + * is replaced by the corresponding entry in the map. + */ + public SignaturePattern parameterizeWith(Map typeVariableMap,World w) { + SignaturePattern ret = new SignaturePattern( + kind, + modifiers, + returnType.parameterizeWith(typeVariableMap,w), + declaringType.parameterizeWith(typeVariableMap,w), + name, + parameterTypes.parameterizeWith(typeVariableMap,w), + throwsPattern.parameterizeWith(typeVariableMap,w), + annotationPattern.parameterizeWith(typeVariableMap,w)); + ret.copyLocationFrom(this); + return ret; + } + + public boolean matches(Member joinPointSignature, World world, boolean allowBridgeMethods) { + // fail (or succeed!) fast tests... + if (joinPointSignature == null) return false; + if (kind != joinPointSignature.getKind()) return false; + if (kind == Member.ADVICE) return true; + + // do the hard work then... + boolean subjectMatch = true; + Iterator candidateMatches = joinPointSignature.getJoinPointSignatures(world); + while(candidateMatches.hasNext()) { + JoinPointSignature aSig = (JoinPointSignature) candidateMatches.next(); + FuzzyBoolean matchResult = matchesExactly(aSig,world,allowBridgeMethods,subjectMatch); + if (matchResult.alwaysTrue()) { + return true; + } else if (matchResult.alwaysFalse()) { + return false; + } + // if we got a "MAYBE" it's worth looking at the other signatures + subjectMatch = false; + } + return false; + } + + // Does this pattern match this exact signature (no declaring type mucking about + // or chasing up the hierarchy) + // return YES if it does, NO if it doesn't and no ancester member could match either, + // and MAYBE if it doesn't but an ancester member could. + private FuzzyBoolean matchesExactly(JoinPointSignature aMember, World inAWorld, boolean allowBridgeMethods,boolean subjectMatch) { + // Java5 introduces bridge methods, we match a call to them but nothing else... + if (aMember.isBridgeMethod() && !allowBridgeMethods) { + return FuzzyBoolean.MAYBE; + } + + // modifiers match on the *subject* + if (subjectMatch && !modifiers.matches(aMember.getModifiers())) { + return FuzzyBoolean.NO; +// if (aMember.isPrivate()) return FuzzyBoolean.NO; +// else return FuzzyBoolean.MAYBE; + } + + + FuzzyBoolean matchesIgnoringAnnotations = FuzzyBoolean.YES; + if (kind == Member.STATIC_INITIALIZATION) { + matchesIgnoringAnnotations = matchesExactlyStaticInitialization(aMember, inAWorld); + } else if (kind == Member.FIELD) { + matchesIgnoringAnnotations = matchesExactlyField(aMember,inAWorld); + } else if (kind == Member.METHOD) { + matchesIgnoringAnnotations = matchesExactlyMethod(aMember,inAWorld, subjectMatch); + } else if (kind == Member.CONSTRUCTOR) { + matchesIgnoringAnnotations = matchesExactlyConstructor(aMember, inAWorld); + } + if (matchesIgnoringAnnotations.alwaysFalse()) return FuzzyBoolean.NO; + + // why did Adrian put this comment in: + // annotations match on the *subject* + // surely you never want execution(@Something * *(..)) to match something just because + // it is a secondary join point signature for a join point and doesn't have the annotation? + // pr239441 + if (matchesIgnoringAnnotations.alwaysTrue()) { + return matchesAnnotations(aMember,inAWorld); + } else { + return matchesIgnoringAnnotations; + } + + } + + /** + * Matches on declaring type + */ + private FuzzyBoolean matchesExactlyStaticInitialization(JoinPointSignature aMember,World world) { + return FuzzyBoolean.fromBoolean(declaringType.matchesStatically(aMember.getDeclaringType().resolve(world))); + } + + /** + * Matches on name, declaring type, field type + */ + private FuzzyBoolean matchesExactlyField(JoinPointSignature aField, World world) { + if (!name.matches(aField.getName())) return FuzzyBoolean.NO; + ResolvedType fieldDeclaringType = aField.getDeclaringType().resolve(world); + if (!declaringType.matchesStatically(fieldDeclaringType)) { + return FuzzyBoolean.MAYBE; + } + if (!returnType.matchesStatically(aField.getReturnType().resolve(world))) { + // looking bad, but there might be parameterization to consider... + if (!returnType.matchesStatically(aField.getGenericReturnType().resolve(world))) { + // ok, it's bad. + return FuzzyBoolean.MAYBE; + } + } + // passed all the guards... + return FuzzyBoolean.YES; + } + + /** + * Matches on name, declaring type, return type, parameter types, throws types + */ + private FuzzyBoolean matchesExactlyMethod(JoinPointSignature aMethod, World world, boolean subjectMatch) { + if (!name.matches(aMethod.getName())) return FuzzyBoolean.NO; + // Check the throws pattern + if (subjectMatch && !throwsPattern.matches(aMethod.getExceptions(), world)) return FuzzyBoolean.NO; + + if (!declaringType.matchesStatically(aMethod.getDeclaringType().resolve(world))) return FuzzyBoolean.MAYBE; + if (!returnType.matchesStatically(aMethod.getReturnType().resolve(world))) { + // looking bad, but there might be parameterization to consider... + if (!returnType.matchesStatically(aMethod.getGenericReturnType().resolve(world))) { + // ok, it's bad. + return FuzzyBoolean.MAYBE; + } + } + if (!parameterTypes.canMatchSignatureWithNParameters(aMethod.getParameterTypes().length)) return FuzzyBoolean.NO; + ResolvedType[] resolvedParameters = world.resolve(aMethod.getParameterTypes()); + ResolvedType[][] parameterAnnotationTypes = aMethod.getParameterAnnotationTypes(); + if (parameterAnnotationTypes==null || parameterAnnotationTypes.length==0) parameterAnnotationTypes=null; + if (!parameterTypes.matches(resolvedParameters, TypePattern.STATIC,parameterAnnotationTypes).alwaysTrue()) { + // It could still be a match based on the generic sig parameter types of a parameterized type + if (!parameterTypes.matches(world.resolve(aMethod.getGenericParameterTypes()),TypePattern.STATIC,parameterAnnotationTypes).alwaysTrue()) { + return FuzzyBoolean.MAYBE; + // It could STILL be a match based on the erasure of the parameter types?? + // to be determined via test cases... + } + } + + // check that varargs specifications match + if (!matchesVarArgs(aMethod,world)) return FuzzyBoolean.MAYBE; + + // passed all the guards.. + return FuzzyBoolean.YES; + } + + + + /** + * match on declaring type, parameter types, throws types + */ + private FuzzyBoolean matchesExactlyConstructor(JoinPointSignature aConstructor, World world) { + if (!declaringType.matchesStatically(aConstructor.getDeclaringType().resolve(world))) return FuzzyBoolean.NO; + + if (!parameterTypes.canMatchSignatureWithNParameters(aConstructor.getParameterTypes().length)) return FuzzyBoolean.NO; + ResolvedType[] resolvedParameters = world.resolve(aConstructor.getParameterTypes()); + + ResolvedType[][] parameterAnnotationTypes = aConstructor.getParameterAnnotationTypes(); + + if (parameterAnnotationTypes==null || parameterAnnotationTypes.length==0) parameterAnnotationTypes=null; + + if (!parameterTypes.matches(resolvedParameters, TypePattern.STATIC,parameterAnnotationTypes).alwaysTrue()) { + // It could still be a match based on the generic sig parameter types of a parameterized type + if (!parameterTypes.matches(world.resolve(aConstructor.getGenericParameterTypes()),TypePattern.STATIC).alwaysTrue()) { + return FuzzyBoolean.MAYBE; + // It could STILL be a match based on the erasure of the parameter types?? + // to be determined via test cases... + } + } + + // check that varargs specifications match + if (!matchesVarArgs(aConstructor,world)) return FuzzyBoolean.NO; + + // Check the throws pattern + if (!throwsPattern.matches(aConstructor.getExceptions(), world)) return FuzzyBoolean.NO; + + // passed all the guards.. + return FuzzyBoolean.YES; + } + + /** + * We've matched against this method or constructor so far, but without considering + * varargs (which has been matched as a simple array thus far). Now we do the additional + * checks to see if the parties agree on whether the last parameter is varargs or a + * straight array. + */ + private boolean matchesVarArgs(JoinPointSignature aMethodOrConstructor, World inAWorld) { + if (parameterTypes.size() == 0) return true; + + TypePattern lastPattern = parameterTypes.get(parameterTypes.size()-1); + boolean canMatchVarArgsSignature = lastPattern.isStar() || + lastPattern.isVarArgs() || + (lastPattern == TypePattern.ELLIPSIS); + + if (aMethodOrConstructor.isVarargsMethod()) { + // we have at least one parameter in the pattern list, and the method has a varargs signature + if (!canMatchVarArgsSignature) { + // XXX - Ideally the shadow would be included in the msg but we don't know it... + inAWorld.getLint().cantMatchArrayTypeOnVarargs.signal(aMethodOrConstructor.toString(),getSourceLocation()); + return false; + } + } else { + // the method ends with an array type, check that we don't *require* a varargs + if (lastPattern.isVarArgs()) return false; + } + + return true; + } + + private FuzzyBoolean matchesAnnotations(ResolvedMember member,World world) { + if (member == null) { + // world.getLint().unresolvableMember.signal(member.toString(), getSourceLocation()); + return FuzzyBoolean.NO; + } + annotationPattern.resolve(world); + + // optimization before we go digging around for annotations on ITDs + if (annotationPattern instanceof AnyAnnotationTypePattern) return FuzzyBoolean.YES; + + // fake members represent ITD'd fields - for their annotations we should go and look up the + // relevant member in the original aspect + if (member.isAnnotatedElsewhere() && member.getKind()==Member.FIELD) { + // FIXME asc duplicate of code in AnnotationPointcut.matchInternal()? same fixmes apply here. +// ResolvedMember [] mems = member.getDeclaringType().resolve(world).getDeclaredFields(); // FIXME asc should include supers with getInterTypeMungersIncludingSupers? + List mungers = member.getDeclaringType().resolve(world).getInterTypeMungers(); + for (Iterator iter = mungers.iterator(); iter.hasNext();) { + ConcreteTypeMunger typeMunger = (ConcreteTypeMunger) iter.next(); + if (typeMunger.getMunger() instanceof NewFieldTypeMunger) { + ResolvedMember fakerm = typeMunger.getSignature(); + ResolvedMember ajcMethod = AjcMemberMaker.interFieldInitializer(fakerm,typeMunger.getAspectType()); + ResolvedMember rmm = findMethod(typeMunger.getAspectType(),ajcMethod); + if (fakerm.equals(member)) { + member = rmm; + } + } + } + } + + if (annotationPattern.matches(member).alwaysTrue()) { + return FuzzyBoolean.YES; + } else { + return FuzzyBoolean.NO; // do NOT look at ancestor members... + } + } + + 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; + } + + public boolean declaringTypeMatchAllowingForCovariance(Member member, UnresolvedType shadowDeclaringType, World world,TypePattern returnTypePattern,ResolvedType sigReturn) { + + ResolvedType onType = shadowDeclaringType.resolve(world); + + // fastmatch + if (declaringType.matchesStatically(onType) && returnTypePattern.matchesStatically(sigReturn)) + return true; + + Collection declaringTypes = member.getDeclaringTypes(world); + + boolean checkReturnType = true; + // XXX Possible enhancement? Doesn't seem to speed things up + // if (returnTypePattern.isStar()) { + // if (returnTypePattern instanceof WildTypePattern) { + // if (((WildTypePattern)returnTypePattern).getDimensions()==0) checkReturnType = false; + // } + // } + + // Sometimes that list includes types that don't explicitly declare the member we are after - + // they are on the list because their supertype is on the list, that's why we use + // lookupMethod rather than lookupMemberNoSupers() + for (Iterator i = declaringTypes.iterator(); i.hasNext(); ) { + ResolvedType type = (ResolvedType)i.next(); + if (declaringType.matchesStatically(type)) { + if (!checkReturnType) return true; + ResolvedMember rm = type.lookupMethod(member); + if (rm==null) rm = type.lookupMethodInITDs(member); // It must be in here, or we have *real* problems + if (rm==null) continue; // might be currently looking at the generic type and we need to continue searching in case we hit a parameterized version of this same type... + UnresolvedType returnTypeX = rm.getReturnType(); + ResolvedType returnType = returnTypeX.resolve(world); + if (returnTypePattern.matchesStatically(returnType)) return true; + } + } + return false; + } + +// private Collection getDeclaringTypes(Signature sig) { +// List l = new ArrayList(); +// Class onType = sig.getDeclaringType(); +// String memberName = sig.getName(); +// if (sig instanceof FieldSignature) { +// Class fieldType = ((FieldSignature)sig).getFieldType(); +// Class superType = onType; +// while(superType != null) { +// try { +// Field f = (superType.getDeclaredField(memberName)); +// if (f.getType() == fieldType) { +// l.add(superType); +// } +// } catch (NoSuchFieldException nsf) {} +// superType = superType.getSuperclass(); +// } +// } else if (sig instanceof MethodSignature) { +// Class[] paramTypes = ((MethodSignature)sig).getParameterTypes(); +// Class superType = onType; +// while(superType != null) { +// try { +// superType.getDeclaredMethod(memberName,paramTypes); +// l.add(superType); +// } catch (NoSuchMethodException nsm) {} +// superType = superType.getSuperclass(); +// } +// } +// return l; +// } + + public NamePattern getName() { return name; } + public TypePattern getDeclaringType() { return declaringType; } + + public MemberKind getKind() { + return kind; + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + + if (annotationPattern != AnnotationTypePattern.ANY) { + buf.append(annotationPattern.toString()); + buf.append(' '); + } + + if (modifiers != ModifiersPattern.ANY) { + buf.append(modifiers.toString()); + buf.append(' '); + } + + if (kind == Member.STATIC_INITIALIZATION) { + buf.append(declaringType.toString()); + buf.append(".()");//FIXME AV - bad, cannot be parsed again + } else if (kind == Member.HANDLER) { + buf.append("handler("); + buf.append(parameterTypes.get(0)); + buf.append(")"); + } else { + if (!(kind == Member.CONSTRUCTOR)) { + buf.append(returnType.toString()); + buf.append(' '); + } + if (declaringType != TypePattern.ANY) { + buf.append(declaringType.toString()); + buf.append('.'); + } + if (kind == Member.CONSTRUCTOR) { + buf.append("new"); + } else { + buf.append(name.toString()); + } + if (kind == Member.METHOD || kind == Member.CONSTRUCTOR) { + buf.append(parameterTypes.toString()); + } + //FIXME AV - throws is not printed here, weird + } + return buf.toString(); + } + + public boolean equals(Object other) { + if (!(other instanceof SignaturePattern)) return false; + SignaturePattern o = (SignaturePattern)other; + return o.kind.equals(this.kind) + && o.modifiers.equals(this.modifiers) + && o.returnType.equals(this.returnType) + && o.declaringType.equals(this.declaringType) + && o.name.equals(this.name) + && o.parameterTypes.equals(this.parameterTypes) + && o.throwsPattern.equals(this.throwsPattern) + && o.annotationPattern.equals(this.annotationPattern); + } + public int hashCode() { + if (hashcode==-1) { + hashcode = 17; + hashcode = 37*hashcode + kind.hashCode(); + hashcode = 37*hashcode + modifiers.hashCode(); + hashcode = 37*hashcode + returnType.hashCode(); + hashcode = 37*hashcode + declaringType.hashCode(); + hashcode = 37*hashcode + name.hashCode(); + hashcode = 37*hashcode + parameterTypes.hashCode(); + hashcode = 37*hashcode + throwsPattern.hashCode(); + hashcode = 37*hashcode + annotationPattern.hashCode(); + } + return hashcode; + } + + public void write(DataOutputStream s) throws IOException { + kind.write(s); + modifiers.write(s); + returnType.write(s); + declaringType.write(s); + name.write(s); + parameterTypes.write(s); + throwsPattern.write(s); + annotationPattern.write(s); + writeLocation(s); + } + + public static SignaturePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + MemberKind kind = MemberKind.read(s); + ModifiersPattern modifiers = ModifiersPattern.read(s); + TypePattern returnType = TypePattern.read(s, context); + TypePattern declaringType = TypePattern.read(s, context); + NamePattern name = NamePattern.read(s); + TypePatternList parameterTypes = TypePatternList.read(s, context); + ThrowsPattern throwsPattern = ThrowsPattern.read(s, context); + + AnnotationTypePattern annotationPattern = AnnotationTypePattern.ANY; + + if (s.getMajorVersion()>=AjAttribute.WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ150) { + annotationPattern = AnnotationTypePattern.read(s,context); + } + + SignaturePattern ret = new SignaturePattern(kind, modifiers, returnType, declaringType, + name, parameterTypes, throwsPattern,annotationPattern); + ret.readLocation(context, s); + return ret; + } + + /** + * @return + */ + public ModifiersPattern getModifiers() { + return modifiers; + } + + /** + * @return + */ + public TypePatternList getParameterTypes() { + return parameterTypes; + } + + /** + * @return + */ + public TypePattern getReturnType() { + return returnType; + } + + /** + * @return + */ + public ThrowsPattern getThrowsPattern() { + return throwsPattern; + } + + /** + * return true if last argument in params is an Object[] but the modifiers say this method + * was declared with varargs (Object...). We shouldn't be matching if this is the case. + */ +// private boolean matchedArrayAgainstVarArgs(TypePatternList params,int modifiers) { +// if (params.size()>0 && (modifiers & Constants.ACC_VARARGS)!=0) { +// // we have at least one parameter in the pattern list, and the method has a varargs signature +// TypePattern lastPattern = params.get(params.size()-1); +// if (lastPattern.isArray() && !lastPattern.isVarArgs) return true; +// } +// return false; +// } + + public AnnotationTypePattern getAnnotationPattern() { + return annotationPattern; + } + + + public boolean isStarAnnotation() { + return annotationPattern == AnnotationTypePattern.ANY; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/SimpleScope.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/SimpleScope.java new file mode 100644 index 000000000..08e3e70f3 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/SimpleScope.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.patterns; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.IMessageHandler; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.Message; +import org.aspectj.bridge.SourceLocation; +import org.aspectj.weaver.IHasPosition; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.World; + +public class SimpleScope implements IScope { + + private World world; + private ResolvedType enclosingType; + protected FormalBinding[] bindings; + + private String[] importedPrefixes = javaLangPrefixArray; + private String[] importedNames = ZERO_STRINGS; + private static final String[] ZERO_STRINGS = new String[0]; + + private static final String[] javaLangPrefixArray = + new String[] { "java.lang.", }; + + + + public SimpleScope(World world, FormalBinding[] bindings) { + super(); + this.world = world; + this.bindings = bindings; + } + + // ---- impl + + //XXX doesn't report any problems + public UnresolvedType lookupType(String name, IHasPosition location) { + for (int i=0; itype is initially a WildTypePattern. If it stays that way, it's a this(Foo) + * type deal. + * however, the resolveBindings method may convert it to a BindingTypePattern, + * in which + * case, it's a this(foo) type deal. + * + * @author Erik Hilsdale + * @author Jim Hugunin + */ +public class ThisOrTargetPointcut extends NameBindingPointcut { + private boolean isThis; + private TypePattern type; + private String declarationText; + + private static final int thisKindSet; + private static final int targetKindSet; + + static { + int thisFlags = Shadow.ALL_SHADOW_KINDS_BITS; + int targFlags = Shadow.ALL_SHADOW_KINDS_BITS; + for (int i = 0; i < Shadow.SHADOW_KINDS.length; i++) { + Shadow.Kind kind = Shadow.SHADOW_KINDS[i]; + if (kind.neverHasThis()) thisFlags-=kind.bit; + if (kind.neverHasTarget()) targFlags-=kind.bit; + } + thisKindSet = thisFlags; + targetKindSet=targFlags; + } + + + public boolean isBinding() { + return (type instanceof BindingTypePattern); + } + + public ThisOrTargetPointcut(boolean isThis, TypePattern type) { + this.isThis = isThis; + this.type = type; + this.pointcutKind = THIS_OR_TARGET; + this.declarationText = (isThis ? "this(" : "target(") + type + ")"; + } + + public TypePattern getType() { + return type; + } + + public boolean isThis() { return isThis; } + + public Pointcut parameterizeWith(Map typeVariableMap,World w) { + ThisOrTargetPointcut ret = new ThisOrTargetPointcut(isThis,type.parameterizeWith(typeVariableMap,w)); + ret.copyLocationFrom(this); + return ret; + } + + public int couldMatchKinds() { + return isThis ? thisKindSet : targetKindSet; + } + + public FuzzyBoolean fastMatch(FastMatchInfo type) { + return FuzzyBoolean.MAYBE; + } + + private boolean couldMatch(Shadow shadow) { + return isThis ? shadow.hasThis() : shadow.hasTarget(); + } + + protected FuzzyBoolean matchInternal(Shadow shadow) { + if (!couldMatch(shadow)) return FuzzyBoolean.NO; + UnresolvedType typeToMatch = isThis ? shadow.getThisType() : shadow.getTargetType(); + //if (typeToMatch == ResolvedType.MISSING) return FuzzyBoolean.NO; + + return type.matches(typeToMatch.resolve(shadow.getIWorld()), TypePattern.DYNAMIC);//AVPT was DYNAMIC + } + + public void write(DataOutputStream s) throws IOException { + s.writeByte(Pointcut.THIS_OR_TARGET); + s.writeBoolean(isThis); + type.write(s); + writeLocation(s); + } + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + boolean isThis = s.readBoolean(); + TypePattern type = TypePattern.read(s, context); + ThisOrTargetPointcut ret = new ThisOrTargetPointcut(isThis, type); + ret.readLocation(context, s); + return ret; + } + + public void resolveBindings(IScope scope, Bindings bindings) { + type = type.resolveBindings(scope, bindings, true, true); + + // look for parameterized type patterns which are not supported... + HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor + visitor = new HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor(); + type.traverse(visitor, null); + if (visitor.wellHasItThen/*?*/()) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.THIS_AND_TARGET_DONT_SUPPORT_PARAMETERS), + getSourceLocation())); + } + // ??? handle non-formal + } + + public void postRead(ResolvedType enclosingType) { + type.postRead(enclosingType); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.NameBindingPointcut#getBindingAnnotationTypePatterns() + */ + public List getBindingAnnotationTypePatterns() { + return Collections.EMPTY_LIST; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.NameBindingPointcut#getBindingTypePatterns() + */ + public List getBindingTypePatterns() { + if (type instanceof BindingTypePattern) { + List l = new ArrayList(); + l.add(type); + return l; + } else return Collections.EMPTY_LIST; + } + + + public boolean equals(Object other) { + if (!(other instanceof ThisOrTargetPointcut)) return false; + ThisOrTargetPointcut o = (ThisOrTargetPointcut)other; + return o.isThis == this.isThis && o.type.equals(this.type); + } + public int hashCode() { + int result = 17; + result = 37*result + (isThis ? 0 : 1); + result = 37*result + type.hashCode(); + return result; + } + public String toString() { + return declarationText; + } + + /** + * Residue is the remainder of the pointcut match that couldn't be + * performed with the purely static information at compile time and + * this method returns the residue of a pointcut at a particular shadow. + */ + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + if (!couldMatch(shadow)) return Literal.FALSE; + + // if no preference is specified, just say TRUE which means no residue + if (type == TypePattern.ANY) return Literal.TRUE; + + Var var = isThis ? shadow.getThisVar() : shadow.getTargetVar(); + + return exposeStateForVar(var, type, state, shadow.getIWorld()); + } + + public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + if (isDeclare(bindings.getEnclosingAdvice())) { + // Enforce rule about which designators are supported in declare + inAspect.getWorld().showMessage(IMessage.ERROR, + WeaverMessages.format(WeaverMessages.THIS_OR_TARGET_IN_DECLARE,isThis?"this":"target"), + bindings.getEnclosingAdvice().getSourceLocation(), null); + return Pointcut.makeMatchesNothing(Pointcut.CONCRETE); + } + + TypePattern newType = type.remapAdviceFormals(bindings); + if (inAspect.crosscuttingMembers != null) { + inAspect.crosscuttingMembers.exposeType(newType.getExactType()); + } + + Pointcut ret = new ThisOrTargetPointcut(isThis, newType); + ret.copyLocationFrom(this); + return ret; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ThrowsPattern.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ThrowsPattern.java new file mode 100644 index 000000000..04dd0c264 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/ThrowsPattern.java @@ -0,0 +1,142 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.util.Map; + +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; + + +public class ThrowsPattern extends PatternNode { + private TypePatternList required; + private TypePatternList forbidden; + + public static final ThrowsPattern ANY = + new ThrowsPattern(TypePatternList.EMPTY, TypePatternList.EMPTY); + + public ThrowsPattern(TypePatternList required, TypePatternList forbidden) { + this.required = required; + this.forbidden = forbidden; + } + + public TypePatternList getRequired() { + return required; + } + + public TypePatternList getForbidden() { + return forbidden; + } + + public String toString() { + if (this == ANY) return ""; + + + String ret = "throws " + required.toString(); + if (forbidden.size() > 0) { + ret = ret + " !(" + forbidden.toString() + ")"; + } + return ret; + } + + public boolean equals(Object other) { + if (!(other instanceof ThrowsPattern)) return false; + ThrowsPattern o = (ThrowsPattern)other; + boolean ret = o.required.equals(this.required) && + o.forbidden.equals(this.forbidden); + return ret; + } + public int hashCode() { + int result = 17; + result = 37*result + required.hashCode(); + result = 37*result + forbidden.hashCode(); + return result; + } + + public ThrowsPattern resolveBindings(IScope scope, Bindings bindings) { + if (this == ANY) return this; + required = required.resolveBindings(scope, bindings, false, false); + forbidden = forbidden.resolveBindings(scope, bindings, false, false); + return this; + } + + public ThrowsPattern parameterizeWith(Map/*name -> resolved type*/ typeVariableMap,World w) { + ThrowsPattern ret = new ThrowsPattern( + required.parameterizeWith(typeVariableMap,w), + forbidden.parameterizeWith(typeVariableMap,w)); + ret.copyLocationFrom(this); + return ret; + } + + public boolean matches(UnresolvedType[] tys, World world) { + if (this == ANY) return true; + + //System.out.println("matching: " + this + " with " + Arrays.asList(tys)); + + ResolvedType[] types = world.resolve(tys); + // int len = types.length; + for (int j=0, lenj = required.size(); j < lenj; j++) { + if (! matchesAny(required.get(j), types)) { + return false; + } + } + for (int j=0, lenj = forbidden.size(); j < lenj; j++) { + if (matchesAny(forbidden.get(j), types)) { + return false; + } + } + return true; + } + + private boolean matchesAny( + TypePattern typePattern, + ResolvedType[] types) + { + for (int i = types.length - 1; i >= 0; i--) { + if (typePattern.matchesStatically(types[i])) return true; + } + return false; + } + + public static ThrowsPattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + TypePatternList required = TypePatternList.read(s, context); + TypePatternList forbidden = TypePatternList.read(s, context); + if (required.size() == 0 && forbidden.size() == 0) return ANY; + ThrowsPattern ret = new ThrowsPattern(required, forbidden); + //XXXret.readLocation(context, s); + return ret; + } + + public void write(DataOutputStream s) throws IOException { + required.write(s); + forbidden.write(s); + //XXXwriteLocation(s); + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public Object traverse(PatternNodeVisitor visitor, Object data) { + Object ret = accept(visitor,data); + forbidden.traverse(visitor, data); + required.traverse(visitor, data); + return ret; + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/TypePattern.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/TypePattern.java new file mode 100644 index 000000000..ed4f1ddf0 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/TypePattern.java @@ -0,0 +1,624 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.lang.reflect.Modifier; +import java.util.Iterator; +import java.util.Map; + +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.TypeVariableReference; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; +/** + * On creation, type pattern only contains WildTypePattern nodes, not BindingType or ExactType. + * + *

Then we call resolveBindings() during compilation + * During concretization of enclosing pointcuts, we call remapAdviceFormals + * + * @author Erik Hilsdale + * @author Jim Hugunin + */ +public abstract class TypePattern extends PatternNode { + public static class MatchKind { + private String name; + public MatchKind(String name) { this.name = name; } + public String toString() { return name; } + } + + public static final MatchKind STATIC = new MatchKind("STATIC"); + public static final MatchKind DYNAMIC = new MatchKind("DYNAMIC"); + + public static final TypePattern ELLIPSIS = new EllipsisTypePattern(); + public static final TypePattern ANY = new AnyTypePattern(); + public static final TypePattern NO = new NoTypePattern(); + + + protected boolean includeSubtypes; + protected boolean isVarArgs = false; + protected AnnotationTypePattern annotationPattern = AnnotationTypePattern.ANY; + protected TypePatternList typeParameters = TypePatternList.EMPTY; + + protected TypePattern(boolean includeSubtypes,boolean isVarArgs,TypePatternList typeParams) { + this.includeSubtypes = includeSubtypes; + this.isVarArgs = isVarArgs; + this.typeParameters = (typeParams == null ? TypePatternList.EMPTY : typeParams); + } + + protected TypePattern(boolean includeSubtypes, boolean isVarArgs) { + this(includeSubtypes,isVarArgs,null); + } + + public AnnotationTypePattern getAnnotationPattern() { + return annotationPattern; + } + + public boolean isVarArgs() { + return isVarArgs; + } + + public boolean isStarAnnotation() { + return annotationPattern == AnnotationTypePattern.ANY; + } + + public boolean isArray() { + return false; + } + + protected TypePattern(boolean includeSubtypes) { + this(includeSubtypes,false); + } + + public void setAnnotationTypePattern(AnnotationTypePattern annPatt) { + this.annotationPattern = annPatt; + } + + public void setTypeParameters(TypePatternList typeParams) { + this.typeParameters = typeParams; + } + + public TypePatternList getTypeParameters() { + return this.typeParameters; + } + + public void setIsVarArgs(boolean isVarArgs) { + this.isVarArgs = isVarArgs; + } + + // answer conservatively... + protected boolean couldEverMatchSameTypesAs(TypePattern other) { + if (this.includeSubtypes || other.includeSubtypes) return true; + if (this.annotationPattern != AnnotationTypePattern.ANY) return true; + if (other.annotationPattern != AnnotationTypePattern.ANY) return true; + return false; + } + + //XXX non-final for Not, && and || + public boolean matchesStatically(ResolvedType type) { + if (includeSubtypes) { + return matchesSubtypes(type); + } else { + return matchesExactly(type); + } + } + public abstract FuzzyBoolean matchesInstanceof(ResolvedType type); + + public final FuzzyBoolean matches(ResolvedType type, MatchKind kind) { +// FuzzyBoolean typeMatch = null; + //??? This is part of gracefully handling missing references + if (type.isMissing()) return FuzzyBoolean.NO; + + if (kind == STATIC) { +// typeMatch = FuzzyBoolean.fromBoolean(matchesStatically(type)); +// return typeMatch.and(annotationPattern.matches(type)); + return FuzzyBoolean.fromBoolean(matchesStatically(type)); + } else if (kind == DYNAMIC) { + //System.err.println("matching: " + this + " with " + type); +// typeMatch = matchesInstanceof(type); + //System.err.println(" got: " + ret); +// return typeMatch.and(annotationPattern.matches(type)); + return matchesInstanceof(type); + } else { + throw new IllegalArgumentException("kind must be DYNAMIC or STATIC"); + } + } + + protected abstract boolean matchesExactly(ResolvedType type); + + protected abstract boolean matchesExactly(ResolvedType type, ResolvedType annotatedType); + + protected boolean matchesSubtypes(ResolvedType type) { + //System.out.println("matching: " + this + " to " + type); + if (matchesExactly(type)) { + //System.out.println(" true"); + return true; + } + // pr124808 + Iterator typesIterator = null; + if (type.isTypeVariableReference()) { + typesIterator = ((TypeVariableReference)type).getTypeVariable().getFirstBound().resolve(type.getWorld()).getDirectSupertypes(); + } else { + // pr223605 + if (type.isRawType()) { + type = type.getGenericType(); + } + typesIterator = type.getDirectSupertypes(); + } + + // FuzzyBoolean ret = FuzzyBoolean.NO; // ??? -eh + for (Iterator i = typesIterator; i.hasNext(); ) { + ResolvedType superType = (ResolvedType)i.next(); + // TODO asc generics, temporary whilst matching isnt aware.. + //if (superType.isParameterizedType()) superType = superType.getRawType().resolve(superType.getWorld()); + if (matchesSubtypes(superType,type)) return true; + } + return false; + } + + protected boolean matchesSubtypes(ResolvedType superType, ResolvedType annotatedType) { + //System.out.println("matching: " + this + " to " + type); + if (matchesExactly(superType,annotatedType)) { + //System.out.println(" true"); + return true; + } + // If an ITD is applied, it will be put onto the generic type, not the parameterized or raw form + if (superType.isParameterizedType() || superType.isRawType()) { + superType = superType.getGenericType(); + } + // FuzzyBoolean ret = FuzzyBoolean.NO; // ??? -eh + for (Iterator i = superType.getDirectSupertypes(); i.hasNext(); ) { + ResolvedType superSuperType = (ResolvedType)i.next(); + if (matchesSubtypes(superSuperType,annotatedType)) return true; + } + return false; + } + + public UnresolvedType resolveExactType(IScope scope, Bindings bindings) { + TypePattern p = resolveBindings(scope, bindings, false, true); + if (!(p instanceof ExactTypePattern)) return ResolvedType.MISSING; + return ((ExactTypePattern)p).getType(); + } + + public UnresolvedType getExactType() { + if (this instanceof ExactTypePattern) return ((ExactTypePattern)this).getType(); + else return ResolvedType.MISSING; + } + + protected TypePattern notExactType(IScope s) { + s.getMessageHandler().handleMessage(MessageUtil.error( + WeaverMessages.format(WeaverMessages.EXACT_TYPE_PATTERN_REQD), getSourceLocation())); + return NO; + } + +// public boolean assertExactType(IMessageHandler m) { +// if (this instanceof ExactTypePattern) return true; +// +// //XXX should try harder to avoid multiple errors for one problem +// m.handleMessage(MessageUtil.error("exact type pattern required", getSourceLocation())); +// return false; +// } + + /** + * This can modify in place, or return a new TypePattern if the type changes. + */ + public TypePattern resolveBindings(IScope scope, Bindings bindings, + boolean allowBinding, boolean requireExactType) + { + annotationPattern = annotationPattern.resolveBindings(scope,bindings,allowBinding); + return this; + } + + public void resolve(World world) { + annotationPattern.resolve(world); + } + + /** + * return a version of this type pattern in which all type variable references have been + * replaced by their corresponding entry in the map. + */ + public abstract TypePattern parameterizeWith(Map typeVariableMap,World w); + + public void postRead(ResolvedType enclosingType) { + } + + public boolean isStar() { + return false; + } + + + + /** + * This is called during concretization of pointcuts, it is used by BindingTypePattern + * to return a new BindingTypePattern with a formal index appropiate for the advice, + * rather than for the lexical declaration, i.e. this handles transforamtions through + * named pointcuts. + *

+     * pointcut foo(String name): args(name);
+     * --> This makes a BindingTypePattern(0) pointing to the 0th formal
+     * 
+     * before(Foo f, String n): this(f) && foo(n) { ... }
+     * --> when resolveReferences is called on the args from the above, it
+     *     will return a BindingTypePattern(1)
+     * 
+     * before(Foo f): this(f) && foo(*) { ... }
+     * --> when resolveReferences is called on the args from the above, it
+     *     will return an ExactTypePattern(String)
+     * 
+ */ + public TypePattern remapAdviceFormals(IntMap bindings) { + return this; + } + + + public static final byte WILD = 1; + public static final byte EXACT = 2; + public static final byte BINDING = 3; + public static final byte ELLIPSIS_KEY = 4; + public static final byte ANY_KEY = 5; + public static final byte NOT = 6; + public static final byte OR = 7; + public static final byte AND = 8; + public static final byte NO_KEY = 9; + public static final byte ANY_WITH_ANNO = 10; + public static final byte HAS_MEMBER = 11; + + public static TypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + byte key = s.readByte(); + switch(key) { + case WILD: return WildTypePattern.read(s, context); + case EXACT: return ExactTypePattern.read(s, context); + case BINDING: return BindingTypePattern.read(s, context); + case ELLIPSIS_KEY: return ELLIPSIS; + case ANY_KEY: return ANY; + case NO_KEY: return NO; + case NOT: return NotTypePattern.read(s, context); + case OR: return OrTypePattern.read(s, context); + case AND: return AndTypePattern.read(s, context); + case ANY_WITH_ANNO: return AnyWithAnnotationTypePattern.read(s,context); + case HAS_MEMBER: return HasMemberTypePattern.read(s,context); + } + throw new BCException("unknown TypePattern kind: " + key); + } + + public boolean isIncludeSubtypes() { + return includeSubtypes; + } + +} + +class EllipsisTypePattern extends TypePattern { + + /** + * Constructor for EllipsisTypePattern. + * @param includeSubtypes + */ + public EllipsisTypePattern() { + super(false,false,new TypePatternList()); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.TypePattern#couldEverMatchSameTypesAs(org.aspectj.weaver.patterns.TypePattern) + */ + protected boolean couldEverMatchSameTypesAs(TypePattern other) { + return true; + } + /** + * @see org.aspectj.weaver.patterns.TypePattern#matchesExactly(IType) + */ + protected boolean matchesExactly(ResolvedType type) { + return false; + } + + protected boolean matchesExactly(ResolvedType type, ResolvedType annotatedType) { + return false; + } + + /** + * @see org.aspectj.weaver.patterns.TypePattern#matchesInstanceof(IType) + */ + public FuzzyBoolean matchesInstanceof(ResolvedType type) { + return FuzzyBoolean.NO; + } + + /** + * @see org.aspectj.weaver.patterns.PatternNode#write(DataOutputStream) + */ + public void write(DataOutputStream s) throws IOException { + s.writeByte(ELLIPSIS_KEY); + } + + public String toString() { return ".."; } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + return (obj instanceof EllipsisTypePattern); + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return 17 * 37; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public TypePattern parameterizeWith(Map typeVariableMap,World w) { + return this; + } + + +} + +class AnyTypePattern extends TypePattern { + + /** + * Constructor for EllipsisTypePattern. + * @param includeSubtypes + */ + public AnyTypePattern() { + super(false,false,new TypePatternList()); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.TypePattern#couldEverMatchSameTypesAs(org.aspectj.weaver.patterns.TypePattern) + */ + protected boolean couldEverMatchSameTypesAs(TypePattern other) { + return true; + } + /** + * @see org.aspectj.weaver.patterns.TypePattern#matchesExactly(IType) + */ + protected boolean matchesExactly(ResolvedType type) { + return true; + } + + protected boolean matchesExactly(ResolvedType type, ResolvedType annotatedType) { + return true; + } + + /** + * @see org.aspectj.weaver.patterns.TypePattern#matchesInstanceof(IType) + */ + public FuzzyBoolean matchesInstanceof(ResolvedType type) { + return FuzzyBoolean.YES; + } + + /** + * @see org.aspectj.weaver.patterns.PatternNode#write(DataOutputStream) + */ + public void write(DataOutputStream s) throws IOException { + s.writeByte(ANY_KEY); + } + + /** + * @see org.aspectj.weaver.patterns.TypePattern#matches(IType, MatchKind) + */ +// public FuzzyBoolean matches(IType type, MatchKind kind) { +// return FuzzyBoolean.YES; +// } + + /** + * @see org.aspectj.weaver.patterns.TypePattern#matchesSubtypes(IType) + */ + protected boolean matchesSubtypes(ResolvedType type) { + return true; + } + + + public boolean isStar() { + return true; + } + + public String toString() { return "*"; } + + public boolean equals(Object obj) { + return (obj instanceof AnyTypePattern); + } + + public int hashCode() { + return 37; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public TypePattern parameterizeWith(Map arg0,World w) { + return this; + } +} + +/** + * This type represents a type pattern of '*' but with an annotation specified, + * e.g. '@Color *' + */ +class AnyWithAnnotationTypePattern extends TypePattern { + + public AnyWithAnnotationTypePattern(AnnotationTypePattern atp) { + super(false,false); + annotationPattern = atp; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this,data); + } + + protected boolean couldEverMatchSameTypesAs(TypePattern other) { + return true; + } + + protected boolean matchesExactly(ResolvedType type) { + annotationPattern.resolve(type.getWorld()); + boolean b = false; + if (type.temporaryAnnotationTypes!=null) { + b = annotationPattern.matches(type,type.temporaryAnnotationTypes).alwaysTrue(); + } else { + b = annotationPattern.matches(type).alwaysTrue(); + } + return b; + } + + + protected boolean matchesExactly(ResolvedType type, ResolvedType annotatedType) { + annotationPattern.resolve(type.getWorld()); + return annotationPattern.matches(annotatedType).alwaysTrue(); + } + + public FuzzyBoolean matchesInstanceof(ResolvedType type) { + if (Modifier.isFinal(type.getModifiers())) { + return FuzzyBoolean.fromBoolean(matchesExactly(type)); + } + return FuzzyBoolean.MAYBE; + } + + public TypePattern parameterizeWith(Map typeVariableMap,World w) { + AnyWithAnnotationTypePattern ret = new AnyWithAnnotationTypePattern(this.annotationPattern.parameterizeWith(typeVariableMap,w)); + ret.copyLocationFrom(this); + return ret; + } + + public void write(DataOutputStream s) throws IOException { + s.writeByte(TypePattern.ANY_WITH_ANNO); + annotationPattern.write(s); + writeLocation(s); + } + + public static TypePattern read(VersionedDataInputStream s,ISourceContext c) throws IOException { + AnnotationTypePattern annPatt = AnnotationTypePattern.read(s,c); + AnyWithAnnotationTypePattern ret = new AnyWithAnnotationTypePattern(annPatt); + ret.readLocation(c, s); + return ret; + } + +// public FuzzyBoolean matches(IType type, MatchKind kind) { +// return FuzzyBoolean.YES; +// } + + protected boolean matchesSubtypes(ResolvedType type) { + return true; + } + + public boolean isStar() { + return false; + } + + public String toString() { return annotationPattern+" *"; } + + public boolean equals(Object obj) { + if (!(obj instanceof AnyWithAnnotationTypePattern)) return false; + AnyWithAnnotationTypePattern awatp = (AnyWithAnnotationTypePattern) obj; + return (annotationPattern.equals(awatp.annotationPattern)); + } + + public int hashCode() { + return annotationPattern.hashCode(); + } +} + +class NoTypePattern extends TypePattern { + + public NoTypePattern() { + super(false,false,new TypePatternList()); + } + + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.TypePattern#couldEverMatchSameTypesAs(org.aspectj.weaver.patterns.TypePattern) + */ + protected boolean couldEverMatchSameTypesAs(TypePattern other) { + return false; + } + /** + * @see org.aspectj.weaver.patterns.TypePattern#matchesExactly(IType) + */ + protected boolean matchesExactly(ResolvedType type) { + return false; + } + + protected boolean matchesExactly(ResolvedType type, ResolvedType annotatedType) { + return false; + } + + /** + * @see org.aspectj.weaver.patterns.TypePattern#matchesInstanceof(IType) + */ + public FuzzyBoolean matchesInstanceof(ResolvedType type) { + return FuzzyBoolean.NO; + } + + /** + * @see org.aspectj.weaver.patterns.PatternNode#write(DataOutputStream) + */ + public void write(DataOutputStream s) throws IOException { + s.writeByte(NO_KEY); + } + + /** + * @see org.aspectj.weaver.patterns.TypePattern#matches(IType, MatchKind) + */ +// public FuzzyBoolean matches(IType type, MatchKind kind) { +// return FuzzyBoolean.YES; +// } + + /** + * @see org.aspectj.weaver.patterns.TypePattern#matchesSubtypes(IType) + */ + protected boolean matchesSubtypes(ResolvedType type) { + return false; + } + + + public boolean isStar() { + return false; + } + + public String toString() { return ""; }//FIXME AV - bad! toString() cannot be parsed back (not idempotent) + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + return (obj instanceof NoTypePattern); + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return 17 * 37 * 37; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public TypePattern parameterizeWith(Map arg0,World w) { + return this; + } +} + diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/TypePatternList.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/TypePatternList.java new file mode 100644 index 000000000..d2b51d7d9 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/TypePatternList.java @@ -0,0 +1,333 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.World; + +public class TypePatternList extends PatternNode { + private TypePattern[] typePatterns; + int ellipsisCount = 0; + + public static final TypePatternList EMPTY = + new TypePatternList(new TypePattern[] {}); + + public static final TypePatternList ANY = + new TypePatternList(new TypePattern[] {new EllipsisTypePattern()}); //can't use TypePattern.ELLIPSIS because of circular static dependency that introduces + + public TypePatternList() { + typePatterns = new TypePattern[0]; + ellipsisCount = 0; + } + + public TypePatternList(TypePattern[] arguments) { + this.typePatterns = arguments; + for (int i=0; i 0) buf.append(", "); + if (type == TypePattern.ELLIPSIS) { + buf.append(".."); + } else { + buf.append(type.toString()); + } + } + buf.append(")"); + return buf.toString(); + } + + /* + * return true iff this pattern could ever match a signature with the + * given number of parameters + */ + public boolean canMatchSignatureWithNParameters(int numParams) { + if (ellipsisCount == 0) { + return numParams == size(); + } else { + return (size() -ellipsisCount) <= numParams; + } + } + public FuzzyBoolean matches(ResolvedType[] types, TypePattern.MatchKind kind) { + return matches(types,kind,null); + } + + //XXX shares much code with WildTypePattern and with NamePattern + /** + * When called with TypePattern.STATIC this will always return either + * FuzzyBoolean.YES or FuzzyBoolean.NO. + * + * When called with TypePattern.DYNAMIC this could return MAYBE if + * at runtime it would be possible for arguments of the given static + * types to dynamically match this, but it is not known for certain. + * + * This method will never return FuzzyBoolean.NEVER + */ + public FuzzyBoolean matches(ResolvedType[] types, TypePattern.MatchKind kind, ResolvedType[][] parameterAnnotations) { + int nameLength = types.length; + int patternLength = typePatterns.length; + + int nameIndex = 0; + int patternIndex = 0; + + if (ellipsisCount == 0) { + if (nameLength != patternLength) return FuzzyBoolean.NO; + FuzzyBoolean finalReturn = FuzzyBoolean.YES; + while (patternIndex < patternLength) { + ResolvedType t = types[nameIndex]; + FuzzyBoolean ret = null; + try { + if (parameterAnnotations!=null) t.temporaryAnnotationTypes = parameterAnnotations[nameIndex]; + ret = typePatterns[patternIndex].matches(t,kind); + } finally { + t.temporaryAnnotationTypes=null; + } + patternIndex++; + nameIndex++; + if (ret == FuzzyBoolean.NO) return ret; + if (ret == FuzzyBoolean.MAYBE) finalReturn = ret; + } + return finalReturn; + } else if (ellipsisCount == 1) { + if (nameLength < patternLength-1) return FuzzyBoolean.NO; + FuzzyBoolean finalReturn = FuzzyBoolean.YES; + while (patternIndex < patternLength) { + TypePattern p = typePatterns[patternIndex++]; + if (p == TypePattern.ELLIPSIS) { + nameIndex = nameLength - (patternLength-patternIndex); + } else { + ResolvedType t = types[nameIndex]; + FuzzyBoolean ret = null; + try { + if (parameterAnnotations!=null) t.temporaryAnnotationTypes = parameterAnnotations[nameIndex]; + ret = p.matches(t, kind); + } finally { + t.temporaryAnnotationTypes=null; + } + nameIndex++; + if (ret == FuzzyBoolean.NO) return ret; + if (ret == FuzzyBoolean.MAYBE) finalReturn = ret; + } + } + return finalReturn; + } else { +// System.err.print("match(" + arguments + ", " + types + ") -> "); + FuzzyBoolean b = outOfStar(typePatterns, types, 0, 0, patternLength - ellipsisCount, nameLength, ellipsisCount, kind); +// System.err.println(b); + return b; + } + } + + private static FuzzyBoolean outOfStar(final TypePattern[] pattern, final ResolvedType[] target, + int pi, int ti, + int pLeft, int tLeft, + final int starsLeft, TypePattern.MatchKind kind) { + if (pLeft > tLeft) return FuzzyBoolean.NO; + FuzzyBoolean finalReturn = FuzzyBoolean.YES; + while (true) { + // invariant: if (tLeft > 0) then (ti < target.length && pi < pattern.length) + if (tLeft == 0) return finalReturn; + if (pLeft == 0) { + if (starsLeft > 0) { + return finalReturn; + } else { + return FuzzyBoolean.NO; + } + } + if (pattern[pi] == TypePattern.ELLIPSIS) { + return inStar(pattern, target, pi+1, ti, pLeft, tLeft, starsLeft-1, kind); + } + FuzzyBoolean ret = pattern[pi].matches(target[ti], kind); + if (ret == FuzzyBoolean.NO) return ret; + if (ret == FuzzyBoolean.MAYBE) finalReturn = ret; + pi++; ti++; pLeft--; tLeft--; + } + } + private static FuzzyBoolean inStar(final TypePattern[] pattern, final ResolvedType[] target, + int pi, int ti, + final int pLeft, int tLeft, + int starsLeft, TypePattern.MatchKind kind) { + // invariant: pLeft > 0, so we know we'll run out of stars and find a real char in pattern + TypePattern patternChar = pattern[pi]; + while (patternChar == TypePattern.ELLIPSIS) { + starsLeft--; + patternChar = pattern[++pi]; + } + while (true) { + // invariant: if (tLeft > 0) then (ti < target.length) + if (pLeft > tLeft) return FuzzyBoolean.NO; + FuzzyBoolean ff = patternChar.matches(target[ti], kind); + if (ff.maybeTrue()) { + FuzzyBoolean xx = outOfStar(pattern, target, pi+1, ti+1, pLeft-1, tLeft-1, starsLeft, kind); + if (xx.maybeTrue()) return ff.and(xx); + } + ti++; tLeft--; + } + } + + /** + * Return a version of this type pattern list in which all type variable references + * are replaced by their corresponding entry in the map + * @param typeVariableMap + * @return + */ + public TypePatternList parameterizeWith(Map typeVariableMap,World w) { + TypePattern[] parameterizedPatterns = new TypePattern[typePatterns.length]; + for (int i = 0; i < parameterizedPatterns.length; i++) { + parameterizedPatterns[i] = typePatterns[i].parameterizeWith(typeVariableMap,w); + } + return new TypePatternList(parameterizedPatterns); + } + + public TypePatternList resolveBindings(IScope scope, Bindings bindings, boolean allowBinding, boolean requireExactType) { + for (int i=0; iFor example:

+ *
    + *
  • <T> T genericMethod(T t) {...}
  • + *
  • static <T extends Foo> T staticGenericMethod(T t) {...}
  • + *
  • Foo<T extends Bar & IGoo> + *
+ */ +public class TypeVariablePattern extends PatternNode { + + private static final String anything = "?"; + + private String name; // eg. "T" + private TypePattern upperBound; // default is object unless of the form T extends Bar + private TypePattern[] interfaceBounds; // additional upper bounds (must be interfaces) arising from + // declarations of the form T extends Bar & IGoo, IDoo + private TypePattern lowerBound; // only set if type variable is of the form T super Bar + + /** + * Create a named type variable with upper bound Object and no lower bounds. + * Use this constructor for the simple "T" case + */ + public TypeVariablePattern(String variableName) { + this.name = variableName; + this.upperBound = new ExactTypePattern(UnresolvedType.OBJECT,false,false); + this.lowerBound = null; + this.interfaceBounds = null; + } + + /** + * Create a named type variable with the given upper bound and no lower bounds + * Use this constructor for the T extends Foo case + * @param variableName + * @param upperBound + */ + public TypeVariablePattern(String variableName, TypePattern upperBound) { + this.name = variableName; + this.upperBound = upperBound; + this.lowerBound = null; + this.interfaceBounds = null; + } + + public TypeVariablePattern(String variableName, TypePattern upperLimit, TypePattern[] interfaceBounds, TypePattern lowerBound) { + this.name = variableName; + this.upperBound = upperLimit; + if (upperBound == null) upperBound = new ExactTypePattern(UnresolvedType.OBJECT,false,false); + this.interfaceBounds = interfaceBounds; + this.lowerBound = lowerBound; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this,data); + } + + public String getName() { + return name; + } + + public boolean isAnythingPattern() { + return name.equals(anything); + } + + public TypePattern getRawTypePattern() { + return upperBound; + } + + public TypePattern getUpperBound() { + return upperBound; + } + + public boolean hasLowerBound() { return (lowerBound != null); } + + public TypePattern getLowerBound() { + return lowerBound; + } + + public boolean hasAdditionalInterfaceBounds() { + return (interfaceBounds != null); + } + + public TypePattern[] getAdditionalInterfaceBounds() { + if (interfaceBounds != null) { + return interfaceBounds; + } else { + return new TypePattern[0]; + } + } + + public boolean equals(Object obj) { + if (!(obj instanceof TypeVariablePattern)) return false; + TypeVariablePattern other = (TypeVariablePattern) obj; + if (!name.equals(other.name)) return false; + if (!upperBound.equals(other.upperBound)) return false; + if (lowerBound != null) { + if (other.lowerBound == null) return false; + if (!lowerBound.equals(other.lowerBound)) return false; + } else { + if (other.lowerBound != null) return false; + } + if (interfaceBounds != null) { + if (other.interfaceBounds == null) return false; + if (interfaceBounds.length != other.interfaceBounds.length) return false; + for (int i = 0; i < interfaceBounds.length; i++) { + if (!interfaceBounds[i].equals(other.interfaceBounds[i])) return false; + } + } else { + if (other.interfaceBounds != null) return false; + } + return true; + } + + public int hashCode() { + int hashCode = 17 + (37 * name.hashCode()); + hashCode = hashCode * 37 + upperBound.hashCode(); + if (lowerBound != null) hashCode = hashCode * 37 + lowerBound.hashCode(); + if (interfaceBounds != null) { + for (int i = 0; i < interfaceBounds.length; i++) { + hashCode = 37*hashCode + interfaceBounds[i].hashCode(); + } + } + return hashCode; + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append(name); + sb.append(getExtendsClause()); + if (interfaceBounds != null) { + sb.append(" & "); + for (int i = 0; i < interfaceBounds.length; i++) { + sb.append(interfaceBounds[i].toString()); + if (i < interfaceBounds.length) sb.append(","); + } + } + if (lowerBound != null) { + sb.append(" super "); + sb.append(lowerBound.toString()); + } + return sb.toString(); + } + + private String getExtendsClause() { + if (upperBound instanceof ExactTypePattern) { + ExactTypePattern bound = (ExactTypePattern) upperBound; + if (bound.type == UnresolvedType.OBJECT) return ""; + } + return " extends " + upperBound.toString(); + } + + public void write(DataOutputStream s) throws IOException { + s.writeUTF(name); + upperBound.write(s); + if (interfaceBounds == null) { + s.writeInt(0); + } else { + s.writeInt(interfaceBounds.length); + for (int i = 0; i < interfaceBounds.length; i++) { + interfaceBounds[i].write(s); + } + } + s.writeBoolean(hasLowerBound()); + if (hasLowerBound()) lowerBound.write(s); + writeLocation(s); + } + + public static TypeVariablePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + TypeVariablePattern tv = null; + String name = s.readUTF(); + TypePattern upperBound = TypePattern.read(s, context); + TypePattern[] additionalInterfaceBounds = null; + int numInterfaceBounds = s.readInt(); + if (numInterfaceBounds > 0) { + additionalInterfaceBounds = new TypePattern[numInterfaceBounds]; + for (int i = 0; i < additionalInterfaceBounds.length; i++) { + additionalInterfaceBounds[i] = TypePattern.read(s, context); + } + } + boolean hasLowerBound = s.readBoolean(); + TypePattern lowerBound = null; + if (hasLowerBound) lowerBound = TypePattern.read(s,context); + tv = new TypeVariablePattern(name,upperBound,additionalInterfaceBounds,lowerBound); + tv.readLocation(context, s); + return tv; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/TypeVariablePatternList.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/TypeVariablePatternList.java new file mode 100644 index 000000000..8ede2a073 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/TypeVariablePatternList.java @@ -0,0 +1,85 @@ +/* ******************************************************************* + * 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.patterns; + +import java.io.DataOutputStream; +import java.io.IOException; + +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.VersionedDataInputStream; + +/** + * @author colyer + * A list of type variable specifications, eg. <T,S> + */ +public class TypeVariablePatternList extends PatternNode { + + public static final TypeVariablePatternList EMPTY = new TypeVariablePatternList(new TypeVariablePattern[0]); + + private TypeVariablePattern[] patterns; + + public TypeVariablePatternList(TypeVariablePattern[] typeVars) { + this.patterns = typeVars; + } + + public TypeVariablePattern[] getTypeVariablePatterns() { + return this.patterns; + } + + public TypeVariablePattern lookupTypeVariable(String name) { + for (int i = 0; i < patterns.length; i++) { + if (patterns[i].getName().equals(name)) { + return patterns[i]; + } + } + return null; + } + + public boolean isEmpty() { + return ((patterns == null) || (patterns.length == 0)); + } + + public void write(DataOutputStream s) throws IOException { + s.writeInt(patterns.length); + for (int i = 0; i < patterns.length; i++) { + patterns[i].write(s); + } + writeLocation(s); + } + + public static TypeVariablePatternList read(VersionedDataInputStream s, ISourceContext context) throws IOException { + TypeVariablePatternList ret = EMPTY; + int length = s.readInt(); + if (length > 0) { + TypeVariablePattern[] patterns = new TypeVariablePattern[length]; + for (int i = 0; i < patterns.length; i++) { + patterns[i] = TypeVariablePattern.read(s,context); + } + ret = new TypeVariablePatternList(patterns); + } + ret.readLocation(context, s); // redundant but safe to read location for EMPTY + return ret; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this,data); + } + + public Object traverse(PatternNodeVisitor visitor, Object data) { + Object ret = accept(visitor,data); + for (int i = 0; i < patterns.length; i++) { + patterns[i].traverse(visitor,ret); + } + return ret; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/WildAnnotationTypePattern.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/WildAnnotationTypePattern.java new file mode 100644 index 000000000..4555df8ef --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/WildAnnotationTypePattern.java @@ -0,0 +1,369 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.AnnotatedElement; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; +import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; + +/** + * @author colyer + * + * TODO To change the template for this generated type comment go to + * Window - Preferences - Java - Code Style - Code Templates + */ +public class WildAnnotationTypePattern extends AnnotationTypePattern { + + private TypePattern typePattern; + private boolean resolved = false; + Map annotationValues; + + /** + * + */ + public WildAnnotationTypePattern(TypePattern typePattern) { + super(); + this.typePattern = typePattern; + this.setLocation(typePattern.getSourceContext(), typePattern.start, typePattern.end); + } + + public WildAnnotationTypePattern(TypePattern typePattern, Map annotationValues) { + super(); + this.typePattern = typePattern; + this.annotationValues = annotationValues; + // PVAL make the location be from start of type pattern to end of values + this.setLocation(typePattern.getSourceContext(), typePattern.start, typePattern.end); + } + + public TypePattern getTypePattern() { + return typePattern; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.AnnotationTypePattern#matches(org.aspectj.weaver.AnnotatedElement) + */ + public FuzzyBoolean matches(AnnotatedElement annotated) { + return matches(annotated,null); + } + + /** + * Resolve any annotation values specified, checking they are all well formed (valid names, valid values) + * @param annotationType the annotation type for which the values have been specified + * @param scope the scope within which to resolve type references (eg. Color.GREEN) + */ + protected void resolveAnnotationValues(ResolvedType annotationType, IScope scope) { + if (annotationValues == null) return; + // Check any values specified are OK: + // - the value names are for valid annotation fields + // - the specified values are of the correct type + // - for enums, check the specified values can be resolved in the specified scope + Set keys = annotationValues.keySet(); + ResolvedMember[] ms = annotationType.getDeclaredMethods(); + for (Iterator kIter = keys.iterator(); kIter.hasNext();) { + String k = (String) kIter.next(); + String v = (String) annotationValues.get(k); + boolean validKey = false; + for (int i = 0; i < ms.length; i++) { + ResolvedMember resolvedMember = ms[i]; + if (resolvedMember.getName().equals(k) && resolvedMember.isAbstract()) { + validKey = true; + ResolvedType t = resolvedMember.getReturnType().resolve(scope.getWorld()); + if (t.isEnum()) { + // value must be an enum reference X.Y + int pos = v.lastIndexOf("."); + if (pos == -1) { + IMessage m = MessageUtil.error( + WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE,v,"enum"), + getSourceLocation()); + scope.getWorld().getMessageHandler().handleMessage(m); + } else { + String typename = v.substring(0,pos); + ResolvedType rt = scope.lookupType(typename, this).resolve(scope.getWorld()); + v = rt.getSignature()+v.substring(pos+1); // from 'Color.RED' to 'Lp/Color;RED' + annotationValues.put(k,v); + } + } else if (t.isPrimitiveType()) { + if (t.getSignature()=="I") { + try { + int value = Integer.parseInt(v); + annotationValues.put(k,Integer.toString(value)); + } catch (NumberFormatException nfe) { + IMessage m = MessageUtil.error( + WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE,v,"int"), + getSourceLocation()); + scope.getWorld().getMessageHandler().handleMessage(m); + } + } else if (t.getSignature()=="F") { + try { + float value = Float.parseFloat(v); + annotationValues.put(k,Float.toString(value)); + } catch (NumberFormatException nfe) { + IMessage m = MessageUtil.error( + WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE,v,"float"), + getSourceLocation()); + scope.getWorld().getMessageHandler().handleMessage(m); + } + + } else if (t.getSignature()=="Z") { + if (v.equalsIgnoreCase("true") || v.equalsIgnoreCase("false")) { + // is it ok ! + } else { + IMessage m = MessageUtil.error( + WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE,v,"boolean"), + getSourceLocation()); + scope.getWorld().getMessageHandler().handleMessage(m); + } + } else if (t.getSignature()=="S") { + try { + short value = Short.parseShort(v); + annotationValues.put(k,Short.toString(value)); + } catch (NumberFormatException nfe) { + IMessage m = MessageUtil.error( + WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE,v,"short"), + getSourceLocation()); + scope.getWorld().getMessageHandler().handleMessage(m); + } + } else if (t.getSignature()=="J") { + try { + long value = Long.parseLong(v); + annotationValues.put(k,Long.toString(value)); + } catch (NumberFormatException nfe) { + IMessage m = MessageUtil.error( + WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE,v,"long"), + getSourceLocation()); + scope.getWorld().getMessageHandler().handleMessage(m); + } + } else if (t.getSignature()=="D") { + try { + double value = Double.parseDouble(v); + annotationValues.put(k,Double.toString(value)); + } catch (NumberFormatException nfe) { + IMessage m = MessageUtil.error( + WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE,v,"double"), + getSourceLocation()); + scope.getWorld().getMessageHandler().handleMessage(m); + } + } else if (t.getSignature()=="B") { + try { + byte value = Byte.parseByte(v); + annotationValues.put(k,Byte.toString(value)); + } catch (NumberFormatException nfe) { + IMessage m = MessageUtil.error( + WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE,v,"byte"), + getSourceLocation()); + scope.getWorld().getMessageHandler().handleMessage(m); + } + } else if (t.getSignature()=="C") { + if (v.length()!=3) { // '?' + IMessage m = MessageUtil.error( + WeaverMessages.format(WeaverMessages.INVALID_ANNOTATION_VALUE,v,"char"), + getSourceLocation()); + scope.getWorld().getMessageHandler().handleMessage(m); + } else { + annotationValues.put(k,v.substring(1,2)); + } + } else { + throw new RuntimeException("Not implemented for "+t); + } + } else if (t.equals(ResolvedType.JAVA_LANG_STRING)) { + // nothing to do, it will be OK + } else { + throw new RuntimeException("Compiler limitation: annotation value support not implemented for type "+t); + } + } + } + if (!validKey) { + IMessage m = MessageUtil.error( + WeaverMessages.format(WeaverMessages.UNKNOWN_ANNOTATION_VALUE,annotationType,k), + getSourceLocation()); + scope.getWorld().getMessageHandler().handleMessage(m); + } + } + } + + + + + public FuzzyBoolean matches(AnnotatedElement annotated,ResolvedType[] parameterAnnotations) { + if (!resolved) { + throw new IllegalStateException("Can't match on an unresolved annotation type pattern"); + } + if (annotationValues!=null) { + // PVAL improve this restriction, would allow '*(value=Color.RED)' + throw new IllegalStateException("Cannot use annotationvalues with a wild annotation pattern"); + } + if (isForParameterAnnotationMatch()) { + if (parameterAnnotations!=null && parameterAnnotations.length!=0) { + for (int i = 0; i < parameterAnnotations.length; i++) { + if (typePattern.matches(parameterAnnotations[i],TypePattern.STATIC).alwaysTrue()) { + return FuzzyBoolean.YES; + } + } + } + } else { + // matches if the type of any of the annotations on the AnnotatedElement is + // matched by the typePattern. + ResolvedType[] annTypes = annotated.getAnnotationTypes(); + if (annTypes!=null && annTypes.length!=0) { + for (int i = 0; i < annTypes.length; i++) { + if (typePattern.matches(annTypes[i],TypePattern.STATIC).alwaysTrue()) { + return FuzzyBoolean.YES; + } + } + } + } + return FuzzyBoolean.NO; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.AnnotationTypePattern#resolve(org.aspectj.weaver.World) + */ + public void resolve(World world) { + // nothing to do... + resolved = true; + } + + /** + * This can modify in place, or return a new TypePattern if the type changes. + */ + public AnnotationTypePattern resolveBindings(IScope scope, Bindings bindings, + boolean allowBinding) + { + if (!scope.getWorld().isInJava5Mode()) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.ANNOTATIONS_NEED_JAVA5), + getSourceLocation())); + return this; + } + if (resolved) return this; + this.typePattern = typePattern.resolveBindings(scope,bindings,false,false); + resolved = true; + if (typePattern instanceof ExactTypePattern) { + ExactTypePattern et = (ExactTypePattern)typePattern; + if (!et.getExactType().resolve(scope.getWorld()).isAnnotation()) { + IMessage m = MessageUtil.error( + WeaverMessages.format(WeaverMessages.REFERENCE_TO_NON_ANNOTATION_TYPE,et.getExactType().getName()), + getSourceLocation()); + scope.getWorld().getMessageHandler().handleMessage(m); + resolved = false; + } + ResolvedType annotationType = et.getExactType().resolve(scope.getWorld()); + resolveAnnotationValues(annotationType,scope); + ExactAnnotationTypePattern eatp = new ExactAnnotationTypePattern(annotationType,annotationValues); + eatp.copyLocationFrom(this); + if (isForParameterAnnotationMatch()) eatp.setForParameterAnnotationMatch(); + return eatp; + } else { + return this; + } + } + + public AnnotationTypePattern parameterizeWith(Map typeVariableMap,World w) { + WildAnnotationTypePattern ret = new WildAnnotationTypePattern(typePattern.parameterizeWith(typeVariableMap,w)); + ret.copyLocationFrom(this); + ret.resolved = resolved; + return ret; + } + + private static final byte VERSION = 1; // rev if ser. form changes + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.PatternNode#write(java.io.DataOutputStream) + */ + public void write(DataOutputStream s) throws IOException { + s.writeByte(AnnotationTypePattern.WILD); + s.writeByte(VERSION); + typePattern.write(s); + writeLocation(s); + s.writeBoolean(isForParameterAnnotationMatch()); + // PVAL + if (annotationValues==null) { + s.writeInt(0); + } else { + s.writeInt(annotationValues.size()); + Set key = annotationValues.keySet(); + for (Iterator keys = key.iterator(); keys.hasNext();) { + String k = (String) keys.next(); + s.writeUTF(k); + s.writeUTF((String)annotationValues.get(k)); + } + } + } + + public static AnnotationTypePattern read(VersionedDataInputStream s,ISourceContext context) throws IOException { + WildAnnotationTypePattern ret; + byte version = s.readByte(); + if (version > VERSION) { + throw new BCException("ExactAnnotationTypePattern was written by a newer version of AspectJ"); + } + TypePattern t = TypePattern.read(s,context); + ret = new WildAnnotationTypePattern(t); + ret.readLocation(context,s); + if (s.getMajorVersion()>=WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ160) { + if (s.readBoolean()) ret.setForParameterAnnotationMatch(); + } + if (s.getMajorVersion()>=WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ160M2) { + int annotationValueCount = s.readInt(); + if (annotationValueCount>0) { + Map aValues = new HashMap(); + for (int i=0;i 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) + * + * 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) + * + * Fo* + * Parser creates WildTypePattern namePatterns = {Fo*}, typeParameters=WTP{String} + * resolveBindings resolves typeParameters to ETP{String} + * returns WildTypePattern(name = Fo*, typeParameters = ETP{String} isGeneric=false) + * + * + * Foo + * + * Foo + * + * Foo + * + * Foo + * + */ +public class WildTypePattern extends TypePattern { + private static final String GENERIC_WILDCARD_CHARACTER = "?"; // signature of ? is * + private static final String GENERIC_WILDCARD_SIGNATURE_CHARACTER = "*"; // signature of ? is * + private NamePattern[] namePatterns; + int ellipsisCount; + String[] importedPrefixes; + String[] knownMatches; + int dim; + + // SECRETAPI - just for testing, turns off boundschecking temporarily... + public static boolean boundscheckingoff = false; + + // these next three are set if the type pattern is constrained by extends or super clauses, in which case the + // namePatterns must have length 1 + // TODO AMC: read/write/resolve of these fields + TypePattern upperBound; // extends Foo + TypePattern[] additionalInterfaceBounds; // extends Foo & A,B,C + TypePattern lowerBound; // super Foo + + // if we have type parameters, these fields indicate whether we should be a generic type pattern or a parameterized + // type pattern. We can only tell during resolve bindings. + private boolean isGeneric = true; + + WildTypePattern(NamePattern[] namePatterns, boolean includeSubtypes, int dim, boolean isVarArgs, TypePatternList typeParams) { + super(includeSubtypes,isVarArgs,typeParams); + this.namePatterns = namePatterns; + this.dim = dim; + ellipsisCount = 0; + for (int i=0; i 0) { + if (!namePatterns[0].matches(otherType.getName())) return false; + } + } + if (other instanceof WildTypePattern) { + WildTypePattern owtp = (WildTypePattern) other; + String mySimpleName = namePatterns[0].maybeGetSimpleName(); + String yourSimpleName = owtp.namePatterns[0].maybeGetSimpleName(); + if (mySimpleName != null && yourSimpleName != null) { + return (mySimpleName.startsWith(yourSimpleName) || + yourSimpleName.startsWith(mySimpleName)); + } + } + return true; + } + + //XXX inefficient implementation + // we don't know whether $ characters are from nested types, or were + // part of the declared type name (generated code often uses $s in type + // names). More work required on our part to get this right... + public static char[][] splitNames(String s, boolean convertDollar) { + List ret = new ArrayList(); + int startIndex = 0; + while (true) { + int breakIndex = s.indexOf('.', startIndex); // what about / + if (convertDollar && (breakIndex == -1)) breakIndex = s.indexOf('$', startIndex); // we treat $ like . here + if (breakIndex == -1) break; + char[] name = s.substring(startIndex, breakIndex).toCharArray(); + ret.add(name); + startIndex = breakIndex+1; + } + ret.add(s.substring(startIndex).toCharArray()); + return (char[][])ret.toArray(new char[ret.size()][]); + } + + + /** + * @see org.aspectj.weaver.TypePattern#matchesExactly(IType) + */ + protected boolean matchesExactly(ResolvedType type) { + return matchesExactly(type,type); + } + + protected boolean matchesExactly(ResolvedType type, ResolvedType annotatedType) { + String targetTypeName = type.getName(); + + //System.err.println("match: " + targetTypeName + ", " + knownMatches); //Arrays.asList(importedPrefixes)); + // Ensure the annotation pattern is resolved + annotationPattern.resolve(type.getWorld()); + + return matchesExactlyByName(targetTypeName,type.isAnonymous(),type.isNested()) && + matchesParameters(type,STATIC) && + matchesBounds(type,STATIC) && + annotationPattern.matches(annotatedType,type.temporaryAnnotationTypes).alwaysTrue(); + } + + + // we've matched against the base (or raw) type, but if this type pattern specifies parameters or + // type variables we need to make sure we match against them too + private boolean matchesParameters(ResolvedType aType, MatchKind staticOrDynamic) { + if (!isGeneric && typeParameters.size() > 0) { + if(!aType.isParameterizedType()) return false; + // we have to match type parameters + return typeParameters.matches(aType.getResolvedTypeParameters(), staticOrDynamic).alwaysTrue(); + } + return true; + } + + // we've matched against the base (or raw) type, but if this type pattern specifies bounds because + // it is a ? extends or ? super deal then we have to match them too. + private boolean matchesBounds(ResolvedType aType, MatchKind staticOrDynamic) { + if (!(aType instanceof BoundedReferenceType)) return true; + BoundedReferenceType boundedRT = (BoundedReferenceType) aType; + if (upperBound == null && boundedRT.getUpperBound() != null) { + // for upper bound, null can also match against Object - but anything else and we're out. + if (!boundedRT.getUpperBound().getName().equals(UnresolvedType.OBJECT.getName())) { + return false; + } + } + if (lowerBound == null && boundedRT.getLowerBound() != null) return false; + if (upperBound != null) { + // match ? extends + if (aType.isGenericWildcard() && boundedRT.isSuper()) return false; + if (boundedRT.getUpperBound() == null) return false; + return upperBound.matches((ResolvedType) boundedRT.getUpperBound(), staticOrDynamic).alwaysTrue(); + } + if (lowerBound != null) { + // match ? super + if (!(boundedRT.isGenericWildcard() && boundedRT.isSuper())) return false; + return lowerBound.matches((ResolvedType) boundedRT.getLowerBound(), staticOrDynamic).alwaysTrue(); + } + return true; + } + + /** + * Used in conjunction with checks on 'isStar()' to tell you if this pattern represents '*' or '*[]' which are + * different ! + */ + public int getDimensions() { + return dim; + } + + public boolean isArray() { + return dim > 1; + } + + /** + * @param targetTypeName + * @return + */ + private boolean matchesExactlyByName(String targetTypeName, boolean isAnonymous, boolean isNested) { + // we deal with parameter matching separately... + if (targetTypeName.indexOf('<') != -1) { + targetTypeName = targetTypeName.substring(0,targetTypeName.indexOf('<')); + } + // we deal with bounds matching separately too... + if (targetTypeName.startsWith(GENERIC_WILDCARD_CHARACTER)) { + targetTypeName = GENERIC_WILDCARD_CHARACTER; + } + //XXX hack + if (knownMatches == null && importedPrefixes == null) { + return innerMatchesExactly(targetTypeName, isAnonymous, isNested); + } + + if (isNamePatternStar()) { + // we match if the dimensions match + int numDimensionsInTargetType = 0; + if (dim > 0) { + int index; + while((index = targetTypeName.indexOf('[')) != -1) { + numDimensionsInTargetType++; + targetTypeName = targetTypeName.substring(index+1); + } + if (numDimensionsInTargetType == dim) { + return true; + } else { + return false; + } + } + } + + // if our pattern is length 1, then known matches are exact matches + // if it's longer than that, then known matches are prefixes of a sort + if (namePatterns.length == 1) { + if (isAnonymous) { + // we've already ruled out "*", and no other name pattern should match an anonymous type + return false; + } + for (int i=0, len=knownMatches.length; i < len; i++) { + if (knownMatches[i].equals(targetTypeName)) return true; + } + } else { + for (int i=0, len=knownMatches.length; i < len; i++) { + String knownPrefix = knownMatches[i] + "$"; + if (targetTypeName.startsWith(knownPrefix)) { + int pos = lastIndexOfDotOrDollar(knownMatches[i]); + if (innerMatchesExactly(targetTypeName.substring(pos+1),isAnonymous,isNested)) { + return true; + } + } + } + } + + + // if any prefixes match, strip the prefix and check that the rest matches + // assumes that prefixes have a dot at the end + for (int i=0, len=importedPrefixes.length; i < len; i++) { + String prefix = importedPrefixes[i]; + //System.err.println("prefix match? " + prefix + " to " + targetTypeName); + if (targetTypeName.startsWith(prefix)) { + + if (innerMatchesExactly(targetTypeName.substring(prefix.length()),isAnonymous,isNested)) { + return true; + } + } + } + + return innerMatchesExactly(targetTypeName,isAnonymous,isNested); + } + + private int lastIndexOfDotOrDollar(String string) { + int dot = string.lastIndexOf('.'); + int dollar = string.lastIndexOf('$'); + return Math.max(dot, dollar); + } + + + + private boolean innerMatchesExactly(String s, boolean isAnonymous, boolean convertDollar /*isNested*/) { + + List ret = new ArrayList(); + int startIndex = 0; + while (true) { + int breakIndex = s.indexOf('.', startIndex); // what about / + if (convertDollar && (breakIndex == -1)) breakIndex = s.indexOf('$', startIndex); // we treat $ like . here + if (breakIndex == -1) break; + char[] name = s.substring(startIndex, breakIndex).toCharArray(); + ret.add(name); + startIndex = breakIndex+1; + } + ret.add(s.substring(startIndex).toCharArray()); + + int namesLength = ret.size(); + int patternsLength = namePatterns.length; + + int namesIndex = 0; + int patternsIndex = 0; + + if ((!namePatterns[patternsLength-1].isAny()) && isAnonymous) return false; + + if (ellipsisCount == 0) { + if (namesLength != patternsLength) return false; + while (patternsIndex < patternsLength) { + if (!namePatterns[patternsIndex++].matches((char[])ret.get(namesIndex++))) { + return false; + } + } + return true; + } else if (ellipsisCount == 1) { + if (namesLength < patternsLength-1) return false; + while (patternsIndex < patternsLength) { + NamePattern p = namePatterns[patternsIndex++]; + if (p == NamePattern.ELLIPSIS) { + namesIndex = namesLength - (patternsLength-patternsIndex); + } else { + if (!p.matches((char[])ret.get(namesIndex++))) { + return false; + } + } + } + return true; + } else { + // System.err.print("match(\"" + Arrays.asList(namePatterns) + "\", \"" + Arrays.asList(names) + "\") -> "); + boolean b = outOfStar(namePatterns, (char[][])ret.toArray(new char[ret.size()][]), 0, 0, patternsLength - ellipsisCount, namesLength, ellipsisCount); + // System.err.println(b); + return b; + } + } + private static boolean outOfStar(final NamePattern[] pattern, final char[][] target, + int pi, int ti, + int pLeft, int tLeft, + final int starsLeft) { + if (pLeft > tLeft) return false; + while (true) { + // invariant: if (tLeft > 0) then (ti < target.length && pi < pattern.length) + if (tLeft == 0) return true; + if (pLeft == 0) { + return (starsLeft > 0); + } + if (pattern[pi] == NamePattern.ELLIPSIS) { + return inStar(pattern, target, pi+1, ti, pLeft, tLeft, starsLeft-1); + } + if (! pattern[pi].matches(target[ti])) { + return false; + } + pi++; ti++; pLeft--; tLeft--; + } + } + private static boolean inStar(final NamePattern[] pattern, final char[][] target, + int pi, int ti, + final int pLeft, int tLeft, + int starsLeft) { + // invariant: pLeft > 0, so we know we'll run out of stars and find a real char in pattern + // of course, we probably can't parse multiple ..'s in a row, but this keeps the algorithm + // exactly parallel with that in NamePattern + NamePattern patternChar = pattern[pi]; + while (patternChar == NamePattern.ELLIPSIS) { + starsLeft--; + patternChar = pattern[++pi]; + } + while (true) { + // invariant: if (tLeft > 0) then (ti < target.length) + if (pLeft > tLeft) return false; + if (patternChar.matches(target[ti])) { + if (outOfStar(pattern, target, pi+1, ti+1, pLeft-1, tLeft-1, starsLeft)) return true; + } + ti++; tLeft--; + } + } + + /** + * @see org.aspectj.weaver.TypePattern#matchesInstanceof(IType) + */ + public FuzzyBoolean matchesInstanceof(ResolvedType type) { + //XXX hack to let unmatched types just silently remain so + if (maybeGetSimpleName() != null) return + FuzzyBoolean.NO; + + type.getWorld().getMessageHandler().handleMessage( + new Message("can't do instanceof matching on patterns with wildcards", + IMessage.ERROR, null, getSourceLocation())); + return FuzzyBoolean.NO; + } + + public NamePattern extractName() { + if (isIncludeSubtypes() || isVarArgs() || isArray() || (typeParameters.size() > 0)) { + // we can't extract a name, the pattern is something like Foo+ and therefore + // it is not ok to treat Foo as a method name! + return null; + } + //System.err.println("extract from : " + Arrays.asList(namePatterns)); + int len = namePatterns.length; + if (len ==1 && !annotationPattern.isAny()) return null; // can't extract + NamePattern ret = namePatterns[len-1]; + NamePattern[] newNames = new NamePattern[len-1]; + System.arraycopy(namePatterns, 0, newNames, 0, len-1); + namePatterns = newNames; + //System.err.println(" left : " + Arrays.asList(namePatterns)); + return ret; + } + + /** + * Method maybeExtractName. + * @param string + * @return boolean + */ + public boolean maybeExtractName(String string) { + int len = namePatterns.length; + NamePattern ret = namePatterns[len-1]; + String simple = ret.maybeGetSimpleName(); + if (simple != null && simple.equals(string)) { + extractName(); + return true; + } + return false; + } + + /** + * If this type pattern has no '.' or '*' in it, then + * return a simple string + * + * otherwise, this will return null; + */ + public String maybeGetSimpleName() { + if (namePatterns.length == 1) { + return namePatterns[0].maybeGetSimpleName(); + } + return null; + } + + /** + * If this type pattern has no '*' or '..' in it + */ + public String maybeGetCleanName() { + if (namePatterns.length == 0) { + throw new RuntimeException("bad name: " + namePatterns); + } + //System.out.println("get clean: " + this); + StringBuffer buf = new StringBuffer(); + for (int i=0, len=namePatterns.length; i < len; i++) { + NamePattern p = namePatterns[i]; + String simpleName = p.maybeGetSimpleName(); + if (simpleName == null) return null; + if (i > 0) buf.append("."); + buf.append(simpleName); + } + //System.out.println(buf); + return buf.toString(); + } + + public TypePattern parameterizeWith(Map typeVariableMap,World w) { + NamePattern[] newNamePatterns = new NamePattern[namePatterns.length]; + for(int i=0; i0) { + typeParameters.resolveBindings(scope,bindings,allowBinding,requireExactType); + isGeneric = false; + } + + // resolve any bounds + if (upperBound != null) upperBound = upperBound.resolveBindings(scope, bindings, allowBinding, requireExactType); + if (lowerBound != null) lowerBound = lowerBound.resolveBindings(scope, bindings, allowBinding, requireExactType); + // amc - additional interface bounds only needed if we support type vars again. + + String fullyQualifiedName = maybeGetCleanName(); + if (fullyQualifiedName != null) { + return resolveBindingsFromFullyQualifiedTypeName(fullyQualifiedName, scope, bindings, allowBinding, requireExactType); + } else { + if (requireExactType) { + scope.getWorld().getMessageHandler().handleMessage( + MessageUtil.error(WeaverMessages.format(WeaverMessages.WILDCARD_NOT_ALLOWED), + getSourceLocation())); + return NO; + } + importedPrefixes = scope.getImportedPrefixes(); + knownMatches = preMatch(scope.getImportedNames()); + return this; // pattern contains wildcards so can't be resolved to an ExactTypePattern... + //XXX need to implement behavior for Lint.invalidWildcardTypeName + } + } + + private TypePattern maybeResolveToAnyPattern(IScope scope, Bindings bindings, + boolean allowBinding, boolean requireExactType) { + // If there is an annotation specified we have to + // use a special variant of Any TypePattern called + // AnyWithAnnotation + if (annotationPattern == AnnotationTypePattern.ANY) { + if (dim == 0 && !isVarArgs && upperBound == null && lowerBound == null && (additionalInterfaceBounds == null || additionalInterfaceBounds.length==0)) { // pr72531 + return TypePattern.ANY; //??? loses source location + } + } else if (!isVarArgs){ + annotationPattern = annotationPattern.resolveBindings(scope,bindings,allowBinding); + AnyWithAnnotationTypePattern ret = new AnyWithAnnotationTypePattern(annotationPattern); + ret.setLocation(sourceContext,start,end); + return ret; + } + return null; // can't resolve to a simple "any" pattern + } + + private TypePattern maybeResolveToBindingTypePattern(IScope scope, Bindings bindings, + boolean allowBinding, boolean requireExactType) { + String simpleName = maybeGetSimpleName(); + if (simpleName != null) { + FormalBinding formalBinding = scope.lookupFormal(simpleName); + if (formalBinding != null) { + if (bindings == null) { + scope.message(IMessage.ERROR, this, "negation doesn't allow binding"); + return this; + } + if (!allowBinding) { + scope.message(IMessage.ERROR, this, + "name binding only allowed in target, this, and args pcds"); + return this; + } + + BindingTypePattern binding = new BindingTypePattern(formalBinding,isVarArgs); + binding.copyLocationFrom(this); + bindings.register(binding, scope); + + return binding; + } + } + return null; // not possible to resolve to a binding type pattern + } + + private TypePattern resolveBindingsFromFullyQualifiedTypeName(String fullyQualifiedName, IScope scope, Bindings bindings, + boolean allowBinding, boolean requireExactType) { + String originalName = fullyQualifiedName; + ResolvedType resolvedTypeInTheWorld = null; + UnresolvedType type; + + //System.out.println("resolve: " + cleanName); + //??? this loop has too many inefficiencies to count + resolvedTypeInTheWorld = lookupTypeInWorldIncludingPrefixes(scope.getWorld(), fullyQualifiedName, scope.getImportedPrefixes()); + + if (resolvedTypeInTheWorld.isGenericWildcard()) { + type = resolvedTypeInTheWorld; + } else { + type = lookupTypeInScope(scope, fullyQualifiedName, this); + } + if ((type instanceof ResolvedType) && ((ResolvedType)type).isMissing()) { + return resolveBindingsForMissingType(resolvedTypeInTheWorld, originalName, scope, bindings, allowBinding, requireExactType); + } else { + return resolveBindingsForExactType(scope,type,fullyQualifiedName,requireExactType); + } + } + + + + private UnresolvedType lookupTypeInScope(IScope scope, String typeName, IHasPosition location) { + UnresolvedType type = null; + while (ResolvedType.isMissing(type = scope.lookupType(typeName, location))) { + int lastDot = typeName.lastIndexOf('.'); + if (lastDot == -1) break; + typeName = typeName.substring(0, lastDot) + '$' + typeName.substring(lastDot+1); + } + return type; + } + + /** + * Searches the world for the ResolvedType with the given typeName. If one + * isn't found then for each of the supplied prefixes, it prepends the typeName + * with the prefix and searches the world for the ResolvedType with this new name. + * If one still isn't found then a MissingResolvedTypeWithKnownSignature is + * returned with the originally requested typeName (this ensures the typeName + * makes sense). + */ + private ResolvedType lookupTypeInWorldIncludingPrefixes(World world, String typeName, String[] prefixes) { + ResolvedType ret = lookupTypeInWorld(world, typeName); + if (!ret.isMissing()) return ret; + ResolvedType retWithPrefix = ret; + int counter = 0; + while (retWithPrefix.isMissing() && (counter < prefixes.length)) { + retWithPrefix = lookupTypeInWorld(world,prefixes[counter] + typeName); + counter++; + } + if (!retWithPrefix.isMissing()) return retWithPrefix; + return ret; + } + + private ResolvedType lookupTypeInWorld(World world, String typeName) { + UnresolvedType ut = UnresolvedType.forName(typeName); + ResolvedType ret = world.resolve(ut,true); + while (ret.isMissing()) { + int lastDot = typeName.lastIndexOf('.'); + if (lastDot == -1) break; + typeName = typeName.substring(0, lastDot) + '$' + typeName.substring(lastDot+1); + ret = world.resolve(UnresolvedType.forName(typeName),true); + } + return ret; + } + + private TypePattern resolveBindingsForExactType(IScope scope, UnresolvedType aType, String fullyQualifiedName,boolean requireExactType) { + TypePattern ret = null; + if (aType.isTypeVariableReference()) { + // we have to set the bounds on it based on the bounds of this pattern + ret = resolveBindingsForTypeVariable(scope, (UnresolvedTypeVariableReferenceType) aType); + } else if (typeParameters.size()>0) { + ret = resolveParameterizedType(scope, aType, requireExactType); + } else if (upperBound != null || lowerBound != null) { + // this must be a generic wildcard with bounds + ret = resolveGenericWildcard(scope, aType); + } else { + if (dim != 0) aType = UnresolvedType.makeArray(aType, dim); + ret = new ExactTypePattern(aType,includeSubtypes,isVarArgs); + } + ret.setAnnotationTypePattern(annotationPattern); + ret.copyLocationFrom(this); + return ret; + } + + private TypePattern resolveGenericWildcard(IScope scope, UnresolvedType aType) { + if (!aType.getSignature().equals(GENERIC_WILDCARD_SIGNATURE_CHARACTER)) throw new IllegalStateException("Can only have bounds for a generic wildcard"); + boolean canBeExact = true; + if ((upperBound != null) && ResolvedType.isMissing(upperBound.getExactType())) canBeExact = false; + if ((lowerBound != null) && ResolvedType.isMissing(lowerBound.getExactType())) canBeExact = false; + if (canBeExact) { + ResolvedType type = null; + if (upperBound != null) { + if (upperBound.isIncludeSubtypes()) { + canBeExact = false; + } else { + ReferenceType upper = (ReferenceType) upperBound.getExactType().resolve(scope.getWorld()); + type = new BoundedReferenceType(upper,true,scope.getWorld()); + } + } else { + if (lowerBound.isIncludeSubtypes()) { + canBeExact = false; + } else { + ReferenceType lower = (ReferenceType) lowerBound.getExactType().resolve(scope.getWorld()); + type = new BoundedReferenceType(lower,false,scope.getWorld()); + } + } + if (canBeExact) { + // might have changed if we find out include subtypes is set on one of the bounds... + return new ExactTypePattern(type,includeSubtypes,isVarArgs); + } + } + + // we weren't able to resolve to an exact type pattern... + // leave as wild type pattern + importedPrefixes = scope.getImportedPrefixes(); + knownMatches = preMatch(scope.getImportedNames()); + return this; + } + + private TypePattern resolveParameterizedType(IScope scope, UnresolvedType aType, boolean requireExactType) { + ResolvedType rt = aType.resolve(scope.getWorld()); + if (!verifyTypeParameters(rt,scope,requireExactType)) return TypePattern.NO; // messages already isued + // Only if the type is exact *and* the type parameters are exact should we create an + // ExactTypePattern for this WildTypePattern + if (typeParameters.areAllExactWithNoSubtypesAllowed()) { + TypePattern[] typePats = typeParameters.getTypePatterns(); + UnresolvedType[] typeParameterTypes = new UnresolvedType[typePats.length]; + for (int i = 0; i < typeParameterTypes.length; i++) { + typeParameterTypes[i] = ((ExactTypePattern)typePats[i]).getExactType(); + } + // rt could be a parameterized type 156058 + if (rt.isParameterizedType()) { + rt = rt.getGenericType(); + } + ResolvedType type = TypeFactory.createParameterizedType(rt, typeParameterTypes, scope.getWorld()); + if (isGeneric) type = type.getGenericType(); +// UnresolvedType tx = UnresolvedType.forParameterizedTypes(aType,typeParameterTypes); +// UnresolvedType type = scope.getWorld().resolve(tx,true); + if (dim != 0) type = ResolvedType.makeArray(type, dim); + return new ExactTypePattern(type,includeSubtypes,isVarArgs); + } else { + // AMC... just leave it as a wild type pattern then? + importedPrefixes = scope.getImportedPrefixes(); + knownMatches = preMatch(scope.getImportedNames()); + return this; + } + } + + private TypePattern resolveBindingsForMissingType(ResolvedType typeFoundInWholeWorldSearch, String nameWeLookedFor, IScope scope, Bindings bindings, + boolean allowBinding, boolean requireExactType) { + if (requireExactType) { + if (!allowBinding) { + scope.getWorld().getMessageHandler().handleMessage( + MessageUtil.error(WeaverMessages.format(WeaverMessages.CANT_BIND_TYPE,nameWeLookedFor), + getSourceLocation())); + } else if (scope.getWorld().getLint().invalidAbsoluteTypeName.isEnabled()) { + scope.getWorld().getLint().invalidAbsoluteTypeName.signal(nameWeLookedFor, getSourceLocation()); + } + return NO; + } else if (scope.getWorld().getLint().invalidAbsoluteTypeName.isEnabled()) { + // Only put the lint warning out if we can't find it in the world + if (typeFoundInWholeWorldSearch.isMissing()) { + scope.getWorld().getLint().invalidAbsoluteTypeName.signal(nameWeLookedFor, getSourceLocation()); + } + } + importedPrefixes = scope.getImportedPrefixes(); + knownMatches = preMatch(scope.getImportedNames()); + return this; + } + + /** + * We resolved the type to a type variable declared in the pointcut designator. + * Now we have to create either an exact type pattern or a wild type pattern for it, + * with upper and lower bounds set accordingly. + * XXX none of this stuff gets serialized yet + * @param scope + * @param tvrType + * @return + */ + private TypePattern resolveBindingsForTypeVariable(IScope scope, UnresolvedTypeVariableReferenceType tvrType) { + Bindings emptyBindings = new Bindings(0); + if (upperBound != null) { + upperBound = upperBound.resolveBindings(scope, emptyBindings, false, false); + } + if (lowerBound != null) { + lowerBound = lowerBound.resolveBindings(scope, emptyBindings, false, false); + } + if (additionalInterfaceBounds != null) { + TypePattern[] resolvedIfBounds = new TypePattern[additionalInterfaceBounds.length]; + for (int i = 0; i < resolvedIfBounds.length; i++) { + resolvedIfBounds[i] = additionalInterfaceBounds[i].resolveBindings(scope, emptyBindings, false, false); + } + additionalInterfaceBounds = resolvedIfBounds; + } + if ( upperBound == null && lowerBound == null && additionalInterfaceBounds == null) { + // no bounds to worry about... + ResolvedType rType = tvrType.resolve(scope.getWorld()); + if (dim != 0) rType = ResolvedType.makeArray(rType, dim); + return new ExactTypePattern(rType,includeSubtypes,isVarArgs); + } else { + // we have to set bounds on the TypeVariable held by tvrType before resolving it + boolean canCreateExactTypePattern = true; + if (upperBound != null && ResolvedType.isMissing(upperBound.getExactType())) canCreateExactTypePattern = false; + if (lowerBound != null && ResolvedType.isMissing(lowerBound.getExactType())) canCreateExactTypePattern = false; + if (additionalInterfaceBounds != null) { + for (int i = 0; i < additionalInterfaceBounds.length; i++) { + if (ResolvedType.isMissing(additionalInterfaceBounds[i].getExactType())) canCreateExactTypePattern = false; + } + } + if (canCreateExactTypePattern) { + TypeVariable tv = tvrType.getTypeVariable(); + if (upperBound != null) tv.setUpperBound(upperBound.getExactType()); + if (lowerBound != null) tv.setLowerBound(lowerBound.getExactType()); + if (additionalInterfaceBounds != null) { + UnresolvedType[] ifBounds = new UnresolvedType[additionalInterfaceBounds.length]; + for (int i = 0; i < ifBounds.length; i++) { + ifBounds[i] = additionalInterfaceBounds[i].getExactType(); + } + tv.setAdditionalInterfaceBounds(ifBounds); + } + ResolvedType rType = tvrType.resolve(scope.getWorld()); + if (dim != 0) rType = ResolvedType.makeArray(rType, dim); + return new ExactTypePattern(rType,includeSubtypes,isVarArgs); + } + return this; // leave as wild type pattern then + } + } + + /** + * When this method is called, we have resolved the base type to an exact type. + * We also have a set of type patterns for the parameters. + * Time to perform some basic checks: + * - can the base type be parameterized? (is it generic) + * - can the type parameter pattern list match the number of parameters on the base type + * - do all parameter patterns meet the bounds of the respective type variables + * If any of these checks fail, a warning message is issued and we return false. + * @return + */ + private boolean verifyTypeParameters(ResolvedType baseType,IScope scope, boolean requireExactType) { + ResolvedType genericType = baseType.getGenericType(); + if (genericType == null) { + // issue message "does not match because baseType.getName() is not generic" + scope.message(MessageUtil.warn( + WeaverMessages.format(WeaverMessages.NOT_A_GENERIC_TYPE,baseType.getName()), + getSourceLocation())); + return false; + } + int minRequiredTypeParameters = typeParameters.size(); + boolean foundEllipsis = false; + TypePattern[] typeParamPatterns = typeParameters.getTypePatterns(); + for (int i = 0; i < typeParamPatterns.length; i++) { + if (typeParamPatterns[i] instanceof WildTypePattern) { + WildTypePattern wtp = (WildTypePattern) typeParamPatterns[i]; + if (wtp.ellipsisCount > 0) { + foundEllipsis = true; + minRequiredTypeParameters--; + } + } + } + TypeVariable[] tvs = genericType.getTypeVariables(); + if ((tvs.length < minRequiredTypeParameters) || + (!foundEllipsis && minRequiredTypeParameters != tvs.length)) + { + // issue message "does not match because wrong no of type params" + String msg = WeaverMessages.format(WeaverMessages.INCORRECT_NUMBER_OF_TYPE_ARGUMENTS, + genericType.getName(),new Integer(tvs.length)); + if (requireExactType) scope.message(MessageUtil.error(msg,getSourceLocation())); + else scope.message(MessageUtil.warn(msg,getSourceLocation())); + return false; + } + + // now check that each typeParameter pattern, if exact, matches the bounds + // of the type variable. + + // pr133307 - delay verification until type binding completion, these next few lines replace + // the call to checkBoundsOK + if (!boundscheckingoff) { + VerifyBoundsForTypePattern verification = + new VerifyBoundsForTypePattern(scope,genericType,requireExactType,typeParameters,getSourceLocation()); + scope.getWorld().getCrosscuttingMembersSet().recordNecessaryCheck(verification); + } +// return checkBoundsOK(scope,genericType,requireExactType); + + return true; + } + + /** + * By capturing the verification in this class, rather than performing it in verifyTypeParameters(), + * we can cope with situations where the interactions between generics and declare parents would + * otherwise cause us problems. For example, if verifying as we go along we may report a problem + * which would have been fixed by a declare parents that we haven't looked at yet. If we + * create and store a verification object, we can verify this later when the type system is + * considered 'complete' + */ + static class VerifyBoundsForTypePattern implements IVerificationRequired { + + private IScope scope; + private ResolvedType genericType; + private boolean requireExactType; + private TypePatternList typeParameters = TypePatternList.EMPTY; + private ISourceLocation sLoc; + + public VerifyBoundsForTypePattern(IScope scope, ResolvedType genericType, boolean requireExactType, + TypePatternList typeParameters, ISourceLocation sLoc) { + this.scope = scope; + this.genericType = genericType; + this.requireExactType = requireExactType; + this.typeParameters = typeParameters; + this.sLoc = sLoc; + } + + public void verify() { + TypeVariable[] tvs = genericType.getTypeVariables(); + TypePattern[] typeParamPatterns = typeParameters.getTypePatterns(); + if (typeParameters.areAllExactWithNoSubtypesAllowed()) { + for (int i = 0; i < tvs.length; i++) { + UnresolvedType ut = typeParamPatterns[i].getExactType(); + boolean continueCheck = true; + // FIXME asc dont like this but ok temporary measure. If the type parameter + // is itself a type variable (from the generic aspect) then assume it'll be + // ok... (see pr112105) Want to break this? Run GenericAspectK test. + if (ut.isTypeVariableReference()) { + continueCheck = false; + } + + //System.err.println("Verifying "+ut.getName()+" meets bounds for "+tvs[i]); + if (continueCheck && + !tvs[i].canBeBoundTo(ut.resolve(scope.getWorld()))) { + // issue message that type parameter does not meet specification + String parameterName = ut.getName(); + if (ut.isTypeVariableReference()) parameterName = ((TypeVariableReference)ut).getTypeVariable().getDisplayName(); + String msg = + WeaverMessages.format( + WeaverMessages.VIOLATES_TYPE_VARIABLE_BOUNDS, + parameterName, + new Integer(i+1), + tvs[i].getDisplayName(), + genericType.getName()); + if (requireExactType) scope.message(MessageUtil.error(msg,sLoc)); + else scope.message(MessageUtil.warn(msg,sLoc)); + } + } + } + } + } + + // pr133307 - moved to verification object +// public boolean checkBoundsOK(IScope scope,ResolvedType genericType,boolean requireExactType) { +// if (boundscheckingoff) return true; +// TypeVariable[] tvs = genericType.getTypeVariables(); +// TypePattern[] typeParamPatterns = typeParameters.getTypePatterns(); +// if (typeParameters.areAllExactWithNoSubtypesAllowed()) { +// for (int i = 0; i < tvs.length; i++) { +// UnresolvedType ut = typeParamPatterns[i].getExactType(); +// boolean continueCheck = true; +// // FIXME asc dont like this but ok temporary measure. If the type parameter +// // is itself a type variable (from the generic aspect) then assume it'll be +// // ok... (see pr112105) Want to break this? Run GenericAspectK test. +// if (ut.isTypeVariableReference()) { +// continueCheck = false; +// } +// +// if (continueCheck && +// !tvs[i].canBeBoundTo(ut.resolve(scope.getWorld()))) { +// // issue message that type parameter does not meet specification +// String parameterName = ut.getName(); +// if (ut.isTypeVariableReference()) parameterName = ((TypeVariableReference)ut).getTypeVariable().getDisplayName(); +// String msg = +// WeaverMessages.format( +// WeaverMessages.VIOLATES_TYPE_VARIABLE_BOUNDS, +// parameterName, +// new Integer(i+1), +// tvs[i].getDisplayName(), +// genericType.getName()); +// if (requireExactType) scope.message(MessageUtil.error(msg,getSourceLocation())); +// else scope.message(MessageUtil.warn(msg,getSourceLocation())); +// return false; +// } +// } +// } +// return true; +// } + + public boolean isStar() { + boolean annPatternStar = annotationPattern == AnnotationTypePattern.ANY; + return (isNamePatternStar() && annPatternStar); + } + + private boolean isNamePatternStar() { + return namePatterns.length == 1 && namePatterns[0].isAny(); + } + + /** + * returns those possible matches which I match exactly the last element of + */ + private String[] preMatch(String[] possibleMatches) { + //if (namePatterns.length != 1) return CollectionUtil.NO_STRINGS; + + List ret = new ArrayList(); + for (int i=0, len=possibleMatches.length; i < len; i++) { + char[][] names = splitNames(possibleMatches[i],true); //??? not most efficient + if (namePatterns[0].matches(names[names.length-1])) { + ret.add(possibleMatches[i]); + continue; + } + if (possibleMatches[i].indexOf("$") != -1) { + names = splitNames(possibleMatches[i],false); //??? not most efficient + if (namePatterns[0].matches(names[names.length-1])) { + ret.add(possibleMatches[i]); + } + } + } + return (String[])ret.toArray(new String[ret.size()]); + } + + +// public void postRead(ResolvedType enclosingType) { +// this.importedPrefixes = enclosingType.getImportedPrefixes(); +// this.knownNames = prematch(enclosingType.getImportedNames()); +// } + + + public String toString() { + StringBuffer buf = new StringBuffer(); + if (annotationPattern != AnnotationTypePattern.ANY) { + buf.append('('); + buf.append(annotationPattern.toString()); + buf.append(' '); + } + for (int i=0, len=namePatterns.length; i < len; i++) { + NamePattern name = namePatterns[i]; + if (name == null) { + buf.append("."); + } else { + if (i > 0) buf.append("."); + buf.append(name.toString()); + } + } + if (upperBound != null) { + buf.append(" extends "); + buf.append(upperBound.toString()); + } + if (lowerBound != null) { + buf.append(" super "); + buf.append(lowerBound.toString()); + } + if (typeParameters!=null && typeParameters.size()!=0) { + buf.append("<"); + buf.append(typeParameters.toString()); + buf.append(">"); + } + if (includeSubtypes) buf.append('+'); + if (isVarArgs) buf.append("..."); + if (annotationPattern != AnnotationTypePattern.ANY) { + buf.append(')'); + } + return buf.toString(); + } + + public boolean equals(Object other) { + if (!(other instanceof WildTypePattern)) return false; + WildTypePattern o = (WildTypePattern)other; + int len = o.namePatterns.length; + if (len != this.namePatterns.length) return false; + if (this.includeSubtypes != o.includeSubtypes) return false; + if (this.dim != o.dim) return false; + if (this.isVarArgs != o.isVarArgs) return false; + if (this.upperBound != null) { + if (o.upperBound == null) return false; + if (!this.upperBound.equals(o.upperBound)) return false; + } else { + if (o.upperBound != null) return false; + } + if (this.lowerBound != null) { + if (o.lowerBound == null) return false; + if (!this.lowerBound.equals(o.lowerBound)) return false; + } else { + if (o.lowerBound != null) return false; + } + if (!typeParameters.equals(o.typeParameters)) return false; + for (int i=0; i < len; i++) { + if (!o.namePatterns[i].equals(this.namePatterns[i])) return false; + } + return (o.annotationPattern.equals(this.annotationPattern)); + } + + public int hashCode() { + int result = 17; + for (int i = 0, len = namePatterns.length; i < len; i++) { + result = 37*result + namePatterns[i].hashCode(); + } + result = 37*result + annotationPattern.hashCode(); + if (upperBound != null) result = 37*result + upperBound.hashCode(); + if (lowerBound != null) result = 37*result + lowerBound.hashCode(); + return result; + } + + private static final byte VERSION = 1; // rev on change + /** + * @see org.aspectj.weaver.patterns.PatternNode#write(DataOutputStream) + */ + public void write(DataOutputStream s) throws IOException { + s.writeByte(TypePattern.WILD); + s.writeByte(VERSION); + s.writeShort(namePatterns.length); + for (int i = 0; i < namePatterns.length; i++) { + namePatterns[i].write(s); + } + s.writeBoolean(includeSubtypes); + s.writeInt(dim); + s.writeBoolean(isVarArgs); + typeParameters.write(s); // ! change from M2 + //??? storing this information with every type pattern is wasteful of .class + // file size. Storing it on enclosing types would be more efficient + FileUtil.writeStringArray(knownMatches, s); + FileUtil.writeStringArray(importedPrefixes, s); + writeLocation(s); + annotationPattern.write(s); + // generics info, new in M3 + s.writeBoolean(isGeneric); + s.writeBoolean(upperBound != null); + if (upperBound != null) upperBound.write(s); + s.writeBoolean(lowerBound != null); + if (lowerBound != null) lowerBound.write(s); + s.writeInt(additionalInterfaceBounds == null ? 0 : additionalInterfaceBounds.length); + if (additionalInterfaceBounds != null) { + for (int i = 0; i < additionalInterfaceBounds.length; i++) { + additionalInterfaceBounds[i].write(s); + } + } + } + + public static TypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { + if (s.getMajorVersion()>=AjAttribute.WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ150) { + return readTypePattern150(s,context); + } else { + return readTypePatternOldStyle(s,context); + } + } + + public static TypePattern readTypePattern150(VersionedDataInputStream s, ISourceContext context) throws IOException { + byte version = s.readByte(); + if (version > VERSION) { + throw new BCException("WildTypePattern was written by a more recent version of AspectJ, cannot read"); + } + int len = s.readShort(); + NamePattern[] namePatterns = new NamePattern[len]; + for (int i=0; i < len; i++) { + namePatterns[i] = NamePattern.read(s); + } + boolean includeSubtypes = s.readBoolean(); + int dim = s.readInt(); + boolean varArg = s.readBoolean(); + TypePatternList typeParams = TypePatternList.read(s, context); + WildTypePattern ret = new WildTypePattern(namePatterns, includeSubtypes, dim, varArg,typeParams); + ret.knownMatches = FileUtil.readStringArray(s); + ret.importedPrefixes = FileUtil.readStringArray(s); + ret.readLocation(context, s); + ret.setAnnotationTypePattern(AnnotationTypePattern.read(s,context)); + // generics info, new in M3 + ret.isGeneric = s.readBoolean(); + if (s.readBoolean()) { + ret.upperBound = TypePattern.read(s,context); + } + if (s.readBoolean()) { + ret.lowerBound = TypePattern.read(s,context); + } + int numIfBounds = s.readInt(); + if (numIfBounds > 0) { + ret.additionalInterfaceBounds = new TypePattern[numIfBounds]; + for (int i = 0; i < numIfBounds; i++) { + ret.additionalInterfaceBounds[i] = TypePattern.read(s,context); + } + } + return ret; + } + + public static TypePattern readTypePatternOldStyle(VersionedDataInputStream s, ISourceContext context) throws IOException { + int len = s.readShort(); + NamePattern[] namePatterns = new NamePattern[len]; + for (int i=0; i < len; i++) { + namePatterns[i] = NamePattern.read(s); + } + boolean includeSubtypes = s.readBoolean(); + int dim = s.readInt(); + WildTypePattern ret = new WildTypePattern(namePatterns, includeSubtypes, dim, false,null); + ret.knownMatches = FileUtil.readStringArray(s); + ret.importedPrefixes = FileUtil.readStringArray(s); + ret.readLocation(context, s); + return ret; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/WithinAnnotationPointcut.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/WithinAnnotationPointcut.java new file mode 100644 index 000000000..ba9ce8b26 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/WithinAnnotationPointcut.java @@ -0,0 +1,217 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.ShadowMunger; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +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.ast.Var; + +/** + * @author colyer + * + * TODO To change the template for this generated type comment go to + * Window - Preferences - Java - Code Style - Code Templates + */ +public class WithinAnnotationPointcut extends NameBindingPointcut { + + private AnnotationTypePattern annotationTypePattern; + private String declarationText; + + /** + * + */ + public WithinAnnotationPointcut(AnnotationTypePattern type) { + super(); + this.annotationTypePattern = type; + this.pointcutKind = ATWITHIN; + buildDeclarationText(); + } + + public WithinAnnotationPointcut(AnnotationTypePattern type, ShadowMunger munger) { + this(type); + this.pointcutKind = ATWITHIN; + } + + public AnnotationTypePattern getAnnotationTypePattern() { + return annotationTypePattern; + } + + public int couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS_BITS; + } + + public Pointcut parameterizeWith(Map typeVariableMap,World w) { + WithinAnnotationPointcut ret = new WithinAnnotationPointcut(this.annotationTypePattern.parameterizeWith(typeVariableMap,w)); + ret.copyLocationFrom(this); + return ret; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.Pointcut#fastMatch(org.aspectj.weaver.patterns.FastMatchInfo) + */ + public FuzzyBoolean fastMatch(FastMatchInfo info) { + return annotationTypePattern.fastMatches(info.getType()); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.Pointcut#match(org.aspectj.weaver.Shadow) + */ + protected FuzzyBoolean matchInternal(Shadow shadow) { + ResolvedType enclosingType = shadow.getIWorld().resolve(shadow.getEnclosingType(),true); + if (enclosingType.isMissing()) { + shadow.getIWorld().getLint().cantFindType.signal( + new String[] {WeaverMessages.format(WeaverMessages.CANT_FIND_TYPE_WITHINPCD, + shadow.getEnclosingType().getName())}, + shadow.getSourceLocation(), + new ISourceLocation[]{getSourceLocation()}); +// IMessage msg = new Message( +// WeaverMessages.format(WeaverMessages.CANT_FIND_TYPE_WITHINPCD, +// shadow.getEnclosingType().getName()), +// shadow.getSourceLocation(),true,new ISourceLocation[]{getSourceLocation()}); +// shadow.getIWorld().getMessageHandler().handleMessage(msg); + } + annotationTypePattern.resolve(shadow.getIWorld()); + return annotationTypePattern.matches(enclosingType); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.Pointcut#resolveBindings(org.aspectj.weaver.patterns.IScope, org.aspectj.weaver.patterns.Bindings) + */ + protected void resolveBindings(IScope scope, Bindings bindings) { + if (!scope.getWorld().isInJava5Mode()) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.ATWITHIN_ONLY_SUPPORTED_AT_JAVA5_LEVEL), + getSourceLocation())); + return; + } + annotationTypePattern = annotationTypePattern.resolveBindings(scope,bindings,true); + // must be either a Var, or an annotation type pattern + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.Pointcut#concretize1(org.aspectj.weaver.ResolvedType, org.aspectj.weaver.IntMap) + */ + protected Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + ExactAnnotationTypePattern newType = (ExactAnnotationTypePattern) annotationTypePattern.remapAdviceFormals(bindings); + Pointcut ret = new WithinAnnotationPointcut(newType, bindings.getEnclosingAdvice()); + ret.copyLocationFrom(this); + return ret; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.Pointcut#findResidue(org.aspectj.weaver.Shadow, org.aspectj.weaver.patterns.ExposedState) + */ + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + if (annotationTypePattern instanceof BindingAnnotationTypePattern) { + BindingAnnotationTypePattern btp = (BindingAnnotationTypePattern)annotationTypePattern; + UnresolvedType annotationType = btp.annotationType; + Var var = shadow.getWithinAnnotationVar(annotationType); + + // This should not happen, we shouldn't have gotten this far + // if we weren't going to find the annotation + if (var == null) + throw new BCException("Impossible! annotation=["+annotationType+ + "] shadow=["+shadow+" at "+shadow.getSourceLocation()+ + "] pointcut is at ["+getSourceLocation()+"]"); + + state.set(btp.getFormalIndex(),var); + } + return match(shadow).alwaysTrue() ? Literal.TRUE : Literal.FALSE; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.NameBindingPointcut#getBindingAnnotationTypePatterns() + */ + public List getBindingAnnotationTypePatterns() { + if (annotationTypePattern instanceof BindingAnnotationTypePattern) { + List l = new ArrayList(); + l.add(annotationTypePattern); + return l; + } else return Collections.EMPTY_LIST; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.NameBindingPointcut#getBindingTypePatterns() + */ + public List getBindingTypePatterns() { + return Collections.EMPTY_LIST; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.PatternNode#write(java.io.DataOutputStream) + */ + public void write(DataOutputStream s) throws IOException { + s.writeByte(Pointcut.ATWITHIN); + annotationTypePattern.write(s); + writeLocation(s); + } + + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + AnnotationTypePattern type = AnnotationTypePattern.read(s, context); + WithinAnnotationPointcut ret = new WithinAnnotationPointcut(type); + ret.readLocation(context, s); + return ret; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + if (!(obj instanceof WithinAnnotationPointcut)) return false; + WithinAnnotationPointcut other = (WithinAnnotationPointcut) obj; + return other.annotationTypePattern.equals(this.annotationTypePattern); + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return 17 + 19*annotationTypePattern.hashCode(); + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + private void buildDeclarationText() { + StringBuffer buf = new StringBuffer(); + buf.append("@within("); + String annPatt = annotationTypePattern.toString(); + buf.append(annPatt.startsWith("@") ? annPatt.substring(1) : annPatt); + buf.append(")"); + this.declarationText = buf.toString(); + } + + public String toString() { + return this.declarationText; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/WithinCodeAnnotationPointcut.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/WithinCodeAnnotationPointcut.java new file mode 100644 index 000000000..ed3e5f145 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/WithinCodeAnnotationPointcut.java @@ -0,0 +1,219 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +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.ShadowMunger; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.VersionedDataInputStream; +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.ast.Var; + +/** + * @author colyer + * + * TODO To change the template for this generated type comment go to + * Window - Preferences - Java - Code Style - Code Templates + */ +public class WithinCodeAnnotationPointcut extends NameBindingPointcut { + + private ExactAnnotationTypePattern annotationTypePattern; + private String declarationText; + + private static final int matchedShadowKinds; + static { + int flags = Shadow.ALL_SHADOW_KINDS_BITS; + for (int i = 0; i < Shadow.SHADOW_KINDS.length; i++) { + if (Shadow.SHADOW_KINDS[i].isEnclosingKind()) + flags -= Shadow.SHADOW_KINDS[i].bit; + } + matchedShadowKinds=flags; + } + + public WithinCodeAnnotationPointcut(ExactAnnotationTypePattern type) { + super(); + this.annotationTypePattern = type; + this.pointcutKind = Pointcut.ATWITHINCODE; + buildDeclarationText(); + } + + public WithinCodeAnnotationPointcut(ExactAnnotationTypePattern type, ShadowMunger munger) { + this(type); + this.pointcutKind = Pointcut.ATWITHINCODE; + } + + public ExactAnnotationTypePattern getAnnotationTypePattern() { + return annotationTypePattern; + } + + public int couldMatchKinds() { + return matchedShadowKinds; + } + + public Pointcut parameterizeWith(Map typeVariableMap,World w) { + WithinCodeAnnotationPointcut ret = new WithinCodeAnnotationPointcut((ExactAnnotationTypePattern)this.annotationTypePattern.parameterizeWith(typeVariableMap,w)); + ret.copyLocationFrom(this); + return ret; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.Pointcut#fastMatch(org.aspectj.weaver.patterns.FastMatchInfo) + */ + public FuzzyBoolean fastMatch(FastMatchInfo info) { + return FuzzyBoolean.MAYBE; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.Pointcut#match(org.aspectj.weaver.Shadow) + */ + protected FuzzyBoolean matchInternal(Shadow shadow) { + Member member = shadow.getEnclosingCodeSignature(); + ResolvedMember rMember = member.resolve(shadow.getIWorld()); + + if (rMember == null) { + if (member.getName().startsWith(NameMangler.PREFIX)) { + return FuzzyBoolean.NO; + } + shadow.getIWorld().getLint().unresolvableMember.signal(member.toString(), getSourceLocation()); + return FuzzyBoolean.NO; + } + + annotationTypePattern.resolve(shadow.getIWorld()); + return annotationTypePattern.matches(rMember); + } + + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.Pointcut#resolveBindings(org.aspectj.weaver.patterns.IScope, org.aspectj.weaver.patterns.Bindings) + */ + protected void resolveBindings(IScope scope, Bindings bindings) { + if (!scope.getWorld().isInJava5Mode()) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.ATWITHINCODE_ONLY_SUPPORTED_AT_JAVA5_LEVEL), + getSourceLocation())); + return; + } + annotationTypePattern = (ExactAnnotationTypePattern) annotationTypePattern.resolveBindings(scope,bindings,true); + // must be either a Var, or an annotation type pattern + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.Pointcut#concretize1(org.aspectj.weaver.ResolvedType, org.aspectj.weaver.IntMap) + */ + protected Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + ExactAnnotationTypePattern newType = (ExactAnnotationTypePattern) annotationTypePattern.remapAdviceFormals(bindings); + Pointcut ret = new WithinCodeAnnotationPointcut(newType, bindings.getEnclosingAdvice()); + ret.copyLocationFrom(this); + return ret; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.Pointcut#findResidue(org.aspectj.weaver.Shadow, org.aspectj.weaver.patterns.ExposedState) + */ + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + + if (annotationTypePattern instanceof BindingAnnotationTypePattern) { + BindingAnnotationTypePattern btp = (BindingAnnotationTypePattern)annotationTypePattern; + UnresolvedType annotationType = btp.annotationType; + Var var = shadow.getWithinCodeAnnotationVar(annotationType); + + // This should not happen, we shouldn't have gotten this far + // if we weren't going to find the annotation + if (var == null) + throw new BCException("Impossible! annotation=["+annotationType+ + "] shadow=["+shadow+" at "+shadow.getSourceLocation()+ + "] pointcut is at ["+getSourceLocation()+"]"); + + state.set(btp.getFormalIndex(),var); + } + if (matchInternal(shadow).alwaysTrue()) return Literal.TRUE; + else return Literal.FALSE; + } + + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.NameBindingPointcut#getBindingAnnotationTypePatterns() + */ + public List getBindingAnnotationTypePatterns() { + if (annotationTypePattern instanceof BindingAnnotationTypePattern) { + List l = new ArrayList(); + l.add(annotationTypePattern); + return l; + } else return Collections.EMPTY_LIST; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.NameBindingPointcut#getBindingTypePatterns() + */ + public List getBindingTypePatterns() { + return Collections.EMPTY_LIST; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.PatternNode#write(java.io.DataOutputStream) + */ + public void write(DataOutputStream s) throws IOException { + s.writeByte(Pointcut.ATWITHINCODE); + annotationTypePattern.write(s); + writeLocation(s); + } + + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + AnnotationTypePattern type = AnnotationTypePattern.read(s, context); + WithinCodeAnnotationPointcut ret = new WithinCodeAnnotationPointcut((ExactAnnotationTypePattern)type); + ret.readLocation(context, s); + return ret; + } + + public boolean equals(Object other) { + if (!(other instanceof WithinCodeAnnotationPointcut)) return false; + WithinCodeAnnotationPointcut o = (WithinCodeAnnotationPointcut)other; + return o.annotationTypePattern.equals(this.annotationTypePattern); + } + + public int hashCode() { + int result = 17; + result = 23*result + annotationTypePattern.hashCode(); + return result; + } + + private void buildDeclarationText() { + StringBuffer buf = new StringBuffer(); + buf.append("@withincode("); + String annPatt = annotationTypePattern.toString(); + buf.append(annPatt.startsWith("@") ? annPatt.substring(1) : annPatt); + buf.append(")"); + this.declarationText = buf.toString(); + } + + public String toString() { return this.declarationText; } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/WithinPointcut.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/WithinPointcut.java new file mode 100644 index 000000000..aaab8355b --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/WithinPointcut.java @@ -0,0 +1,152 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.util.Map; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Test; + +public class WithinPointcut extends Pointcut { + private TypePattern typePattern; + + public WithinPointcut(TypePattern type) { + this.typePattern = type; + this.pointcutKind = WITHIN; + } + + public TypePattern getTypePattern() { + return typePattern; + } + + private FuzzyBoolean isWithinType(ResolvedType type) { + while (type != null) { + if (typePattern.matchesStatically(type)) { + return FuzzyBoolean.YES; + } + type = type.getDeclaringType(); + } + return FuzzyBoolean.NO; + } + + public int couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS_BITS; + } + + public Pointcut parameterizeWith(Map typeVariableMap,World w) { + WithinPointcut ret = new WithinPointcut(this.typePattern.parameterizeWith(typeVariableMap,w)); + ret.copyLocationFrom(this); + return ret; + } + + public FuzzyBoolean fastMatch(FastMatchInfo info) { + if (typePattern.annotationPattern instanceof AnyAnnotationTypePattern) { + return isWithinType(info.getType()); + } + return FuzzyBoolean.MAYBE; + } + + protected FuzzyBoolean matchInternal(Shadow shadow) { + ResolvedType enclosingType = shadow.getIWorld().resolve(shadow.getEnclosingType(),true); + if (enclosingType.isMissing()) { + shadow.getIWorld().getLint().cantFindType.signal( + new String[] {WeaverMessages.format(WeaverMessages.CANT_FIND_TYPE_WITHINPCD, + shadow.getEnclosingType().getName())}, + shadow.getSourceLocation(), + new ISourceLocation[]{getSourceLocation()}); +// IMessage msg = new Message( +// WeaverMessages.format(WeaverMessages.CANT_FIND_TYPE_WITHINPCD, +// shadow.getEnclosingType().getName()), +// shadow.getSourceLocation(),true,new ISourceLocation[]{getSourceLocation()}); +// shadow.getIWorld().getMessageHandler().handleMessage(msg); + } + typePattern.resolve(shadow.getIWorld()); + return isWithinType(enclosingType); + } + + public void write(DataOutputStream s) throws IOException { + s.writeByte(Pointcut.WITHIN); + typePattern.write(s); + writeLocation(s); + } + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + TypePattern type = TypePattern.read(s, context); + WithinPointcut ret = new WithinPointcut(type); + ret.readLocation(context, s); + return ret; + } + + public void resolveBindings(IScope scope, Bindings bindings) { + typePattern = typePattern.resolveBindings(scope, bindings, false, false); + + // look for parameterized type patterns which are not supported... + HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor + visitor = new HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor(); + typePattern.traverse(visitor, null); + if (visitor.wellHasItThen/*?*/()) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.WITHIN_PCD_DOESNT_SUPPORT_PARAMETERS), + getSourceLocation())); + } + } + + public void postRead(ResolvedType enclosingType) { + typePattern.postRead(enclosingType); + } + + public boolean couldEverMatchSameJoinPointsAs(WithinPointcut other) { + return typePattern.couldEverMatchSameTypesAs(other.typePattern); + } + + public boolean equals(Object other) { + if (!(other instanceof WithinPointcut)) return false; + WithinPointcut o = (WithinPointcut)other; + return o.typePattern.equals(this.typePattern); + } + public int hashCode() { + int result = 43; + result = 37*result + typePattern.hashCode(); + return result; + } + + public String toString() { + return "within(" + typePattern + ")"; + } + + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + return match(shadow).alwaysTrue() ? Literal.TRUE : Literal.FALSE; + } + + + public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + Pointcut ret = new WithinPointcut(typePattern); + ret.copyLocationFrom(this); + return ret; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/patterns/WithincodePointcut.java b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/WithincodePointcut.java new file mode 100644 index 000000000..91beeb87c --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/patterns/WithincodePointcut.java @@ -0,0 +1,142 @@ +/* ******************************************************************* + * 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 java.io.DataOutputStream; +import java.io.IOException; +import java.util.Map; + +import org.aspectj.bridge.MessageUtil; +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.VersionedDataInputStream; +import org.aspectj.weaver.WeaverMessages; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Test; + +public class WithincodePointcut extends Pointcut { + private SignaturePattern signature; + private static final int matchedShadowKinds; + static { + int flags = Shadow.ALL_SHADOW_KINDS_BITS; + for (int i = 0; i < Shadow.SHADOW_KINDS.length; i++) { + if (Shadow.SHADOW_KINDS[i].isEnclosingKind()) + flags -= Shadow.SHADOW_KINDS[i].bit; + } + // these next two are needed for inlining of field initializers + flags|=Shadow.ConstructorExecution.bit; + flags|=Shadow.Initialization.bit; + matchedShadowKinds = flags; + } + + public WithincodePointcut(SignaturePattern signature) { + this.signature = signature; + this.pointcutKind = WITHINCODE; + } + + public SignaturePattern getSignature() { + return signature; + } + + public int couldMatchKinds() { + return matchedShadowKinds; + } + + public Pointcut parameterizeWith(Map typeVariableMap,World w) { + WithincodePointcut ret = new WithincodePointcut(signature.parameterizeWith(typeVariableMap,w)); + ret.copyLocationFrom(this); + return ret; + } + + public FuzzyBoolean fastMatch(FastMatchInfo type) { + return FuzzyBoolean.MAYBE; + } + + protected FuzzyBoolean matchInternal(Shadow shadow) { + //This will not match code in local or anonymous classes as if + //they were withincode of the outer signature + return FuzzyBoolean.fromBoolean( + signature.matches(shadow.getEnclosingCodeSignature(), shadow.getIWorld(), false)); + } + + public void write(DataOutputStream s) throws IOException { + s.writeByte(Pointcut.WITHINCODE); + signature.write(s); + writeLocation(s); + } + + public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException { + WithincodePointcut ret = new WithincodePointcut(SignaturePattern.read(s, context)); + ret.readLocation(context, s); + return ret; + } + + public void resolveBindings(IScope scope, Bindings bindings) { + signature = signature.resolveBindings(scope, bindings); + + // look for inappropriate use of parameterized types and tell user... + HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor + visitor = new HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor(); + signature.getDeclaringType().traverse(visitor, null); + if (visitor.wellHasItThen/*?*/()) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.WITHINCODE_DOESNT_SUPPORT_PARAMETERIZED_DECLARING_TYPES), + getSourceLocation())); + } + + visitor = new HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor(); + signature.getThrowsPattern().traverse(visitor, null); + if (visitor.wellHasItThen/*?*/()) { + scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.NO_GENERIC_THROWABLES), + getSourceLocation())); + } + } + + public void postRead(ResolvedType enclosingType) { + signature.postRead(enclosingType); + } + + public boolean equals(Object other) { + if (!(other instanceof WithincodePointcut)) return false; + WithincodePointcut o = (WithincodePointcut)other; + return o.signature.equals(this.signature); + } + public int hashCode() { + int result = 43; + result = 37*result + signature.hashCode(); + return result; + } + + public String toString() { + return "withincode(" + signature + ")"; + } + + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + return match(shadow).alwaysTrue() ? Literal.TRUE : Literal.FALSE; + } + + + public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) { + Pointcut ret = new WithincodePointcut(signature); + ret.copyLocationFrom(this); + return ret; + } + + public Object accept(PatternNodeVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/reflect/AnnotationFinder.java b/org.aspectj.matcher/src/org/aspectj/weaver/reflect/AnnotationFinder.java new file mode 100644 index 000000000..7efeac9ae --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/reflect/AnnotationFinder.java @@ -0,0 +1,45 @@ +/* ******************************************************************* + * 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.Member; +import java.util.Set; + +import org.aspectj.weaver.AnnotationAJ; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.World; + +/** + * @author colyer Used in 1.4 code to access annotations safely + */ +public interface AnnotationFinder { + + void setClassLoader(ClassLoader annotationLoader); + + void setWorld(World aWorld); + + Object getAnnotation(ResolvedType annotationType, Object onObject); + + Object getAnnotationFromMember(ResolvedType annotationType, Member aMember); + + public AnnotationAJ getAnnotationOfType(UnresolvedType ofType, + Member onMember); + + public String getAnnotationDefaultValue(Member onMember); + + Object getAnnotationFromClass(ResolvedType annotationType, Class aClass); + + Set/* ResolvedType */getAnnotations(Member onMember); + + ResolvedType[][] getParameterAnnotationTypes(Member onMember); +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/reflect/GenericSignatureInformationProvider.java b/org.aspectj.matcher/src/org/aspectj/weaver/reflect/GenericSignatureInformationProvider.java new file mode 100644 index 000000000..fce972348 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/reflect/GenericSignatureInformationProvider.java @@ -0,0 +1,31 @@ +/* ******************************************************************* + * 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 org.aspectj.weaver.UnresolvedType; + +/** + * This interface exists to support two different strategies for answering + * generic signature related questions on Java 5 and pre-Java 5. + */ +public interface GenericSignatureInformationProvider { + + UnresolvedType[] getGenericParameterTypes(ReflectionBasedResolvedMemberImpl resolvedMember); + + UnresolvedType getGenericReturnType(ReflectionBasedResolvedMemberImpl resolvedMember); + + boolean isBridge(ReflectionBasedResolvedMemberImpl resolvedMember); + + boolean isVarArgs(ReflectionBasedResolvedMemberImpl resolvedMember); + + boolean isSynthetic(ReflectionBasedResolvedMemberImpl resolvedMember); +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/reflect/IReflectionWorld.java b/org.aspectj.matcher/src/org/aspectj/weaver/reflect/IReflectionWorld.java new file mode 100644 index 000000000..60d90d357 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/reflect/IReflectionWorld.java @@ -0,0 +1,19 @@ +/* ******************************************************************* + * 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: + * Ron Bodkin initial implementation + * ******************************************************************/ + package org.aspectj.weaver.reflect; + +import org.aspectj.weaver.ResolvedType; + +public interface IReflectionWorld { + public AnnotationFinder getAnnotationFinder(); + public ResolvedType resolve(Class aClass); +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/reflect/Java14GenericSignatureInformationProvider.java b/org.aspectj.matcher/src/org/aspectj/weaver/reflect/Java14GenericSignatureInformationProvider.java new file mode 100644 index 000000000..91b32ff0e --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/reflect/Java14GenericSignatureInformationProvider.java @@ -0,0 +1,60 @@ +/* ******************************************************************* + * 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 org.aspectj.weaver.UnresolvedType; + +/** + * Under JDK 1.4 or lower, we can't give generic signature info... + */ +public class Java14GenericSignatureInformationProvider implements + GenericSignatureInformationProvider { + + /* (non-Javadoc) + * @see org.aspectj.weaver.reflect.GenericSignatureInformationProvider#getGenericParameterTypes(org.aspectj.weaver.reflect.ReflectionBasedResolvedMemberImpl) + */ + public UnresolvedType[] getGenericParameterTypes( + ReflectionBasedResolvedMemberImpl resolvedMember) { + return resolvedMember.getParameterTypes(); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.reflect.GenericSignatureInformationProvider#getGenericReturnType(org.aspectj.weaver.reflect.ReflectionBasedResolvedMemberImpl) + */ + public UnresolvedType getGenericReturnType( + ReflectionBasedResolvedMemberImpl resolvedMember) { + return resolvedMember.getReturnType(); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.reflect.GenericSignatureInformationProvider#isBridge() + */ + public boolean isBridge(ReflectionBasedResolvedMemberImpl resolvedMember) { + return false; + } + + + /* (non-Javadoc) + * @see org.aspectj.weaver.reflect.GenericSignatureInformationProvider#isVarArgs() + */ + public boolean isVarArgs(ReflectionBasedResolvedMemberImpl resolvedMember) { + return false; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.reflect.GenericSignatureInformationProvider#isSynthetic() + */ + public boolean isSynthetic(ReflectionBasedResolvedMemberImpl resolvedMember) { + return false; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/reflect/JoinPointMatchImpl.java b/org.aspectj.matcher/src/org/aspectj/weaver/reflect/JoinPointMatchImpl.java new file mode 100644 index 000000000..69a5f4577 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/reflect/JoinPointMatchImpl.java @@ -0,0 +1,53 @@ +/* ******************************************************************* + * 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 org.aspectj.weaver.tools.JoinPointMatch; +import org.aspectj.weaver.tools.PointcutParameter; + +/** + * @author colyer + * Implementation of JoinPointMatch for reflection based worlds. + */ +public class JoinPointMatchImpl implements JoinPointMatch { + + public final static JoinPointMatch NO_MATCH = new JoinPointMatchImpl(); + private final static PointcutParameter[] NO_BINDINGS = new PointcutParameter[0]; + + private boolean match; + private PointcutParameter[] bindings; + + public JoinPointMatchImpl(PointcutParameter[] bindings) { + this.match = true; + this.bindings = bindings; + } + + private JoinPointMatchImpl() { + this.match = false; + this.bindings = NO_BINDINGS; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.tools.JoinPointMatch#matches() + */ + public boolean matches() { + return match; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.tools.JoinPointMatch#getParameterBindings() + */ + public PointcutParameter[] getParameterBindings() { + return bindings; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/reflect/PointcutParameterImpl.java b/org.aspectj.matcher/src/org/aspectj/weaver/reflect/PointcutParameterImpl.java new file mode 100644 index 000000000..722d64839 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/reflect/PointcutParameterImpl.java @@ -0,0 +1,43 @@ +/* ******************************************************************* + * 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 org.aspectj.weaver.tools.PointcutParameter; + +public class PointcutParameterImpl implements PointcutParameter { + + String name; + Class type; + Object binding; + + public PointcutParameterImpl(String name, Class type) { + this.name = name; + this.type = type; + } + + public String getName() { + return name; + } + + public Class getType() { + return type; + } + + public Object getBinding() { + return binding; + } + + void setBinding(Object boundValue) { + this.binding = boundValue; + } + +} \ No newline at end of file diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/reflect/ReflectionBasedReferenceTypeDelegate.java b/org.aspectj.matcher/src/org/aspectj/weaver/reflect/ReflectionBasedReferenceTypeDelegate.java new file mode 100644 index 000000000..b96611a05 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/reflect/ReflectionBasedReferenceTypeDelegate.java @@ -0,0 +1,442 @@ +/* ******************************************************************* + * 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.net.URL; +import java.net.URLClassLoader; +import java.util.Collection; +import java.util.Collections; + +import org.aspectj.weaver.AnnotationAJ; +import org.aspectj.weaver.AnnotationTargetKind; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.ReferenceType; +import org.aspectj.weaver.ReferenceTypeDelegate; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.SourceContextImpl; +import org.aspectj.weaver.TypeVariable; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.WeakClassLoaderReference; +import org.aspectj.weaver.WeaverStateInfo; +import org.aspectj.weaver.World; +import org.aspectj.weaver.patterns.PerClause; + +/** + * @author colyer A delegate for a resolved type that uses runtime type information (java.lang.reflect) to answer questions. This + * class uses only Java 1.4 features to answer questions. In a Java 1.5 environment use the + * Java5ReflectionBasedReferenceTypeDelegate subtype. + */ +public class ReflectionBasedReferenceTypeDelegate implements ReferenceTypeDelegate { + + private static final ClassLoader bootClassLoader = new URLClassLoader(new URL[0]);// ReflectionBasedReferenceTypeDelegate.class. + // getClassLoader(); + + protected Class myClass = null; + protected WeakClassLoaderReference classLoaderReference = null; + private World world; + private ReferenceType resolvedType; + private ResolvedMember[] fields = null; + private ResolvedMember[] methods = null; + private ResolvedType[] interfaces = null; + + public ReflectionBasedReferenceTypeDelegate(Class forClass, ClassLoader aClassLoader, World inWorld, ReferenceType resolvedType) { + initialize(resolvedType, forClass, aClassLoader, inWorld); + } + + /** for reflective construction only */ + public ReflectionBasedReferenceTypeDelegate() { + } + + public void initialize(ReferenceType aType, Class aClass, ClassLoader aClassLoader, World aWorld) { + this.myClass = aClass; + this.resolvedType = aType; + this.world = aWorld; + this.classLoaderReference = new WeakClassLoaderReference((aClassLoader != null) ? aClassLoader : bootClassLoader); + } + + protected Class getBaseClass() { + return this.myClass; + } + + protected World getWorld() { + return this.world; + } + + public ReferenceType buildGenericType() { + throw new UnsupportedOperationException("Shouldn't be asking for generic type at 1.4 source level or lower"); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#addAnnotation(org.aspectj.weaver .AnnotationX) + */ + public void addAnnotation(AnnotationAJ annotationX) { + throw new UnsupportedOperationException("Cannot add an annotation to a reflection based delegate"); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#isAspect() + */ + public boolean isAspect() { + // we could do better than this in Java 5 by looking at the annotations + // on the type... + return false; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#isAnnotationStyleAspect() + */ + public boolean isAnnotationStyleAspect() { + // we could do better than this in Java 5 by looking at the annotations + // on the type... + return false; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#isInterface() + */ + public boolean isInterface() { + return this.myClass.isInterface(); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#isEnum() + */ + public boolean isEnum() { + // cant be an enum in Java 1.4 or prior + return false; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#isAnnotation() + */ + public boolean isAnnotation() { + // cant be an annotation in Java 1.4 or prior + return false; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#isAnnotationWithRuntimeRetention () + */ + public boolean isAnnotationWithRuntimeRetention() { + // cant be an annotation in Java 1.4 or prior + return false; + } + + public String getRetentionPolicy() { + // cant be an annotation in Java 1.4 or prior + return null; + } + + public boolean canAnnotationTargetType() { + return false; + } + + public AnnotationTargetKind[] getAnnotationTargetKinds() { + return null; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#isClass() + */ + public boolean isClass() { + return !this.myClass.isInterface() && !this.myClass.isPrimitive() && !this.myClass.isArray(); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#isGeneric() + */ + public boolean isGeneric() { + // cant be generic in 1.4 + return false; + } + + public boolean isAnonymous() { + return false; + } + + public boolean isNested() { + // FIXME this is *wrong* but isMemberClass() doesnt exist in pre-1.5... + // (same deal as isAnonymous above...) + return true; + // boolean member = this.myClass.isMemberClass(); + // return member; + } + + public ResolvedType getOuterClass() { + // FIXME getEnclosingClass() is Java5 ... dammit + // return + // ReflectionBasedReferenceTypeDelegateFactory.resolveTypeInWorld( + // myClass.getEnclosingClass(),world); + return null; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#isExposedToWeaver() + */ + public boolean isExposedToWeaver() { + // reflection based types are never exposed to the weaver + return false; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#hasAnnotation(org.aspectj.weaver .UnresolvedType) + */ + public boolean hasAnnotation(UnresolvedType ofType) { + // in Java 1.4 we cant have an annotation + return false; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#getAnnotations() + */ + public AnnotationAJ[] getAnnotations() { + // no annotations in Java 1.4 + return AnnotationAJ.EMPTY_ARRAY; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#getAnnotationTypes() + */ + public ResolvedType[] getAnnotationTypes() { + // no annotations in Java 1.4 + return new ResolvedType[0]; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#getDeclaredFields() + */ + public ResolvedMember[] getDeclaredFields() { + if (fields == null) { + Field[] reflectFields = this.myClass.getDeclaredFields(); + ResolvedMember[] rFields = new ResolvedMember[reflectFields.length]; + for (int i = 0; i < reflectFields.length; i++) { + rFields[i] = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(reflectFields[i], world); + } + this.fields = rFields; + } + return fields; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#getDeclaredInterfaces() + */ + public ResolvedType[] getDeclaredInterfaces() { + if (interfaces == null) { + Class[] reflectInterfaces = this.myClass.getInterfaces(); + ResolvedType[] rInterfaces = new ResolvedType[reflectInterfaces.length]; + for (int i = 0; i < reflectInterfaces.length; i++) { + rInterfaces[i] = ReflectionBasedReferenceTypeDelegateFactory.resolveTypeInWorld(reflectInterfaces[i], world); + } + this.interfaces = rInterfaces; + } + return interfaces; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#getDeclaredMethods() + */ + public ResolvedMember[] getDeclaredMethods() { + if (methods == null) { + Method[] reflectMethods = this.myClass.getDeclaredMethods(); + Constructor[] reflectCons = this.myClass.getDeclaredConstructors(); + ResolvedMember[] rMethods = new ResolvedMember[reflectMethods.length + reflectCons.length]; + for (int i = 0; i < reflectMethods.length; i++) { + rMethods[i] = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(reflectMethods[i], world); + } + for (int i = 0; i < reflectCons.length; i++) { + rMethods[i + reflectMethods.length] = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember( + reflectCons[i], world); + } + this.methods = rMethods; + } + return methods; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#getDeclaredPointcuts() + */ + public ResolvedMember[] getDeclaredPointcuts() { + return new ResolvedMember[0]; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#getTypeVariables() + */ + public TypeVariable[] getTypeVariables() { + // no type variables in Java 1.4 + return new TypeVariable[0]; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#getPerClause() + */ + public PerClause getPerClause() { + // no per clause... + return null; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#getDeclares() + */ + public Collection getDeclares() { + // no declares + return Collections.EMPTY_SET; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#getTypeMungers() + */ + public Collection getTypeMungers() { + // no type mungers + return Collections.EMPTY_SET; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#getPrivilegedAccesses() + */ + public Collection getPrivilegedAccesses() { + // no aspect members..., not used for weaving + return Collections.EMPTY_SET; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#getModifiers() + */ + public int getModifiers() { + return this.myClass.getModifiers(); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#getSuperclass() + */ + public ResolvedType getSuperclass() { + if (this.myClass.getSuperclass() == null) { + if (myClass == Object.class) { + return null; + } + return world.resolve(UnresolvedType.OBJECT); + } + return ReflectionBasedReferenceTypeDelegateFactory.resolveTypeInWorld(this.myClass.getSuperclass(), world); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#getWeaverState() + */ + public WeaverStateInfo getWeaverState() { + return null; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#getResolvedTypeX() + */ + public ReferenceType getResolvedTypeX() { + return this.resolvedType; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#doesNotExposeShadowMungers() + */ + public boolean doesNotExposeShadowMungers() { + return false; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ReferenceTypeDelegate#getDeclaredGenericSignature() + */ + public String getDeclaredGenericSignature() { + // no generic sig in 1.4 + return null; + } + + public void ensureDelegateConsistent() { + // Nothing to do - a reflection based delegate can't become + // inconsistent... + } + + public ReflectionBasedResolvedMemberImpl createResolvedMemberFor(Member aMember) { + return null; + } + + public String getSourcefilename() { + // crappy guess.. + return resolvedType.getName() + ".class"; + } + + public ISourceContext getSourceContext() { + return SourceContextImpl.UNKNOWN_SOURCE_CONTEXT; + } + + public boolean copySourceContext() { + return true; + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/reflect/ReflectionBasedReferenceTypeDelegateFactory.java b/org.aspectj.matcher/src/org/aspectj/weaver/reflect/ReflectionBasedReferenceTypeDelegateFactory.java new file mode 100644 index 000000000..dc315bd41 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/reflect/ReflectionBasedReferenceTypeDelegateFactory.java @@ -0,0 +1,218 @@ +/* ******************************************************************* + * 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.InvocationTargetException; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import org.aspectj.util.LangUtil; +import org.aspectj.weaver.ReferenceType; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedMemberImpl; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.World; + +/** + * @author colyer + * Creates the appropriate ReflectionBasedReferenceTypeDelegate according to + * the VM level we are running at. Uses reflection to avoid 1.5 dependencies in + * 1.4 and 1.3 code base. + */ +public class ReflectionBasedReferenceTypeDelegateFactory { + + public static ReflectionBasedReferenceTypeDelegate + createDelegate(ReferenceType forReferenceType, World inWorld, ClassLoader usingClassLoader) { + try { + Class c = Class.forName(forReferenceType.getName(),false,usingClassLoader); + if (LangUtil.is15VMOrGreater()) { + ReflectionBasedReferenceTypeDelegate rbrtd = create15Delegate(forReferenceType,c,usingClassLoader,inWorld); + if (rbrtd!=null) return rbrtd; // can be null if we didn't find the class the delegate logic loads + } + return new ReflectionBasedReferenceTypeDelegate(c,usingClassLoader,inWorld,forReferenceType); + } catch (ClassNotFoundException cnfEx) { + return null; + } + } + + // can return 'null' if we can't find the class + private static ReflectionBasedReferenceTypeDelegate create15Delegate(ReferenceType forReferenceType, Class forClass, ClassLoader usingClassLoader, World inWorld) { + try { + Class delegateClass = Class.forName("org.aspectj.weaver.reflect.Java15ReflectionBasedReferenceTypeDelegate"); + ReflectionBasedReferenceTypeDelegate ret = (ReflectionBasedReferenceTypeDelegate) delegateClass.newInstance(); + ret.initialize(forReferenceType,forClass,usingClassLoader,inWorld); + return ret; + } catch (ClassNotFoundException cnfEx) { + throw new IllegalStateException("Attempted to create Java 1.5 reflection based delegate but org.aspectj.weaver.reflect.Java15ReflectionBasedReferenceTypeDelegate was not found on classpath"); + } catch (InstantiationException insEx) { + throw new IllegalStateException("Attempted to create Java 1.5 reflection based delegate but InstantiationException: " + insEx + " occured"); + } catch (IllegalAccessException illAccEx) { + throw new IllegalStateException("Attempted to create Java 1.5 reflection based delegate but IllegalAccessException: " + illAccEx + " occured"); + } + } + + private static GenericSignatureInformationProvider createGenericSignatureProvider(World inWorld) { + if (LangUtil.is15VMOrGreater()) { + try { + Class providerClass = Class.forName("org.aspectj.weaver.reflect.Java15GenericSignatureInformationProvider"); + Constructor cons = providerClass.getConstructor(new Class[] {World.class}); + GenericSignatureInformationProvider ret = (GenericSignatureInformationProvider) cons.newInstance(new Object[] {inWorld}); + return ret; + } catch (ClassNotFoundException cnfEx) { + // drop through and create a 14 provider... + // throw new IllegalStateException("Attempted to create Java 1.5 generic signature provider but org.aspectj.weaver.reflect.Java15GenericSignatureInformationProvider was not found on classpath"); + } catch (NoSuchMethodException nsmEx) { + throw new IllegalStateException("Attempted to create Java 1.5 generic signature provider but: " + nsmEx + " occured"); + } catch (InstantiationException insEx) { + throw new IllegalStateException("Attempted to create Java 1.5 generic signature provider but: " + insEx + " occured"); + } catch (InvocationTargetException invEx) { + throw new IllegalStateException("Attempted to create Java 1.5 generic signature provider but: " + invEx + " occured"); + } catch (IllegalAccessException illAcc) { + throw new IllegalStateException("Attempted to create Java 1.5 generic signature provider but: " + illAcc + " occured"); + } + } + return new Java14GenericSignatureInformationProvider(); + } + + /** + * convert a java.lang.reflect.Member into a resolved member in the world + * @param reflectMember + * @param inWorld + * @return + */ + public static ResolvedMember createResolvedMember(Member reflectMember, World inWorld) { + if (reflectMember instanceof Method) { + return createResolvedMethod((Method)reflectMember,inWorld); + } else if (reflectMember instanceof Constructor) { + return createResolvedConstructor((Constructor)reflectMember,inWorld); + } else { + return createResolvedField((Field)reflectMember,inWorld); + } + } + + public static ResolvedMember createResolvedMethod(Method aMethod, World inWorld) { + ReflectionBasedResolvedMemberImpl ret = new ReflectionBasedResolvedMemberImpl(org.aspectj.weaver.Member.METHOD, + toResolvedType(aMethod.getDeclaringClass(),(IReflectionWorld)inWorld), + aMethod.getModifiers(), + toResolvedType(aMethod.getReturnType(),(IReflectionWorld)inWorld), + aMethod.getName(), + toResolvedTypeArray(aMethod.getParameterTypes(),inWorld), + toResolvedTypeArray(aMethod.getExceptionTypes(),inWorld), + aMethod + ); + if (inWorld instanceof IReflectionWorld) { + ret.setAnnotationFinder(((IReflectionWorld)inWorld).getAnnotationFinder()); + } + ret.setGenericSignatureInformationProvider(createGenericSignatureProvider(inWorld)); + return ret; + } + + public static ResolvedMember createResolvedAdviceMember(Method aMethod, World inWorld) { + ReflectionBasedResolvedMemberImpl ret = + new ReflectionBasedResolvedMemberImpl(org.aspectj.weaver.Member.ADVICE, + toResolvedType(aMethod.getDeclaringClass(),(IReflectionWorld)inWorld), + aMethod.getModifiers(), + toResolvedType(aMethod.getReturnType(),(IReflectionWorld)inWorld), + aMethod.getName(), + toResolvedTypeArray(aMethod.getParameterTypes(),inWorld), + toResolvedTypeArray(aMethod.getExceptionTypes(),inWorld), + aMethod + ); + if (inWorld instanceof IReflectionWorld) { + ret.setAnnotationFinder(((IReflectionWorld)inWorld).getAnnotationFinder()); + } + ret.setGenericSignatureInformationProvider(createGenericSignatureProvider(inWorld)); + return ret; + } + + public static ResolvedMember createStaticInitMember(Class forType, World inWorld) { + return new ResolvedMemberImpl(org.aspectj.weaver.Member.STATIC_INITIALIZATION, + toResolvedType(forType,(IReflectionWorld)inWorld), + Modifier.STATIC, + ResolvedType.VOID, + "", + new UnresolvedType[0], + new UnresolvedType[0] + ); + } + + public static ResolvedMember createResolvedConstructor(Constructor aConstructor, World inWorld) { + ReflectionBasedResolvedMemberImpl ret = + new ReflectionBasedResolvedMemberImpl(org.aspectj.weaver.Member.CONSTRUCTOR, + toResolvedType(aConstructor.getDeclaringClass(),(IReflectionWorld)inWorld), + aConstructor.getModifiers(), + // to return what BCEL returns, the return type for ctor is void + ResolvedType.VOID,//toResolvedType(aConstructor.getDeclaringClass(),(IReflectionWorld)inWorld), + "", + toResolvedTypeArray(aConstructor.getParameterTypes(),inWorld), + toResolvedTypeArray(aConstructor.getExceptionTypes(),inWorld), + aConstructor + ); + if (inWorld instanceof IReflectionWorld) { + ret.setAnnotationFinder(((IReflectionWorld)inWorld).getAnnotationFinder()); + } + ret.setGenericSignatureInformationProvider(createGenericSignatureProvider(inWorld)); + return ret; + } + + public static ResolvedMember createResolvedField(Field aField, World inWorld) { + ReflectionBasedResolvedMemberImpl ret = + new ReflectionBasedResolvedMemberImpl(org.aspectj.weaver.Member.FIELD, + toResolvedType(aField.getDeclaringClass(),(IReflectionWorld)inWorld), + aField.getModifiers(), + toResolvedType(aField.getType(),(IReflectionWorld)inWorld), + aField.getName(), + new UnresolvedType[0], + aField); + if (inWorld instanceof IReflectionWorld) { + ret.setAnnotationFinder(((IReflectionWorld)inWorld).getAnnotationFinder()); + } + ret.setGenericSignatureInformationProvider(createGenericSignatureProvider(inWorld)); + return ret; + } + + public static ResolvedMember createHandlerMember(Class exceptionType, Class inType,World inWorld) { + return new ResolvedMemberImpl( + org.aspectj.weaver.Member.HANDLER, + toResolvedType(inType,(IReflectionWorld)inWorld), + Modifier.STATIC, + "", + "(" + inWorld.resolve(exceptionType.getName()).getSignature() + ")V"); + } + + public static ResolvedType resolveTypeInWorld(Class aClass, World aWorld) { +// classes that represent arrays return a class name that is the signature of the array type, ho-hum... + String className = aClass.getName(); + if (aClass.isArray()) { + return aWorld.resolve(UnresolvedType.forSignature(className.replace('.','/'))); + } + else{ + return aWorld.resolve(className); + } + } + + private static ResolvedType toResolvedType(Class aClass, IReflectionWorld aWorld) { + return aWorld.resolve(aClass); + } + + private static ResolvedType[] toResolvedTypeArray(Class[] classes, World inWorld) { + ResolvedType[] ret = new ResolvedType[classes.length]; + for (int i = 0; i < ret.length; i++) { + ret[i] = ((IReflectionWorld)inWorld).resolve(classes[i]); + } + return ret; + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/reflect/ReflectionBasedResolvedMemberImpl.java b/org.aspectj.matcher/src/org/aspectj/weaver/reflect/ReflectionBasedResolvedMemberImpl.java new file mode 100644 index 000000000..f982f89c3 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/reflect/ReflectionBasedResolvedMemberImpl.java @@ -0,0 +1,216 @@ +/* ******************************************************************* + * 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.Member; +import java.util.Iterator; + +import org.aspectj.weaver.AnnotationAJ; +import org.aspectj.weaver.MemberKind; +import org.aspectj.weaver.ResolvedMember; +import org.aspectj.weaver.ResolvedMemberImpl; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; + +/** + * Subtype of ResolvedMemberImpl used in reflection world. Knows how to get + * annotations from a java.lang.reflect.Member + * + */ +public class ReflectionBasedResolvedMemberImpl extends ResolvedMemberImpl { + + private AnnotationFinder annotationFinder = null; + private GenericSignatureInformationProvider gsigInfoProvider = new Java14GenericSignatureInformationProvider(); + + private Member reflectMember; + + /** + * @param kind + * @param declaringType + * @param modifiers + * @param returnType + * @param name + * @param parameterTypes + */ + public ReflectionBasedResolvedMemberImpl(MemberKind kind, + UnresolvedType declaringType, int modifiers, + UnresolvedType returnType, String name, + UnresolvedType[] parameterTypes, Member reflectMember) { + super(kind, declaringType, modifiers, returnType, name, parameterTypes); + this.reflectMember = reflectMember; + } + + /** + * @param kind + * @param declaringType + * @param modifiers + * @param returnType + * @param name + * @param parameterTypes + * @param checkedExceptions + */ + public ReflectionBasedResolvedMemberImpl(MemberKind kind, + UnresolvedType declaringType, int modifiers, + UnresolvedType returnType, String name, + UnresolvedType[] parameterTypes, + UnresolvedType[] checkedExceptions, Member reflectMember) { + super(kind, declaringType, modifiers, returnType, name, parameterTypes, + checkedExceptions); + this.reflectMember = reflectMember; + } + + /** + * @param kind + * @param declaringType + * @param modifiers + * @param returnType + * @param name + * @param parameterTypes + * @param checkedExceptions + * @param backingGenericMember + */ + public ReflectionBasedResolvedMemberImpl(MemberKind kind, + UnresolvedType declaringType, int modifiers, + UnresolvedType returnType, String name, + UnresolvedType[] parameterTypes, + UnresolvedType[] checkedExceptions, + ResolvedMember backingGenericMember, Member reflectMember) { + super(kind, declaringType, modifiers, returnType, name, parameterTypes, + checkedExceptions, backingGenericMember); + this.reflectMember = reflectMember; + } + + /** + * @param kind + * @param declaringType + * @param modifiers + * @param name + * @param signature + */ + public ReflectionBasedResolvedMemberImpl(MemberKind kind, + UnresolvedType declaringType, int modifiers, String name, + String signature, Member reflectMember) { + super(kind, declaringType, modifiers, name, signature); + this.reflectMember = reflectMember; + } + + public Member getMember() { + return this.reflectMember; + } + + // generic signature support + + public void setGenericSignatureInformationProvider( + GenericSignatureInformationProvider gsigProvider) { + this.gsigInfoProvider = gsigProvider; + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ResolvedMemberImpl#getGenericParameterTypes() + */ + public UnresolvedType[] getGenericParameterTypes() { + return this.gsigInfoProvider.getGenericParameterTypes(this); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ResolvedMemberImpl#getGenericReturnType() + */ + public UnresolvedType getGenericReturnType() { + return this.gsigInfoProvider.getGenericReturnType(this); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ResolvedMemberImpl#isSynthetic() + */ + public boolean isSynthetic() { + return this.gsigInfoProvider.isSynthetic(this); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ResolvedMemberImpl#isVarargsMethod() + */ + public boolean isVarargsMethod() { + return this.gsigInfoProvider.isVarArgs(this); + } + + /* + * (non-Javadoc) + * + * @see org.aspectj.weaver.ResolvedMemberImpl#isBridgeMethod() + */ + public boolean isBridgeMethod() { + return this.gsigInfoProvider.isBridge(this); + } + + // annotation support + + public void setAnnotationFinder(AnnotationFinder finder) { + this.annotationFinder = finder; + } + + public boolean hasAnnotation(UnresolvedType ofType) { + unpackAnnotations(); + return super.hasAnnotation(ofType); + } + + public boolean hasAnnotations() { + unpackAnnotations(); + return super.hasAnnotations(); + } + + public ResolvedType[] getAnnotationTypes() { + unpackAnnotations(); + return super.getAnnotationTypes(); + } + + public AnnotationAJ getAnnotationOfType(UnresolvedType ofType) { + unpackAnnotations(); + if (annotationFinder == null) + return null; + for (Iterator iterator = annotationTypes.iterator(); iterator.hasNext();) { + ResolvedType type = (ResolvedType) iterator.next(); + if (type.getSignature().equals(ofType.getSignature())) { + return annotationFinder.getAnnotationOfType(ofType, + reflectMember); + } + } + return null; + } + + public String getAnnotationDefaultValue() { + if (annotationFinder == null) + return null; + return annotationFinder.getAnnotationDefaultValue(reflectMember); + } + + public ResolvedType[][] getParameterAnnotationTypes() { + if (parameterAnnotationTypes == null && annotationFinder != null) { + parameterAnnotationTypes = annotationFinder + .getParameterAnnotationTypes(reflectMember); + } + return parameterAnnotationTypes; + } + + private void unpackAnnotations() { + if (annotationTypes == null && annotationFinder != null) { + annotationTypes = annotationFinder.getAnnotations(reflectMember); + } + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/reflect/ReflectionFastMatchInfo.java b/org.aspectj.matcher/src/org/aspectj/weaver/reflect/ReflectionFastMatchInfo.java new file mode 100644 index 000000000..892fdabc5 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/reflect/ReflectionFastMatchInfo.java @@ -0,0 +1,42 @@ +/* ******************************************************************* + * 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.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.patterns.FastMatchInfo; +import org.aspectj.weaver.tools.MatchingContext; + +/** + * An implementation of FastMatchInfo that can also expose a + * MatchingContext. + * + * @author Adrian Colyer + * @since 1.5.1 + */ +public class ReflectionFastMatchInfo extends FastMatchInfo { + + private final MatchingContext context; + + public ReflectionFastMatchInfo(ResolvedType type, Shadow.Kind kind, MatchingContext context) { + super(type,kind); + this.context = context; + } + + /** + * @return Returns the matching context. + */ + public MatchingContext getMatchingContext() { + return this.context; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/reflect/ReflectionShadow.java b/org.aspectj.matcher/src/org/aspectj/weaver/reflect/ReflectionShadow.java new file mode 100644 index 000000000..9d98e3ba2 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/reflect/ReflectionShadow.java @@ -0,0 +1,345 @@ +/* ******************************************************************* + * 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.Method; +import java.util.HashMap; +import java.util.Map; + +import org.aspectj.bridge.ISourceLocation; +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; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.Var; +import org.aspectj.weaver.tools.MatchingContext; + +/** + * @author colyer + * + */ +public class ReflectionShadow extends Shadow { + + private final World world; + private final ResolvedType enclosingType; + private final ResolvedMember enclosingMember; + private final MatchingContext matchContext; + private Var thisVar = null; + private Var targetVar = null; + private Var[] argsVars = null; + private Var atThisVar = null; + private Var atTargetVar = null; + private Map atArgsVars = new HashMap(); + private Map withinAnnotationVar = new HashMap(); + private Map withinCodeAnnotationVar = new HashMap(); + private Map annotationVar = new HashMap(); + private AnnotationFinder annotationFinder; + + public static Shadow makeExecutionShadow(World inWorld, java.lang.reflect.Member forMethod, MatchingContext withContext) { + Kind kind = (forMethod instanceof Method) ? Shadow.MethodExecution : Shadow.ConstructorExecution; + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(forMethod, inWorld); + ResolvedType enclosingType = signature.getDeclaringType().resolve(inWorld); + return new ReflectionShadow(inWorld,kind,signature,null,enclosingType,null,withContext); + } + + public static Shadow makeAdviceExecutionShadow(World inWorld, java.lang.reflect.Method forMethod, MatchingContext withContext) { + Kind kind = Shadow.AdviceExecution; + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedAdviceMember(forMethod, inWorld); + ResolvedType enclosingType = signature.getDeclaringType().resolve(inWorld); + return new ReflectionShadow(inWorld,kind,signature,null,enclosingType,null,withContext); + } + + public static Shadow makeCallShadow(World inWorld, java.lang.reflect.Member aMember, java.lang.reflect.Member withinCode, MatchingContext withContext) { + Shadow enclosingShadow = makeExecutionShadow(inWorld,withinCode,withContext); + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(aMember, inWorld); + ResolvedMember enclosingMember = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(withinCode, inWorld); + ResolvedType enclosingType = enclosingMember.getDeclaringType().resolve(inWorld); + Kind kind = aMember instanceof Method ? Shadow.MethodCall : Shadow.ConstructorCall; + return new ReflectionShadow(inWorld,kind,signature,enclosingShadow,enclosingType,enclosingMember,withContext); + } + + public static Shadow makeCallShadow(World inWorld, java.lang.reflect.Member aMember, Class thisClass, MatchingContext withContext) { + Shadow enclosingShadow = makeStaticInitializationShadow(inWorld, thisClass,withContext); + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(aMember, inWorld); + ResolvedMember enclosingMember = ReflectionBasedReferenceTypeDelegateFactory.createStaticInitMember(thisClass, inWorld); + ResolvedType enclosingType = enclosingMember.getDeclaringType().resolve(inWorld); + Kind kind = aMember instanceof Method ? Shadow.MethodCall : Shadow.ConstructorCall; + return new ReflectionShadow(inWorld,kind,signature,enclosingShadow,enclosingType,enclosingMember,withContext); + } + + public static Shadow makeStaticInitializationShadow(World inWorld, Class forType, MatchingContext withContext) { + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createStaticInitMember(forType, inWorld); + ResolvedType enclosingType = signature.getDeclaringType().resolve(inWorld); + Kind kind = Shadow.StaticInitialization; + return new ReflectionShadow(inWorld,kind,signature,null,enclosingType,null,withContext); + } + + public static Shadow makePreInitializationShadow(World inWorld, Constructor forConstructor, MatchingContext withContext) { + Kind kind = Shadow.PreInitialization; + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(forConstructor, inWorld); + ResolvedType enclosingType = signature.getDeclaringType().resolve(inWorld); + return new ReflectionShadow(inWorld,kind,signature,null,enclosingType,null,withContext); + } + + public static Shadow makeInitializationShadow(World inWorld, Constructor forConstructor, MatchingContext withContext) { + Kind kind = Shadow.Initialization; + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(forConstructor, inWorld); + ResolvedType enclosingType = signature.getDeclaringType().resolve(inWorld); + return new ReflectionShadow(inWorld,kind,signature,null,enclosingType,null,withContext); + } + + public static Shadow makeHandlerShadow(World inWorld, Class exceptionType, Class withinType, MatchingContext withContext) { + Kind kind = Shadow.ExceptionHandler; + Shadow enclosingShadow = makeStaticInitializationShadow(inWorld, withinType,withContext); + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createHandlerMember(exceptionType, withinType, inWorld); + ResolvedMember enclosingMember = ReflectionBasedReferenceTypeDelegateFactory.createStaticInitMember(withinType, inWorld); + ResolvedType enclosingType = enclosingMember.getDeclaringType().resolve(inWorld); + return new ReflectionShadow(inWorld,kind,signature,enclosingShadow,enclosingType,enclosingMember,withContext); + } + + public static Shadow makeHandlerShadow(World inWorld, Class exceptionType, java.lang.reflect.Member withinCode, MatchingContext withContext) { + Kind kind = Shadow.ExceptionHandler; + Shadow enclosingShadow = makeExecutionShadow(inWorld, withinCode,withContext); + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createHandlerMember(exceptionType, withinCode.getDeclaringClass(), inWorld); + ResolvedMember enclosingMember = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(withinCode, inWorld); + ResolvedType enclosingType = enclosingMember.getDeclaringType().resolve(inWorld); + return new ReflectionShadow(inWorld,kind,signature,enclosingShadow,enclosingType,enclosingMember,withContext); + } + + public static Shadow makeFieldGetShadow(World inWorld, Field forField, Class callerType, MatchingContext withContext) { + Shadow enclosingShadow = makeStaticInitializationShadow(inWorld, callerType,withContext); + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedField(forField, inWorld); + ResolvedMember enclosingMember = ReflectionBasedReferenceTypeDelegateFactory.createStaticInitMember(callerType, inWorld); + ResolvedType enclosingType = enclosingMember.getDeclaringType().resolve(inWorld); + Kind kind = Shadow.FieldGet; + return new ReflectionShadow(inWorld,kind,signature,enclosingShadow,enclosingType,enclosingMember,withContext); + } + + public static Shadow makeFieldGetShadow(World inWorld, Field forField, java.lang.reflect.Member inMember, MatchingContext withContext) { + Shadow enclosingShadow = makeExecutionShadow(inWorld,inMember,withContext); + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedField(forField, inWorld); + ResolvedMember enclosingMember = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(inMember, inWorld); + ResolvedType enclosingType = enclosingMember.getDeclaringType().resolve(inWorld); + Kind kind = Shadow.FieldGet; + return new ReflectionShadow(inWorld,kind,signature,enclosingShadow,enclosingType,enclosingMember,withContext); + } + + public static Shadow makeFieldSetShadow(World inWorld, Field forField, Class callerType, MatchingContext withContext) { + Shadow enclosingShadow = makeStaticInitializationShadow(inWorld, callerType, withContext); + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedField(forField, inWorld); + ResolvedMember enclosingMember = ReflectionBasedReferenceTypeDelegateFactory.createStaticInitMember(callerType, inWorld); + ResolvedType enclosingType = enclosingMember.getDeclaringType().resolve(inWorld); + Kind kind = Shadow.FieldSet; + return new ReflectionShadow(inWorld,kind,signature,enclosingShadow,enclosingType,enclosingMember,withContext); + } + + public static Shadow makeFieldSetShadow(World inWorld, Field forField, java.lang.reflect.Member inMember, MatchingContext withContext) { + Shadow enclosingShadow = makeExecutionShadow(inWorld,inMember,withContext); + Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedField(forField, inWorld); + ResolvedMember enclosingMember = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(inMember, inWorld); + ResolvedType enclosingType = enclosingMember.getDeclaringType().resolve(inWorld); + Kind kind = Shadow.FieldSet; + return new ReflectionShadow(inWorld,kind,signature,enclosingShadow,enclosingType,enclosingMember,withContext); + } + + public ReflectionShadow(World world, Kind kind, Member signature, Shadow enclosingShadow, ResolvedType enclosingType, ResolvedMember enclosingMember, MatchingContext withContext) { + super(kind,signature,enclosingShadow); + this.world = world; + this.enclosingType = enclosingType; + this.enclosingMember = enclosingMember; + this.matchContext = withContext; + if (world instanceof IReflectionWorld) { + this.annotationFinder = ((IReflectionWorld)world).getAnnotationFinder(); + } + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.Shadow#getIWorld() + */ + public World getIWorld() { + return world; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.Shadow#getThisVar() + */ + public Var getThisVar() { + if (thisVar == null && hasThis()) { + thisVar = ReflectionVar.createThisVar(getThisType().resolve(world),this.annotationFinder); + } + return thisVar; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.Shadow#getTargetVar() + */ + public Var getTargetVar() { + if (targetVar == null && hasTarget()) { + targetVar = ReflectionVar.createTargetVar(getThisType().resolve(world),this.annotationFinder); + } + return targetVar; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.Shadow#getEnclosingType() + */ + public UnresolvedType getEnclosingType() { + return this.enclosingType; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.Shadow#getArgVar(int) + */ + public Var getArgVar(int i) { + if (argsVars == null) { + this.argsVars = new Var[this.getArgCount()]; + for (int j = 0; j < this.argsVars.length; j++) { + this.argsVars[j] = ReflectionVar.createArgsVar(getArgType(j).resolve(world), j,this.annotationFinder); + } + } + if (i < argsVars.length) { + return argsVars[i]; + } else { + return null; + } + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.Shadow#getThisJoinPointVar() + */ + public Var getThisJoinPointVar() { + // TODO Auto-generated method stub + return null; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.Shadow#getThisJoinPointStaticPartVar() + */ + public Var getThisJoinPointStaticPartVar() { + // TODO Auto-generated method stub + return null; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.Shadow#getThisEnclosingJoinPointStaticPartVar() + */ + public Var getThisEnclosingJoinPointStaticPartVar() { + // TODO Auto-generated method stub + return null; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.Shadow#getKindedAnnotationVar(org.aspectj.weaver.UnresolvedType) + */ + public Var getKindedAnnotationVar(UnresolvedType forAnnotationType) { + ResolvedType annType = forAnnotationType.resolve(world); + if (annotationVar.get(annType) == null) { + Var v = ReflectionVar.createAtAnnotationVar(annType,this.annotationFinder); + annotationVar.put(annType,v); + } + return (Var) annotationVar.get(annType); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.Shadow#getWithinAnnotationVar(org.aspectj.weaver.UnresolvedType) + */ + public Var getWithinAnnotationVar(UnresolvedType forAnnotationType) { + ResolvedType annType = forAnnotationType.resolve(world); + if (withinAnnotationVar.get(annType) == null) { + Var v = ReflectionVar.createWithinAnnotationVar(annType,this.annotationFinder); + withinAnnotationVar.put(annType,v); + } + return (Var) withinAnnotationVar.get(annType); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.Shadow#getWithinCodeAnnotationVar(org.aspectj.weaver.UnresolvedType) + */ + public Var getWithinCodeAnnotationVar(UnresolvedType forAnnotationType) { + ResolvedType annType = forAnnotationType.resolve(world); + if (withinCodeAnnotationVar.get(annType) == null) { + Var v = ReflectionVar.createWithinCodeAnnotationVar(annType,this.annotationFinder); + withinCodeAnnotationVar.put(annType,v); + } + return (Var) withinCodeAnnotationVar.get(annType); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.Shadow#getThisAnnotationVar(org.aspectj.weaver.UnresolvedType) + */ + public Var getThisAnnotationVar(UnresolvedType forAnnotationType) { + if (atThisVar == null) { + atThisVar = ReflectionVar.createThisAnnotationVar(forAnnotationType.resolve(world),this.annotationFinder); + } + return atThisVar; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.Shadow#getTargetAnnotationVar(org.aspectj.weaver.UnresolvedType) + */ + public Var getTargetAnnotationVar(UnresolvedType forAnnotationType) { + if (atTargetVar == null) { + atTargetVar = ReflectionVar.createTargetAnnotationVar(forAnnotationType.resolve(world),this.annotationFinder); + } + return atTargetVar; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.Shadow#getArgAnnotationVar(int, org.aspectj.weaver.UnresolvedType) + */ + public Var getArgAnnotationVar(int i, UnresolvedType forAnnotationType) { + ResolvedType annType = forAnnotationType.resolve(world); + if (atArgsVars.get(annType) == null) { + Var[] vars = new Var[getArgCount()]; + atArgsVars.put(annType,vars); + } + Var[] vars = (Var[]) atArgsVars.get(annType); + if (i > (vars.length - 1) ) return null; + if (vars[i] == null) { + vars[i] = ReflectionVar.createArgsAnnotationVar(annType, i,this.annotationFinder); + } + return vars[i]; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.Shadow#getEnclosingCodeSignature() + */ + public Member getEnclosingCodeSignature() { + // XXX this code is copied from BcelShadow with one minor change... + 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 this.enclosingMember; + } else { + return enclosingShadow.getSignature(); + } + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.Shadow#getSourceLocation() + */ + public ISourceLocation getSourceLocation() { + return null; + } + + public MatchingContext getMatchingContext() { + return this.matchContext; + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/reflect/ReflectionVar.java b/org.aspectj.matcher/src/org/aspectj/weaver/reflect/ReflectionVar.java new file mode 100644 index 000000000..59876a737 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/reflect/ReflectionVar.java @@ -0,0 +1,169 @@ +/* ******************************************************************* + * 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.Member; + +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.ast.Var; + +/** + * A variable at a reflection shadow, used by the residual tests. + */ +public final class ReflectionVar extends Var { + + static final int THIS_VAR = 0; + static final int TARGET_VAR = 1; + static final int ARGS_VAR = 2; + static final int AT_THIS_VAR = 3; + static final int AT_TARGET_VAR = 4; + static final int AT_ARGS_VAR = 5; + static final int AT_WITHIN_VAR = 6; + static final int AT_WITHINCODE_VAR = 7; + static final int AT_ANNOTATION_VAR = 8; + + private AnnotationFinder annotationFinder = null; + +// static { +// try { +// Class java15AnnotationFinder = Class.forName("org.aspectj.weaver.reflect.Java15AnnotationFinder"); +// annotationFinder = (AnnotationFinder) java15AnnotationFinder.newInstance(); +// } catch(ClassNotFoundException ex) { +// // must be on 1.4 or earlier +// } catch(IllegalAccessException ex) { +// // not so good +// throw new RuntimeException("AspectJ internal error",ex); +// } catch(InstantiationException ex) { +// throw new RuntimeException("AspectJ internal error",ex); +// } +// } + + private int argsIndex = 0; + private int varType; + + public static ReflectionVar createThisVar(ResolvedType type,AnnotationFinder finder) { + ReflectionVar ret = new ReflectionVar(type,finder); + ret.varType = THIS_VAR; + return ret; + } + + public static ReflectionVar createTargetVar(ResolvedType type, AnnotationFinder finder) { + ReflectionVar ret = new ReflectionVar(type,finder); + ret.varType = TARGET_VAR; + return ret; + } + + public static ReflectionVar createArgsVar(ResolvedType type, int index, AnnotationFinder finder) { + ReflectionVar ret = new ReflectionVar(type,finder); + ret.varType = ARGS_VAR; + ret.argsIndex = index; + return ret; + } + + public static ReflectionVar createThisAnnotationVar(ResolvedType type, AnnotationFinder finder) { + ReflectionVar ret = new ReflectionVar(type,finder); + ret.varType = AT_THIS_VAR; + return ret; + } + + public static ReflectionVar createTargetAnnotationVar(ResolvedType type, AnnotationFinder finder) { + ReflectionVar ret = new ReflectionVar(type,finder); + ret.varType = AT_TARGET_VAR; + return ret; + } + + public static ReflectionVar createArgsAnnotationVar(ResolvedType type, int index, AnnotationFinder finder) { + ReflectionVar ret = new ReflectionVar(type,finder); + ret.varType = AT_ARGS_VAR; + ret.argsIndex = index; + return ret; + } + + public static ReflectionVar createWithinAnnotationVar(ResolvedType annType, AnnotationFinder finder) { + ReflectionVar ret = new ReflectionVar(annType,finder); + ret.varType = AT_WITHIN_VAR; + return ret; + } + + public static ReflectionVar createWithinCodeAnnotationVar(ResolvedType annType, AnnotationFinder finder) { + ReflectionVar ret = new ReflectionVar(annType,finder); + ret.varType = AT_WITHINCODE_VAR; + return ret; + } + + public static ReflectionVar createAtAnnotationVar(ResolvedType annType, AnnotationFinder finder) { + ReflectionVar ret = new ReflectionVar(annType,finder); + ret.varType = AT_ANNOTATION_VAR; + return ret; + } + + private ReflectionVar(ResolvedType type,AnnotationFinder finder) { + super(type); + this.annotationFinder = finder; + } + + + public Object getBindingAtJoinPoint(Object thisObject, Object targetObject, Object[] args) { + return getBindingAtJoinPoint(thisObject,targetObject,args,null,null,null); + } + /** + * At a join point with the given this, target, and args, return the object to which this + * var is bound. + * @param thisObject + * @param targetObject + * @param args + * @return + */ + public Object getBindingAtJoinPoint( + Object thisObject, + Object targetObject, + Object[] args, + Member subject, + Member withinCode, + Class withinType) { + switch( this.varType) { + case THIS_VAR: return thisObject; + case TARGET_VAR: return targetObject; + case ARGS_VAR: + if (this.argsIndex > (args.length - 1)) return null; + return args[argsIndex]; + case AT_THIS_VAR: + if (annotationFinder != null) { + return annotationFinder.getAnnotation(getType(), thisObject); + } else return null; + case AT_TARGET_VAR: + if (annotationFinder != null) { + return annotationFinder.getAnnotation(getType(), targetObject); + } else return null; + case AT_ARGS_VAR: + if (this.argsIndex > (args.length - 1)) return null; + if (annotationFinder != null) { + return annotationFinder.getAnnotation(getType(), args[argsIndex]); + } else return null; + case AT_WITHIN_VAR: + if (annotationFinder != null) { + return annotationFinder.getAnnotationFromClass(getType(), withinType); + } else return null; + case AT_WITHINCODE_VAR: + if (annotationFinder != null) { + return annotationFinder.getAnnotationFromMember(getType(), withinCode); + } else return null; + case AT_ANNOTATION_VAR: + if (annotationFinder != null) { + return annotationFinder.getAnnotationFromMember(getType(), subject); + } else return null; + } + + return null; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/reflect/ReflectionWorld.java b/org.aspectj.matcher/src/org/aspectj/weaver/reflect/ReflectionWorld.java new file mode 100644 index 000000000..4f14303d6 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/reflect/ReflectionWorld.java @@ -0,0 +1,140 @@ +/* ******************************************************************* + * 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 org.aspectj.bridge.AbortException; +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.IMessageHandler; +import org.aspectj.util.LangUtil; +import org.aspectj.weaver.BCException; +import org.aspectj.weaver.IWeavingSupport; +import org.aspectj.weaver.ReferenceType; +import org.aspectj.weaver.ReferenceTypeDelegate; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.WeakClassLoaderReference; +import org.aspectj.weaver.World; + +/** + * A ReflectionWorld is used solely for purposes of type resolution based on the runtime classpath (java.lang.reflect). It does not + * support weaving operations (creation of mungers etc..). + * + */ +public class ReflectionWorld extends World implements IReflectionWorld { + + private WeakClassLoaderReference classLoaderReference; + private AnnotationFinder annotationFinder; + + private ReflectionWorld() { + // super(); + // this.setMessageHandler(new ExceptionBasedMessageHandler()); + // setBehaveInJava5Way(LangUtil.is15VMOrGreater()); + // this.classLoaderReference = new + // WeakClassLoaderReference(ReflectionWorld.class.getClassLoader()); + // this.annotationFinder = + // makeAnnotationFinderIfAny(classLoaderReference.getClassLoader(), + // this); + } + + public ReflectionWorld(ClassLoader aClassLoader) { + super(); + this.setMessageHandler(new ExceptionBasedMessageHandler()); + setBehaveInJava5Way(LangUtil.is15VMOrGreater()); + classLoaderReference = new WeakClassLoaderReference(aClassLoader); + annotationFinder = makeAnnotationFinderIfAny(classLoaderReference.getClassLoader(), this); + } + + public static AnnotationFinder makeAnnotationFinderIfAny(ClassLoader loader, World world) { + AnnotationFinder annotationFinder = null; + try { + if (LangUtil.is15VMOrGreater()) { + Class java15AnnotationFinder = Class.forName("org.aspectj.weaver.reflect.Java15AnnotationFinder"); + annotationFinder = (AnnotationFinder) java15AnnotationFinder.newInstance(); + annotationFinder.setClassLoader(loader); + annotationFinder.setWorld(world); + } + } catch (ClassNotFoundException ex) { + // must be on 1.4 or earlier + } catch (IllegalAccessException ex) { + // not so good + throw new BCException("AspectJ internal error", ex); + } catch (InstantiationException ex) { + throw new BCException("AspectJ internal error", ex); + } + return annotationFinder; + } + + public ClassLoader getClassLoader() { + return classLoaderReference.getClassLoader(); + } + + public AnnotationFinder getAnnotationFinder() { + return annotationFinder; + } + + public ResolvedType resolve(Class aClass) { + return resolve(this, aClass); + } + + public static ResolvedType resolve(World world, Class aClass) { + // classes that represent arrays return a class name that is the + // signature of the array type, ho-hum... + String className = aClass.getName(); + if (aClass.isArray()) { + return world.resolve(UnresolvedType.forSignature(className.replace('.', '/'))); + } else { + return world.resolve(className); + } + } + + protected ReferenceTypeDelegate resolveDelegate(ReferenceType ty) { + return ReflectionBasedReferenceTypeDelegateFactory.createDelegate(ty, this, classLoaderReference.getClassLoader()); + } + + public static class ReflectionWorldException extends RuntimeException { + + private static final long serialVersionUID = -3432261918302793005L; + + public ReflectionWorldException(String message) { + super(message); + } + } + + private static class ExceptionBasedMessageHandler implements IMessageHandler { + + public boolean handleMessage(IMessage message) throws AbortException { + throw new ReflectionWorldException(message.toString()); + } + + public boolean isIgnoring(org.aspectj.bridge.IMessage.Kind kind) { + if (kind == IMessage.INFO) { + return true; + } else { + return false; + } + } + + public void dontIgnore(org.aspectj.bridge.IMessage.Kind kind) { + // empty + } + + public void ignore(org.aspectj.bridge.IMessage.Kind kind) { + // empty + } + + } + + public IWeavingSupport getWeavingSupport() { + return null; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/reflect/ShadowMatchImpl.java b/org.aspectj.matcher/src/org/aspectj/weaver/reflect/ShadowMatchImpl.java new file mode 100644 index 000000000..88309b8aa --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/reflect/ShadowMatchImpl.java @@ -0,0 +1,189 @@ +/* ******************************************************************* + * 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.Member; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ast.And; +import org.aspectj.weaver.ast.Call; +import org.aspectj.weaver.ast.FieldGetCall; +import org.aspectj.weaver.ast.HasAnnotation; +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; +import org.aspectj.weaver.patterns.ExposedState; +import org.aspectj.weaver.tools.DefaultMatchingContext; +import org.aspectj.weaver.tools.JoinPointMatch; +import org.aspectj.weaver.tools.MatchingContext; +import org.aspectj.weaver.tools.PointcutParameter; +import org.aspectj.weaver.tools.ShadowMatch; + +/** + * @author colyer + * Implementation of ShadowMatch for reflection based worlds. + */ +public class ShadowMatchImpl implements ShadowMatch { + + private FuzzyBoolean match; + private ExposedState state; + private Test residualTest; + private PointcutParameter[] params; + private Member withinCode; + private Member subject; + private Class withinType; + private MatchingContext matchContext = new DefaultMatchingContext(); + + public ShadowMatchImpl(FuzzyBoolean match, Test test, ExposedState state, PointcutParameter[] params) { + this.match = match; + this.residualTest = test; + this.state = state; + this.params = params; + } + + public void setWithinCode(Member aMember) { this.withinCode = aMember; } + public void setSubject(Member aMember) { this.subject = aMember; } + public void setWithinType(Class aClass) { this.withinType = aClass; } + + public boolean alwaysMatches() { + return match.alwaysTrue(); + } + + public boolean maybeMatches() { + return match.maybeTrue(); + } + + public boolean neverMatches() { + return match.alwaysFalse(); + } + + public JoinPointMatch matchesJoinPoint(Object thisObject, Object targetObject, Object[] args) { + if (neverMatches()) return JoinPointMatchImpl.NO_MATCH; + if (new RuntimeTestEvaluator(residualTest,thisObject,targetObject,args,this.matchContext).matches()) { + return new JoinPointMatchImpl(getPointcutParameters(thisObject,targetObject,args)); + } else { + return JoinPointMatchImpl.NO_MATCH; + } + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.tools.ShadowMatch#setMatchingContext(org.aspectj.weaver.tools.MatchingContext) + */ + public void setMatchingContext(MatchingContext aMatchContext) { + this.matchContext = aMatchContext; + } + + private PointcutParameter[] getPointcutParameters(Object thisObject, Object targetObject, Object[] args) { + Var[] vars = state.vars; + PointcutParameterImpl[] bindings = new PointcutParameterImpl[params.length]; + for (int i = 0; i < bindings.length; i++) { + bindings[i] = new PointcutParameterImpl(params[i].getName(),params[i].getType()); + bindings[i].setBinding(((ReflectionVar)vars[i]).getBindingAtJoinPoint(thisObject, targetObject, args,subject,withinCode,withinType)); + } + return bindings; + } + + private static class RuntimeTestEvaluator implements ITestVisitor { + + private boolean matches = true; + private final Test test; + private final Object thisObject; + private final Object targetObject; + private final Object[] args; + private final MatchingContext matchContext; + + + public RuntimeTestEvaluator(Test aTest,Object thisObject, Object targetObject, Object[] args, MatchingContext context) { + this.test = aTest; + this.thisObject = thisObject; + this.targetObject = targetObject; + this.args = args; + this.matchContext = context; + } + + public boolean matches() { + test.accept(this); + return matches; + } + + public void visit(And e) { + boolean leftMatches = + new RuntimeTestEvaluator(e.getLeft(),thisObject,targetObject,args,matchContext).matches(); + if (!leftMatches) { + matches = false; + } else { + matches = new RuntimeTestEvaluator(e.getRight(),thisObject,targetObject,args,matchContext).matches(); + } + } + + public void visit(Instanceof i) { + ReflectionVar v = (ReflectionVar) i.getVar(); + Object value = v.getBindingAtJoinPoint(thisObject,targetObject, args); + World world = v.getType().getWorld(); + ResolvedType desiredType = i.getType().resolve(world); + ResolvedType actualType = world.resolve(value.getClass().getName()); + matches = desiredType.isAssignableFrom(actualType); + } + + public void visit(MatchingContextBasedTest matchingContextTest) { + matches = matchingContextTest.matches(this.matchContext); + } + + public void visit(Not not) { + matches = ! new RuntimeTestEvaluator(not.getBody(),thisObject,targetObject,args,matchContext).matches(); + } + + public void visit(Or or) { + boolean leftMatches = + new RuntimeTestEvaluator(or.getLeft(),thisObject,targetObject,args,matchContext).matches(); + if (leftMatches) { + matches = true; + } else { + matches = new RuntimeTestEvaluator(or.getRight(),thisObject,targetObject,args,matchContext).matches(); + } + } + + public void visit(Literal literal) { + if (literal == Literal.FALSE) { + matches = false; + } else { + matches = true; + } + } + + public void visit(Call call) { + throw new UnsupportedOperationException("Can't evaluate call test at runtime"); + } + + public void visit(FieldGetCall fieldGetCall) { + throw new UnsupportedOperationException("Can't evaluate fieldGetCall test at runtime"); + } + + public void visit(HasAnnotation hasAnnotation) { + ReflectionVar v = (ReflectionVar) hasAnnotation.getVar(); + Object value = v.getBindingAtJoinPoint(thisObject,targetObject, args); + World world = v.getType().getWorld(); + ResolvedType actualVarType = world.resolve(value.getClass().getName()); + ResolvedType requiredAnnotationType = hasAnnotation.getAnnotationType().resolve(world); + matches = actualVarType.hasAnnotation(requiredAnnotationType); + } + + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/tools/AbstractTrace.java b/org.aspectj.matcher/src/org/aspectj/weaver/tools/AbstractTrace.java new file mode 100644 index 000000000..2ec0aa3a9 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/tools/AbstractTrace.java @@ -0,0 +1,192 @@ +/******************************************************************************* + * 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.io.File; +import java.lang.reflect.Array; +import java.net.URL; +import java.text.SimpleDateFormat; +import java.util.Collection; +import java.util.Date; + +import org.aspectj.bridge.IMessage.Kind; + +public abstract class AbstractTrace implements Trace { + + protected Class tracedClass; + + private static SimpleDateFormat timeFormat; + + protected AbstractTrace (Class clazz) { + this.tracedClass = clazz; + } + + public abstract void enter (String methodName, Object thiz, Object[] args); + + public abstract void enter(String methodName, Object thiz); + + public abstract void exit(String methodName, Object ret); + + public abstract void exit(String methodName, Throwable th); + + /* + * Convenience methods + */ + public void enter (String methodName) { + enter(methodName,null,null); + } + + public void enter (String methodName, Object thiz, Object arg) { + enter(methodName,thiz,new Object[] { arg }); + } + + public void enter (String methodName, Object thiz, boolean z) { + enter(methodName,thiz,new Boolean(z)); + } + + public void exit (String methodName, boolean b) { + exit(methodName,new Boolean(b)); + } + + public void exit (String methodName, int i) { + exit(methodName,new Integer(i)); + } + + public void event (String methodName, Object thiz, Object arg) { + event(methodName,thiz,new Object[] { arg }); + } + + public void warn(String message) { + warn(message,null); + } + + public void error(String message) { + error(message,null); + } + + public void fatal (String message) { + fatal(message,null); + } + + /* + * Formatting + */ + protected String formatMessage(String kind, String className, String methodName, Object thiz, Object[] args) { + StringBuffer message = new StringBuffer(); + Date now = new Date(); + message.append(formatDate(now)).append(" "); + message.append(Thread.currentThread().getName()).append(" "); + message.append(kind).append(" "); + message.append(className); + message.append(".").append(methodName); + if (thiz != null) message.append(" ").append(formatObj(thiz)); + if (args != null) message.append(" ").append(formatArgs(args)); + return message.toString(); + } + + protected String formatMessage(String kind, String text, Throwable th) { + StringBuffer message = new StringBuffer(); + Date now = new Date(); + message.append(formatDate(now)).append(" "); + message.append(Thread.currentThread().getName()).append(" "); + message.append(kind).append(" "); + message.append(text); + if (th != null) message.append(" ").append(formatObj(th)); + return message.toString(); + } + + private static String formatDate (Date date) { + if (timeFormat == null) { + timeFormat = new SimpleDateFormat("HH:mm:ss.SSS"); + } + + return timeFormat.format(date); + } + + /** + * Format objects safely avoiding toString which can cause recursion, + * NullPointerExceptions or highly verbose results. + * + * @param obj parameter to be formatted + * @return the formated parameter + */ + protected Object formatObj(Object obj) { + + /* These classes have a safe implementation of toString() */ + if (obj == null + || obj instanceof String + || obj instanceof Number + || obj instanceof Boolean + || obj instanceof Exception + || obj instanceof Character + || obj instanceof Class + || obj instanceof File + || obj instanceof StringBuffer + || obj instanceof URL + || obj instanceof Kind + ) return obj; + else if (obj.getClass().isArray()) { + return formatArray(obj); + } + else if (obj instanceof Collection) { + return formatCollection((Collection)obj); + } + else try { + + /* Classes can provide an alternative implementation of toString() */ + if (obj instanceof Traceable) { + Traceable t = (Traceable)obj; + return t.toTraceString(); + } + + /* Use classname@hashcode */ + else return obj.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(obj)); + + /* Object.hashCode() can be override and may thow an exception */ + } catch (Exception ex) { + return obj.getClass().getName() + "@FFFFFFFF"; + } + } + + protected String formatArray (Object obj) { + return obj.getClass().getComponentType().getName() + "[" + Array.getLength(obj) + "]"; + } + + protected String formatCollection (Collection c) { + return c.getClass().getName() + "(" + c.size() + ")"; + } + + /** + * Format arguments into a comma separated list + * + * @param names array of argument names + * @param args array of arguments + * @return the formated list + */ + protected String formatArgs(Object[] args) { + StringBuffer sb = new StringBuffer(); + + for (int i = 0; i < args.length; i++) { + sb.append(formatObj(args[i])); + if (i < args.length-1) sb.append(", "); + } + + return sb.toString(); + } + + protected Object[] formatObjects(Object[] args) { + for (int i = 0; i < args.length; i++) { + args[i] = formatObj(args[i]); + } + + return args; + } +} \ No newline at end of file diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/tools/CommonsTrace.java b/org.aspectj.matcher/src/org/aspectj/weaver/tools/CommonsTrace.java new file mode 100644 index 000000000..25562c050 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/tools/CommonsTrace.java @@ -0,0 +1,106 @@ +/******************************************************************************* + * 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 org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class CommonsTrace extends AbstractTrace { + + private Log log; + private String className; + + public CommonsTrace (Class clazz) { + super(clazz); + this.log = LogFactory.getLog(clazz); + this.className = tracedClass.getName(); + } + + public void enter(String methodName, Object thiz, Object[] args) { + if (log.isDebugEnabled()) { + log.debug(formatMessage(">", className, methodName, thiz, args)); + } + } + + public void enter(String methodName, Object thiz) { + if (log.isDebugEnabled()) { + log.debug(formatMessage(">", className, methodName, thiz, null)); + } + } + + public void exit(String methodName, Object ret) { + if (log.isDebugEnabled()) { + log.debug(formatMessage("<", className, methodName, ret, null)); + } + } + + public void exit(String methodName, Throwable th) { + if (log.isDebugEnabled()) { + log.debug(formatMessage("<", className, methodName, th, null)); + } + } + + public void exit(String methodName) { + if (log.isDebugEnabled()) { + log.debug(formatMessage("<", className, methodName, null, null)); + } + } + + public void event(String methodName, Object thiz, Object[] args) { + if (log.isDebugEnabled()) { + log.debug(formatMessage("-", className, methodName, thiz, args)); + } + } + + public void event(String methodName) { + if (log.isDebugEnabled()) { + log.debug(formatMessage("-", className, methodName, null, null)); + } + } + + public boolean isTraceEnabled () { + return log.isDebugEnabled(); + } + + public void setTraceEnabled (boolean b) { + } + + public void debug (String message) { + if (log.isDebugEnabled()) { + log.debug(message); + } + } + + public void info(String message) { + if (log.isInfoEnabled()) { + log.info(message); + } + } + + public void warn (String message, Throwable th) { + if (log.isWarnEnabled()) { + log.warn(message,th); + } + } + + public void error (String message, Throwable th) { + if (log.isErrorEnabled()) { + log.error(message,th); + } + } + + public void fatal (String message, Throwable th) { + if (log.isFatalEnabled()) { + log.fatal(message,th); + } + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/tools/CommonsTraceFactory.java b/org.aspectj.matcher/src/org/aspectj/weaver/tools/CommonsTraceFactory.java new file mode 100644 index 000000000..8f9f91e98 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/tools/CommonsTraceFactory.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * 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 org.apache.commons.logging.LogFactory; +//OPTIMIZE move out of main weaver for now? +public class CommonsTraceFactory extends TraceFactory { + + private LogFactory logFactory = LogFactory.getFactory(); + + public Trace getTrace(Class clazz) { + return new CommonsTrace(clazz); + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/tools/ContextBasedMatcher.java b/org.aspectj.matcher/src/org/aspectj/weaver/tools/ContextBasedMatcher.java new file mode 100644 index 000000000..fc247dc90 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/tools/ContextBasedMatcher.java @@ -0,0 +1,65 @@ +/* ******************************************************************* + * 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; + +/** + * Pointcut expression interface for pointcut + * expressions returned by a + * PointcutDesignatorHandler. Provides an additional + * matching method for matching based on context + * information over and above that normally used + * by AspectJ. + * + * @see MatchingContext + * + */ +public interface ContextBasedMatcher { + + /** + * return true iff this matcher could ever match + * a join point in the given type + * @deprecated use couldMatchJoinPointsInType(Class,MatchingContext) instead + */ + boolean couldMatchJoinPointsInType(Class aClass); + + /** + * return true iff this matcher could ever match + * a join point in the given type, may also use any + * match context information available + * @since 1.5.1 + */ + boolean couldMatchJoinPointsInType(Class aClass, MatchingContext matchContext); + + /** + * return true if matchesStatically can ever return + * FuzzyBoolean.MAYBE (necessitating a per-join point test + * to determine matching at a given join point). + */ + boolean mayNeedDynamicTest(); + + /** + * Return FuzzyBoolean.YES if a join point with the given + * matching context is always matched. + * Return FuzzyBoolean.NO if a join point with the given + * matching context is never matched. + * Return FuzzyBoolean.MAYBE if a match cannot be determined + * statically (whilst generating a ShadowMatch), and must + * be determined on a per-join point basis. + */ + FuzzyBoolean matchesStatically(MatchingContext matchContext); + + /** + * Called during processing of ShadowMatch.matchesJoinPoint + * when matchesStatically returned FuzzyBoolean.MAYBE. + */ + boolean matchesDynamically(MatchingContext matchContext); +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/tools/DefaultMatchingContext.java b/org.aspectj.matcher/src/org/aspectj/weaver/tools/DefaultMatchingContext.java new file mode 100644 index 000000000..fa47a87a3 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/tools/DefaultMatchingContext.java @@ -0,0 +1,56 @@ +/* ******************************************************************* + * 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.util.HashMap; +import java.util.Map; + +/** + * Default implementation of MatchingContext, backed + * by a Map. + */ +public class DefaultMatchingContext implements MatchingContext { + + private Map contextMap = new HashMap(); + + /* (non-Javadoc) + * @see org.aspectj.weaver.tools.MatchingContext#hasContextParameter(java.lang.String) + */ + public boolean hasContextBinding(String contextParameterName) { + return this.contextMap.containsKey(contextParameterName); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.tools.MatchingContext#get(java.lang.String) + */ + public Object getBinding(String contextParameterName) { + return this.contextMap.get(contextParameterName); + } + + /** + * Add a context binding with the given name and value + * @param name + * @param value + */ + public void addContextBinding(String name, Object value) { + this.contextMap.put(name, value); + } + + /** + * Remove the context binding with the given name + * @param name + */ + public void removeContextBinding(String name) { + this.contextMap.remove(name); + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/tools/DefaultTrace.java b/org.aspectj.matcher/src/org/aspectj/weaver/tools/DefaultTrace.java new file mode 100644 index 000000000..ad95bbb3e --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/tools/DefaultTrace.java @@ -0,0 +1,120 @@ +/******************************************************************************* + * 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.io.PrintStream; + + +public class DefaultTrace extends AbstractTrace { + + private boolean traceEnabled = false; + private PrintStream print = System.err; + + public DefaultTrace (Class clazz) { + super(clazz); + } + + public boolean isTraceEnabled () { + return traceEnabled; + } + + public void setTraceEnabled (boolean b) { + traceEnabled = b; + } + + public void enter (String methodName, Object thiz, Object[] args) { + if (traceEnabled) { + println(formatMessage(">",tracedClass.getName(),methodName,thiz, args)); + } + } + + public void enter (String methodName, Object thiz) { + if (traceEnabled) { + println(formatMessage(">",tracedClass.getName(),methodName,thiz, null)); + } + } + + public void exit (String methodName, Object ret) { + if (traceEnabled) { + println(formatMessage("<",tracedClass.getName(),methodName,ret, null)); + } + } + + public void exit (String methodName) { + if (traceEnabled) { + println(formatMessage("<",tracedClass.getName(),methodName,null, null)); + } + } + + public void exit(String methodName, Throwable th) { + if (traceEnabled) { + println(formatMessage("<",tracedClass.getName(),methodName,th, null)); + } + } + + public void event(String methodName, Object thiz, Object[] args) { + if (traceEnabled) { + println(formatMessage("-",tracedClass.getName(),methodName,thiz, args)); + } + } + + public void event(String methodName) { + if (traceEnabled) { + println(formatMessage("-",tracedClass.getName(),methodName,null,null)); + } + } + + public void debug (String message) { + println(formatMessage("?",message,null)); + } + + public void info(String message) { + println(formatMessage("I",message,null)); + } + + public void warn (String message, Throwable th) { + println(formatMessage("W",message,th)); + if (th != null) th.printStackTrace(); + } + + + public void error (String message, Throwable th) { + println(formatMessage("E",message,th)); + if (th != null) th.printStackTrace(); + } + + public void fatal (String message, Throwable th) { + println(formatMessage("X",message,th)); + if (th != null) th.printStackTrace(); + } + + /** + * Template method that allows choice of destination for output + * + * @param s message to be traced + */ + protected void println (String s) { + print.println(s); + } + + public void setPrintStream (PrintStream printStream) { + this.print = printStream; + } + +// private static boolean isTracingEnabled = getBoolean("org.aspectj.weaver.tools.tracing",false); +// +// private static boolean getBoolean (String name, boolean def) { +// String defaultValue = String.valueOf(def); +// String value = System.getProperty(name,defaultValue); +// return Boolean.valueOf(value).booleanValue(); +// } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/tools/DefaultTraceFactory.java b/org.aspectj.matcher/src/org/aspectj/weaver/tools/DefaultTraceFactory.java new file mode 100644 index 000000000..c7860589a --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/tools/DefaultTraceFactory.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * 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.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; + +public class DefaultTraceFactory extends TraceFactory { + + public final static String ENABLED_PROPERTY = "org.aspectj.tracing.enabled"; + public final static String FILE_PROPERTY = "org.aspectj.tracing.file"; + + private boolean tracingEnabled = getBoolean(ENABLED_PROPERTY,false); + private PrintStream print; + + public DefaultTraceFactory() { + String filename = System.getProperty(FILE_PROPERTY); + if (filename != null) { + File file = new File(filename); + try { + print = new PrintStream(new FileOutputStream(file)); + } + catch (IOException ex) { + if (debug) ex.printStackTrace(); + } + } + } + + public boolean isEnabled() { + return tracingEnabled; + } + + public Trace getTrace (Class clazz) { + DefaultTrace trace = new DefaultTrace(clazz); + trace.setTraceEnabled(tracingEnabled); + if (print != null) trace.setPrintStream(print); + return trace; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/tools/FuzzyBoolean.java b/org.aspectj.matcher/src/org/aspectj/weaver/tools/FuzzyBoolean.java new file mode 100644 index 000000000..77079c094 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/tools/FuzzyBoolean.java @@ -0,0 +1,37 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * 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; + +/** + * This class implements a boolean that includes a "maybe" + */ +public final class FuzzyBoolean { + + // Note :- this implementation is not safe under serialization / deserialization + private String name; + + public static final FuzzyBoolean YES = new FuzzyBoolean("YES"); + public static final FuzzyBoolean NO = new FuzzyBoolean("NO"); + public static final FuzzyBoolean MAYBE = new FuzzyBoolean("MAYBE"); + + + public static final FuzzyBoolean fromBoolean(boolean b) { + return b ? YES : NO; + } + + public String toString() { return name; } + + private FuzzyBoolean() {} + + private FuzzyBoolean(String n) { this.name = n; } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/tools/GeneratedClassHandler.java b/org.aspectj.matcher/src/org/aspectj/weaver/tools/GeneratedClassHandler.java new file mode 100644 index 000000000..43be82323 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/tools/GeneratedClassHandler.java @@ -0,0 +1,29 @@ +/* ******************************************************************* + * 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 + * ******************************************************************/ +package org.aspectj.weaver.tools; + +/** + * Interface implemented by weaving class loaders to allow classes generated by + * the weaving process to be defined. + */ +public interface GeneratedClassHandler { + + /** + * Accept class generated by WeavingAdaptor. The class loader should store + * the class definition in its local cache until called upon to load it. + * @param name class name + * @param bytes class definition + */ + public void acceptClass (String name, byte[] bytes); + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/tools/ISupportsMessageContext.java b/org.aspectj.matcher/src/org/aspectj/weaver/tools/ISupportsMessageContext.java new file mode 100644 index 000000000..69e55b143 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/tools/ISupportsMessageContext.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * 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 org.aspectj.bridge.IMessageContext; + +public interface ISupportsMessageContext { + + public void setMessageContext (IMessageContext messageContext); +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/tools/JoinPointMatch.java b/org.aspectj.matcher/src/org/aspectj/weaver/tools/JoinPointMatch.java new file mode 100644 index 000000000..588b3c0bc --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/tools/JoinPointMatch.java @@ -0,0 +1,31 @@ +/* ******************************************************************* + * 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; + +/** + * @author colyer + * The result of asking a ShadowMatch to match at a given join point. + */ +public interface JoinPointMatch { + + /** + * True if the pointcut expression has matched at this join point, and false + * otherwise + */ + boolean matches(); + + /** + * Get the parameter bindings at the matched join point. + * If the join point was not matched an empty array is returned. + */ + PointcutParameter[] getParameterBindings(); +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/tools/MatchingContext.java b/org.aspectj.matcher/src/org/aspectj/weaver/tools/MatchingContext.java new file mode 100644 index 000000000..e668a30b7 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/tools/MatchingContext.java @@ -0,0 +1,43 @@ +/* ******************************************************************* + * 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; + +/** + * When extending AspectJ's pointcut parsing and + * matching with custom PointcutDesignatorHandlers, + * it may be necessary to match based on context information + * at a join point not exposed simply by java.lang.reflect + * member information or argument values. The matching context + * interface provides an extension point for the specification + * of additional shadow and join point context that can be + * taken into account during the matching process. + * + * @see DefaultMatchingContext + */ +public interface MatchingContext { + + /** + * Returns true iff this matching context has a defined + * binding for the given context parameter. + * @param contextParameterName + */ + boolean hasContextBinding(String contextParameterName); + + /** + * returns the binding associated with the + * given context parameter name (or null if + * there is no such context). + * @param contextParameterName + * @return + */ + Object getBinding(String contextParameterName); +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/tools/PointcutDesignatorHandler.java b/org.aspectj.matcher/src/org/aspectj/weaver/tools/PointcutDesignatorHandler.java new file mode 100644 index 000000000..63f4a81e2 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/tools/PointcutDesignatorHandler.java @@ -0,0 +1,49 @@ +/* ******************************************************************* + * 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; + + +/** + * The PointcutDesignator interface allows extension of the + * AspectJ pointcut language so that third-party tools integrating + * with AspectJ can add easily their own custom + * domain-specific designators and have them interoperate seamlessly + * with the standard AspectJ designators. + * + * A pointcut designator can only be used for matching, not for + * binding. + */ +public interface PointcutDesignatorHandler { + + /** + * The name of this pointcut designator. For example, + * if this designator handles a "bean(<NamePattern>) + * format designator, this method would return "bean". + * @return + */ + String getDesignatorName() ; + + /** + * Parse the given expression string + * and return a ContextBasedMatcher that can be used + * for matching. + * @param expression the body of the pointcut expression. + * For example, given the expression "bean(*DAO)" the parse + * method will be called with the argument "*DAO". + * @return a pointcut expression that can be used for + * matching. + * @throws IllegalArgumentException if the expression + * is ill-formed. + */ + ContextBasedMatcher parse(String expression); + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/tools/PointcutExpression.java b/org.aspectj.matcher/src/org/aspectj/weaver/tools/PointcutExpression.java new file mode 100644 index 000000000..6b598a50c --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/tools/PointcutExpression.java @@ -0,0 +1,204 @@ +/* ******************************************************************* + * 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.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; + +/** + * Represents an AspectJ pointcut expression and provides convenience methods to determine + * whether or not the pointcut matches join points specified in terms of the + * java.lang.reflect interfaces. + */ +public interface PointcutExpression { + + /** + * Set the matching context to be used for + * subsequent calls to match. + * @see MatchingContext + */ + void setMatchingContext(MatchingContext aMatchContext); + + /** + * Determine whether or not this pointcut could ever match a join point in the given class. + * @param aClass the candidate class + * @return true iff this pointcut may match a join point within(aClass), and false otherwise + */ + boolean couldMatchJoinPointsInType(Class aClass); + + /** + * Returns true iff this pointcut contains any expression that might necessitate a dynamic test + * at some join point (e.g. args) + */ + boolean mayNeedDynamicTest(); + + /** + * Determine whether or not this pointcut matches the execution of a given method. + * @param aMethod the method being executed + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never + * matches join points representing the execution of the method. + */ + ShadowMatch matchesMethodExecution(Method aMethod ); + + /** + * Determine whether or not this pointcut matches the execution of a given constructor. + * @param aConstructor the constructor being executed + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never + * matches join points representing the execution of the constructor. + */ + ShadowMatch matchesConstructorExecution(Constructor aConstructor); + + /** + * Determine whether or not this pointcut matches the static initialization + * of the given class. + * @param aClass the class being statically initialized + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never + * matchs join points representing the static initialization of the given type + */ + ShadowMatch matchesStaticInitialization(Class aClass); + + /** + * Determine whether or not this pointcut matches the execution of a given piece of advice. + * @param anAdviceMethod a method representing the advice being executed + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never + * matches join points representing the execution of the advice. + */ + ShadowMatch matchesAdviceExecution(Method anAdviceMethod); + + /** + * Determine whether or not this pointcut matches the initialization of an + * object initiated by a call to the given constructor. + * @param aConstructor the constructor initiating the initialization + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never + * matches join points representing initialization via the given constructor. + */ + ShadowMatch matchesInitialization(Constructor aConstructor); + + /** + * Determine whether or not this pointcut matches the pre-initialization of an + * object initiated by a call to the given constructor. + * @param aConstructor the constructor initiating the initialization + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never + * matches join points representing pre-initialization via the given constructor. + */ + ShadowMatch matchesPreInitialization(Constructor aConstructor); + + /** + * Determine whether or not this pointcut matches a method call to the given method, made during + * the execution of the given method or constructor. + * @param aMethod the method being called + * @param withinCode the Method or Constructor from within which the call is made + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never + * matches join points representing a call to this method during the execution of the given member. + */ + ShadowMatch matchesMethodCall(Method aMethod, Member withinCode); + + /** + * Determine whether or not this pointcut matches a method call to the given method, made outside + * of the scope of any method or constructor, but within the callerType (for example, during + * static initialization of the type). + * @param aMethod the method being called + * @param callerType the declared type of the caller + * @param receiverType the declared type of the recipient of the call + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never + * matches join points representing a call to this method during the execution of the given member. + */ + ShadowMatch matchesMethodCall(Method aMethod, Class callerType); + + /** + * Determine whether or not this pointcut matches a method call to the given constructor, made during + * the execution of the given method or constructor. + * @param aConstructor the constructor being called + * @param withinCode the Method or Constructor from within which the call is made + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never + * matches join points representing a call to this constructor during the execution of the given member. + */ + ShadowMatch matchesConstructorCall(Constructor aConstructor, Member withinCode); + + /** + * Determine whether or not this pointcut matches a method call to the given constructor, made outside + * of the scope of any method or constructor, but within the callerType. + * @param aConstructor the cosstructor being called + * @param callerType the declared type of the caller + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or never + * matches join points representing a call to this constructor during the execution of the given member. + */ + ShadowMatch matchesConstructorCall(Constructor aConstructor, Class callerType); + + /** + * Determine whether or not this pointcut matches the execution of a given exception + * handler within the given method or constructor + * @param exceptionType the static type of the exception being handled + * @param withinCode the method or constructor in which the catch block is declared + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or + * never matches join points representing the handling of the given exception + */ + ShadowMatch matchesHandler(Class exceptionType, Member withinCode); + + /** + * Determine whether or not this pointcut matches the execution of a given exception + * handler outside of the scope of any method or constructor, but within the handling type. + * @param exceptionType the static type of the exception being handled + * @param handlingType the type in which the handler block is executing + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or + * never matches join points representing the handling of the given exception + */ + ShadowMatch matchesHandler(Class exceptionType, Class handlingType); + + /** + * Determine whether or not this pointcut matches a set of the given field from within the given + * method or constructor. + * @param aField the field being updated + * @param withinCode the Method or Constructor owning the call site + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or + * never matches field set join points for the given field and call site. + */ + ShadowMatch matchesFieldSet(Field aField, Member withinCode); + + /** + * Determine whether or not this pointcut matches a set of the given field outside of the + * scope of any method or constructor, but within the given type (for example, during + * static initialization). + * @param aField the field being updated + * @param withinType the type owning the call site + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or + * never matches field set join points for the given field and call site. + */ + ShadowMatch matchesFieldSet(Field aField, Class withinType); + + /** + * Determine whether or not this pointcut matches a get of the given field from within the given + * method or constructor. + * @param aField the field being updated + * @param withinCode the Method or Constructor owning the call site + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or + * never matches field get join points for the given field and call site. + */ + ShadowMatch matchesFieldGet(Field aField, Member withinCode); + + /** + * Determine whether or not this pointcut matches a get of the given field outside of the + * scope of any method or constructor, but within the given type (for example, during + * static initialization). + * @param aField the field being accessed + * @param withinType the type owning the call site + * @return a ShadowMatch indicating whether the pointcut always, sometimes, or + * never matches field get join points for the given field and call site. + */ + ShadowMatch matchesFieldGet(Field aField, Class withinType); + + /** + * Return a string representation of this pointcut expression. + */ + String getPointcutExpression(); +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/tools/PointcutParameter.java b/org.aspectj.matcher/src/org/aspectj/weaver/tools/PointcutParameter.java new file mode 100644 index 000000000..a0eaa108a --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/tools/PointcutParameter.java @@ -0,0 +1,36 @@ +/* ******************************************************************* + * 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; + +/** + * @author colyer + * Represents a parameter in a pointcut expression. + * For example pointcut pc(String s) : .....; has a PointcutParameter of + * name "s" and type String. + */ +public interface PointcutParameter { + + /** + * The name of this parameter + */ + String getName(); + + /** + * The type of the parameter + */ + Class getType(); + + /** + * At a matched join point, the parameter binding. + */ + Object getBinding(); +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/tools/PointcutParser.java b/org.aspectj.matcher/src/org/aspectj/weaver/tools/PointcutParser.java new file mode 100644 index 000000000..13841b71d --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/tools/PointcutParser.java @@ -0,0 +1,546 @@ +/******************************************************************************* + * 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.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Properties; +import java.util.Set; + +import org.aspectj.bridge.IMessageHandler; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.SourceLocation; +import org.aspectj.weaver.BindingScope; +import org.aspectj.weaver.IHasPosition; +import org.aspectj.weaver.ISourceContext; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.WeakClassLoaderReference; +import org.aspectj.weaver.World; +import org.aspectj.weaver.internal.tools.PointcutExpressionImpl; +import org.aspectj.weaver.internal.tools.TypePatternMatcherImpl; +import org.aspectj.weaver.patterns.AndPointcut; +import org.aspectj.weaver.patterns.CflowPointcut; +import org.aspectj.weaver.patterns.FormalBinding; +import org.aspectj.weaver.patterns.IScope; +import org.aspectj.weaver.patterns.KindedPointcut; +import org.aspectj.weaver.patterns.NotPointcut; +import org.aspectj.weaver.patterns.OrPointcut; +import org.aspectj.weaver.patterns.ParserException; +import org.aspectj.weaver.patterns.PatternParser; +import org.aspectj.weaver.patterns.Pointcut; +import org.aspectj.weaver.patterns.SimpleScope; +import org.aspectj.weaver.patterns.ThisOrTargetAnnotationPointcut; +import org.aspectj.weaver.patterns.ThisOrTargetPointcut; +import org.aspectj.weaver.patterns.TypePattern; +import org.aspectj.weaver.reflect.PointcutParameterImpl; +import org.aspectj.weaver.reflect.ReflectionWorld; + +/** + * A PointcutParser can be used to build PointcutExpressions for a user-defined subset of AspectJ's pointcut language + */ +public class PointcutParser { + + private ReflectionWorld world; + private WeakClassLoaderReference classLoaderReference; + private final Set supportedPrimitives; + private final Set pointcutDesignators = new HashSet(); + + /** + * @return a Set containing every PointcutPrimitive except if, cflow, and cflowbelow (useful for passing to PointcutParser + * constructor). + */ + public static Set getAllSupportedPointcutPrimitives() { + Set primitives = new HashSet(); + primitives.add(PointcutPrimitive.ADVICE_EXECUTION); + primitives.add(PointcutPrimitive.ARGS); + primitives.add(PointcutPrimitive.CALL); + primitives.add(PointcutPrimitive.EXECUTION); + primitives.add(PointcutPrimitive.GET); + primitives.add(PointcutPrimitive.HANDLER); + primitives.add(PointcutPrimitive.INITIALIZATION); + primitives.add(PointcutPrimitive.PRE_INITIALIZATION); + primitives.add(PointcutPrimitive.SET); + primitives.add(PointcutPrimitive.STATIC_INITIALIZATION); + primitives.add(PointcutPrimitive.TARGET); + primitives.add(PointcutPrimitive.THIS); + primitives.add(PointcutPrimitive.WITHIN); + primitives.add(PointcutPrimitive.WITHIN_CODE); + primitives.add(PointcutPrimitive.AT_ANNOTATION); + primitives.add(PointcutPrimitive.AT_THIS); + primitives.add(PointcutPrimitive.AT_TARGET); + primitives.add(PointcutPrimitive.AT_ARGS); + primitives.add(PointcutPrimitive.AT_WITHIN); + primitives.add(PointcutPrimitive.AT_WITHINCODE); + primitives.add(PointcutPrimitive.REFERENCE); + + return primitives; + } + + /** + * Returns a pointcut parser that can parse the full AspectJ pointcut language with the following exceptions: + *
    + *
  • The if, cflow, and cflowbelow pointcut designators are not supported + *
  • Pointcut expressions must be self-contained :- they cannot contain references to other named pointcuts + *
  • The pointcut expression must be anonymous with no formals allowed. + *
+ *

+ * When resolving types in pointcut expressions, the context classloader is used to find types. + *

+ */ + public static PointcutParser getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution() { + PointcutParser p = new PointcutParser(); + p.setClassLoader(Thread.currentThread().getContextClassLoader()); + return p; + } + + /** + * Returns a pointcut parser that can parse pointcut expressions built from a user-defined subset of AspectJ's supported + * pointcut primitives. The following restrictions apply: + *
    + *
  • The if, cflow, and cflowbelow pointcut designators are not supported + *
  • Pointcut expressions must be self-contained :- they cannot contain references to other named pointcuts + *
  • The pointcut expression must be anonymous with no formals allowed. + *
+ *

+ * When resolving types in pointcut expressions, the context classloader is used to find types. + *

+ * + * @param supportedPointcutKinds a set of PointcutPrimitives this parser should support + * @throws UnsupportedOperationException if the set contains if, cflow, or cflow below + */ + public static PointcutParser getPointcutParserSupportingSpecifiedPrimitivesAndUsingContextClassloaderForResolution( + Set supportedPointcutKinds) { + PointcutParser p = new PointcutParser(supportedPointcutKinds); + p.setClassLoader(Thread.currentThread().getContextClassLoader()); + return p; + } + + /** + * Returns a pointcut parser that can parse the full AspectJ pointcut language with the following exceptions: + *
    + *
  • The if, cflow, and cflowbelow pointcut designators are not supported + *
  • Pointcut expressions must be self-contained :- they cannot contain references to other named pointcuts + *
  • The pointcut expression must be anonymous with no formals allowed. + *
+ *

+ * When resolving types in pointcut expressions, the given classloader is used to find types. + *

+ */ + public static PointcutParser getPointcutParserSupportingAllPrimitivesAndUsingSpecifiedClassloaderForResolution( + ClassLoader classLoader) { + PointcutParser p = new PointcutParser(); + p.setClassLoader(classLoader); + return p; + } + + /** + * Returns a pointcut parser that can parse pointcut expressions built from a user-defined subset of AspectJ's supported + * pointcut primitives. The following restrictions apply: + *
    + *
  • The if, cflow, and cflowbelow pointcut designators are not supported + *
  • Pointcut expressions must be self-contained :- they cannot contain references to other named pointcuts + *
  • The pointcut expression must be anonymous with no formals allowed. + *
+ *

+ * When resolving types in pointcut expressions, the given classloader is used to find types. + *

+ * + * @param supportedPointcutKinds a set of PointcutPrimitives this parser should support + * @throws UnsupportedOperationException if the set contains if, cflow, or cflow below + */ + public static PointcutParser getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution( + Set supportedPointcutKinds, ClassLoader classLoader) { + PointcutParser p = new PointcutParser(supportedPointcutKinds); + p.setClassLoader(classLoader); + return p; + } + + /** + * Create a pointcut parser that can parse the full AspectJ pointcut language with the following exceptions: + *
    + *
  • The if, cflow, and cflowbelow pointcut designators are not supported + *
  • Pointcut expressions must be self-contained :- they cannot contain references to other named pointcuts + *
  • The pointcut expression must be anonymous with no formals allowed. + *
+ */ + protected PointcutParser() { + supportedPrimitives = getAllSupportedPointcutPrimitives(); + setClassLoader(PointcutParser.class.getClassLoader()); + } + + /** + * Create a pointcut parser that can parse pointcut expressions built from a user-defined subset of AspectJ's supported pointcut + * primitives. The following restrictions apply: + *
    + *
  • The if, cflow, and cflowbelow pointcut designators are not supported + *
  • Pointcut expressions must be self-contained :- they cannot contain references to other named pointcuts + *
  • The pointcut expression must be anonymous with no formals allowed. + *
+ * + * @param supportedPointcutKinds a set of PointcutPrimitives this parser should support + * @throws UnsupportedOperationException if the set contains if, cflow, or cflow below + */ + private PointcutParser(Set/* */supportedPointcutKinds) { + supportedPrimitives = supportedPointcutKinds; + for (Iterator iter = supportedPointcutKinds.iterator(); iter.hasNext();) { + PointcutPrimitive element = (PointcutPrimitive) iter.next(); + if ((element == PointcutPrimitive.IF) || (element == PointcutPrimitive.CFLOW) + || (element == PointcutPrimitive.CFLOW_BELOW)) { + throw new UnsupportedOperationException("Cannot handle if, cflow, and cflowbelow primitives"); + } + } + setClassLoader(PointcutParser.class.getClassLoader()); + } + + protected void setWorld(ReflectionWorld aWorld) { + this.world = aWorld; + } + + /** + * Set the classloader that this parser should use for type resolution. + * + * @param aLoader + */ + protected void setClassLoader(ClassLoader aLoader) { + this.classLoaderReference = new WeakClassLoaderReference(aLoader); + world = new ReflectionWorld(this.classLoaderReference.getClassLoader()); + } + + /** + * Set the lint properties for this parser from the given resource on the classpath. + * + * @param resourcePath path to a file containing aspectj lint properties + */ + public void setLintProperties(String resourcePath) throws IOException { + URL url = this.classLoaderReference.getClassLoader().getResource(resourcePath); + InputStream is = url.openStream(); + Properties p = new Properties(); + p.load(is); + setLintProperties(p); + } + + /** + * Set the lint properties for this parser from the given properties set. + * + * @param properties + */ + public void setLintProperties(Properties properties) { + getWorld().getLint().setFromProperties(properties); + } + + /** + * Register a new pointcut designator handler with this parser. This provides an extension mechansim for the integration of + * domain-specific pointcut designators with the AspectJ pointcut language. + * + * @param designatorHandler + */ + public void registerPointcutDesignatorHandler(PointcutDesignatorHandler designatorHandler) { + this.pointcutDesignators.add(designatorHandler); + if (world != null) + world.registerPointcutHandler(designatorHandler); + } + + /** + * Create a pointcut parameter of the given name and type. + * + * @param name + * @param type + * @return + */ + public PointcutParameter createPointcutParameter(String name, Class type) { + return new PointcutParameterImpl(name, type); + } + + /** + * Parse the given pointcut expression. A global scope is assumed for resolving any type references, and the pointcut must + * contain no formals (variables to be bound). + * + * @throws UnsupportedPointcutPrimitiveException if the parser encounters a primitive pointcut expression of a kind not + * supported by this PointcutParser. + * @throws IllegalArgumentException if the expression is not a well-formed pointcut expression + */ + public PointcutExpression parsePointcutExpression(String expression) throws UnsupportedPointcutPrimitiveException, + IllegalArgumentException { + return parsePointcutExpression(expression, null, new PointcutParameter[0]); + } + + /** + * Parse the given pointcut expression. The pointcut is resolved as if it had been declared inside the inScope class (this + * allows the pointcut to contain unqualified references to other pointcuts declared in the same type for example). The pointcut + * may contain zero or more formal parameters to be bound at matched join points. + * + * @throws UnsupportedPointcutPrimitiveException if the parser encounters a primitive pointcut expression of a kind not + * supported by this PointcutParser. + * @throws IllegalArgumentException if the expression is not a well-formed pointcut expression + */ + public PointcutExpression parsePointcutExpression(String expression, Class inScope, PointcutParameter[] formalParameters) + throws UnsupportedPointcutPrimitiveException, IllegalArgumentException { + PointcutExpressionImpl pcExpr = null; + try { + Pointcut pc = resolvePointcutExpression(expression, inScope, formalParameters); + pc = concretizePointcutExpression(pc, inScope, formalParameters); + validateAgainstSupportedPrimitives(pc, expression); // again, because we have now followed any ref'd pcuts + pcExpr = new PointcutExpressionImpl(pc, expression, formalParameters, getWorld()); + } catch (ParserException pEx) { + throw new IllegalArgumentException(buildUserMessageFromParserException(expression, pEx)); + } catch (ReflectionWorld.ReflectionWorldException rwEx) { + throw new IllegalArgumentException(rwEx.getMessage()); + } + return pcExpr; + } + + protected Pointcut resolvePointcutExpression(String expression, Class inScope, PointcutParameter[] formalParameters) { + try { + PatternParser parser = new PatternParser(expression); + parser.setPointcutDesignatorHandlers(pointcutDesignators, world); + Pointcut pc = parser.parsePointcut(); + validateAgainstSupportedPrimitives(pc, expression); + IScope resolutionScope = buildResolutionScope((inScope == null ? Object.class : inScope), formalParameters); + pc = pc.resolve(resolutionScope); + return pc; + } catch (ParserException pEx) { + throw new IllegalArgumentException(buildUserMessageFromParserException(expression, pEx)); + } + } + + protected Pointcut concretizePointcutExpression(Pointcut pc, Class inScope, PointcutParameter[] formalParameters) { + ResolvedType declaringTypeForResolution = null; + if (inScope != null) { + declaringTypeForResolution = getWorld().resolve(inScope.getName()); + } else { + declaringTypeForResolution = ResolvedType.OBJECT.resolve(getWorld()); + } + IntMap arity = new IntMap(formalParameters.length); + for (int i = 0; i < formalParameters.length; i++) { + arity.put(i, i); + } + return pc.concretize(declaringTypeForResolution, declaringTypeForResolution, arity); + } + + /** + * Parse the given aspectj type pattern, and return a matcher that can be used to match types using it. + * + * @param typePattern an aspectj type pattern + * @return a type pattern matcher that matches using the given pattern + * @throws IllegalArgumentException if the type pattern cannot be successfully parsed. + */ + public TypePatternMatcher parseTypePattern(String typePattern) throws IllegalArgumentException { + try { + TypePattern tp = new PatternParser(typePattern).parseTypePattern(); + tp.resolve(world); + return new TypePatternMatcherImpl(tp, world); + } catch (ParserException pEx) { + throw new IllegalArgumentException(buildUserMessageFromParserException(typePattern, pEx)); + } catch (ReflectionWorld.ReflectionWorldException rwEx) { + throw new IllegalArgumentException(rwEx.getMessage()); + } + } + + private World getWorld() { + return world; + } + + /* for testing */ + Set getSupportedPrimitives() { + return supportedPrimitives; + } + + /* for testing */ + IMessageHandler setCustomMessageHandler(IMessageHandler aHandler) { + IMessageHandler current = getWorld().getMessageHandler(); + getWorld().setMessageHandler(aHandler); + return current; + } + + private IScope buildResolutionScope(Class inScope, PointcutParameter[] formalParameters) { + if (formalParameters == null) + formalParameters = new PointcutParameter[0]; + FormalBinding[] formalBindings = new FormalBinding[formalParameters.length]; + for (int i = 0; i < formalBindings.length; i++) { + formalBindings[i] = new FormalBinding(toUnresolvedType(formalParameters[i].getType()), formalParameters[i].getName(), i); + } + if (inScope == null) { + return new SimpleScope(getWorld(), formalBindings); + } else { + ResolvedType inType = getWorld().resolve(inScope.getName()); + ISourceContext sourceContext = new ISourceContext() { + public ISourceLocation makeSourceLocation(IHasPosition position) { + return new SourceLocation(new File(""), 0); + } + + public ISourceLocation makeSourceLocation(int line, int offset) { + return new SourceLocation(new File(""), line); + } + + public int getOffset() { + return 0; + } + + public void tidy() { + } + }; + return new BindingScope(inType, sourceContext, formalBindings); + } + } + + private UnresolvedType toUnresolvedType(Class clazz) { + if (clazz.isArray()) { + return UnresolvedType.forSignature(clazz.getName().replace('.', '/')); + } else { + return UnresolvedType.forName(clazz.getName()); + } + } + + private void validateAgainstSupportedPrimitives(Pointcut pc, String expression) { + switch (pc.getPointcutKind()) { + case Pointcut.AND: + validateAgainstSupportedPrimitives(((AndPointcut) pc).getLeft(), expression); + validateAgainstSupportedPrimitives(((AndPointcut) pc).getRight(), expression); + break; + case Pointcut.ARGS: + if (!supportedPrimitives.contains(PointcutPrimitive.ARGS)) + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.ARGS); + break; + case Pointcut.CFLOW: + CflowPointcut cfp = (CflowPointcut) pc; + if (cfp.isCflowBelow()) { + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.CFLOW_BELOW); + } else { + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.CFLOW); + } + case Pointcut.HANDLER: + if (!supportedPrimitives.contains(PointcutPrimitive.HANDLER)) + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.HANDLER); + break; + case Pointcut.IF: + case Pointcut.IF_FALSE: + case Pointcut.IF_TRUE: + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.IF); + case Pointcut.KINDED: + validateKindedPointcut(((KindedPointcut) pc), expression); + break; + case Pointcut.NOT: + validateAgainstSupportedPrimitives(((NotPointcut) pc).getNegatedPointcut(), expression); + break; + case Pointcut.OR: + validateAgainstSupportedPrimitives(((OrPointcut) pc).getLeft(), expression); + validateAgainstSupportedPrimitives(((OrPointcut) pc).getRight(), expression); + break; + case Pointcut.THIS_OR_TARGET: + boolean isThis = ((ThisOrTargetPointcut) pc).isThis(); + if (isThis && !supportedPrimitives.contains(PointcutPrimitive.THIS)) { + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.THIS); + } else if (!supportedPrimitives.contains(PointcutPrimitive.TARGET)) { + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.TARGET); + } + break; + case Pointcut.WITHIN: + if (!supportedPrimitives.contains(PointcutPrimitive.WITHIN)) + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.WITHIN); + break; + case Pointcut.WITHINCODE: + if (!supportedPrimitives.contains(PointcutPrimitive.WITHIN_CODE)) + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.WITHIN_CODE); + break; + case Pointcut.ATTHIS_OR_TARGET: + isThis = ((ThisOrTargetAnnotationPointcut) pc).isThis(); + if (isThis && !supportedPrimitives.contains(PointcutPrimitive.AT_THIS)) { + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.AT_THIS); + } else if (!supportedPrimitives.contains(PointcutPrimitive.AT_TARGET)) { + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.AT_TARGET); + } + break; + case Pointcut.ATARGS: + if (!supportedPrimitives.contains(PointcutPrimitive.AT_ARGS)) + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.AT_ARGS); + break; + case Pointcut.ANNOTATION: + if (!supportedPrimitives.contains(PointcutPrimitive.AT_ANNOTATION)) + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.AT_ANNOTATION); + break; + case Pointcut.ATWITHIN: + if (!supportedPrimitives.contains(PointcutPrimitive.AT_WITHIN)) + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.AT_WITHIN); + break; + case Pointcut.ATWITHINCODE: + if (!supportedPrimitives.contains(PointcutPrimitive.AT_WITHINCODE)) + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.AT_WITHINCODE); + break; + case Pointcut.REFERENCE: + if (!supportedPrimitives.contains(PointcutPrimitive.REFERENCE)) + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.REFERENCE); + break; + case Pointcut.USER_EXTENSION: + // always ok... + break; + case Pointcut.NONE: // deliberate fall-through + default: + throw new IllegalArgumentException("Unknown pointcut kind: " + pc.getPointcutKind()); + } + } + + private void validateKindedPointcut(KindedPointcut pc, String expression) { + Shadow.Kind kind = pc.getKind(); + if ((kind == Shadow.MethodCall) || (kind == Shadow.ConstructorCall)) { + if (!supportedPrimitives.contains(PointcutPrimitive.CALL)) + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.CALL); + } else if ((kind == Shadow.MethodExecution) || (kind == Shadow.ConstructorExecution)) { + if (!supportedPrimitives.contains(PointcutPrimitive.EXECUTION)) + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.EXECUTION); + } else if (kind == Shadow.AdviceExecution) { + if (!supportedPrimitives.contains(PointcutPrimitive.ADVICE_EXECUTION)) + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.ADVICE_EXECUTION); + } else if (kind == Shadow.FieldGet) { + if (!supportedPrimitives.contains(PointcutPrimitive.GET)) + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.GET); + } else if (kind == Shadow.FieldSet) { + if (!supportedPrimitives.contains(PointcutPrimitive.SET)) + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.SET); + } else if (kind == Shadow.Initialization) { + if (!supportedPrimitives.contains(PointcutPrimitive.INITIALIZATION)) + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.INITIALIZATION); + } else if (kind == Shadow.PreInitialization) { + if (!supportedPrimitives.contains(PointcutPrimitive.PRE_INITIALIZATION)) + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.PRE_INITIALIZATION); + } else if (kind == Shadow.StaticInitialization) { + if (!supportedPrimitives.contains(PointcutPrimitive.STATIC_INITIALIZATION)) + throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.STATIC_INITIALIZATION); + } + } + + private String buildUserMessageFromParserException(String pc, ParserException ex) { + StringBuffer msg = new StringBuffer(); + msg.append("Pointcut is not well-formed: expecting '"); + msg.append(ex.getMessage()); + msg.append("'"); + IHasPosition location = ex.getLocation(); + msg.append(" at character position "); + msg.append(location.getStart()); + msg.append("\n"); + msg.append(pc); + msg.append("\n"); + for (int i = 0; i < location.getStart(); i++) { + msg.append(" "); + } + for (int j = location.getStart(); j <= location.getEnd(); j++) { + msg.append("^"); + } + msg.append("\n"); + return msg.toString(); + } +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/tools/PointcutPrimitive.java b/org.aspectj.matcher/src/org/aspectj/weaver/tools/PointcutPrimitive.java new file mode 100644 index 000000000..ae3f607ff --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/tools/PointcutPrimitive.java @@ -0,0 +1,50 @@ +/* ******************************************************************* + * 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.util.TypeSafeEnum; + +/** + * An enumeration of the different kinds of pointcut primitives + * supported by AspectJ. + */ +public final class PointcutPrimitive extends TypeSafeEnum { + + public static final PointcutPrimitive CALL = new PointcutPrimitive("call",1); + public static final PointcutPrimitive EXECUTION = new PointcutPrimitive("execution",2); + public static final PointcutPrimitive GET = new PointcutPrimitive("get",3); + public static final PointcutPrimitive SET = new PointcutPrimitive("set",4); + public static final PointcutPrimitive INITIALIZATION = new PointcutPrimitive("initialization",5); + public static final PointcutPrimitive PRE_INITIALIZATION = new PointcutPrimitive("preinitialization",6); + public static final PointcutPrimitive STATIC_INITIALIZATION = new PointcutPrimitive("staticinitialization",7); + public static final PointcutPrimitive HANDLER = new PointcutPrimitive("handler",8); + public static final PointcutPrimitive ADVICE_EXECUTION = new PointcutPrimitive("adviceexecution",9); + public static final PointcutPrimitive WITHIN = new PointcutPrimitive("within",10); + public static final PointcutPrimitive WITHIN_CODE = new PointcutPrimitive("withincode",11); + public static final PointcutPrimitive CFLOW = new PointcutPrimitive("cflow",12); + public static final PointcutPrimitive CFLOW_BELOW = new PointcutPrimitive("cflowbelow",13); + public static final PointcutPrimitive IF = new PointcutPrimitive("if",14); + public static final PointcutPrimitive THIS = new PointcutPrimitive("this",15); + public static final PointcutPrimitive TARGET = new PointcutPrimitive("target",16); + public static final PointcutPrimitive ARGS = new PointcutPrimitive("args",17); + public static final PointcutPrimitive REFERENCE = new PointcutPrimitive("reference pointcut",18); + public static final PointcutPrimitive AT_ANNOTATION = new PointcutPrimitive("@annotation",19); + public static final PointcutPrimitive AT_THIS = new PointcutPrimitive("@this",20); + public static final PointcutPrimitive AT_TARGET = new PointcutPrimitive("@target",21); + public static final PointcutPrimitive AT_ARGS = new PointcutPrimitive("@args",22); + public static final PointcutPrimitive AT_WITHIN = new PointcutPrimitive("@within",23); + public static final PointcutPrimitive AT_WITHINCODE = new PointcutPrimitive("@withincode",24); + + private PointcutPrimitive(String name, int key) { + super(name, key); + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/tools/ShadowMatch.java b/org.aspectj.matcher/src/org/aspectj/weaver/tools/ShadowMatch.java new file mode 100644 index 000000000..55e2581df --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/tools/ShadowMatch.java @@ -0,0 +1,58 @@ +/* ******************************************************************* + * 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; + +/** + * The result of asking a PointcutExpression to match at a shadow (method execution, + * handler, constructor call, and so on). + * + */ +public interface ShadowMatch { + + /** + * True iff the pointcut expression will match any join point at this + * shadow (for example, any call to the given method). + */ + boolean alwaysMatches(); + + /** + * True if the pointcut expression may match some join points at this + * shadow (for example, some calls to the given method may match, depending + * on the type of the caller). + *

If alwaysMatches is true, then maybeMatches is always true.

+ */ + boolean maybeMatches(); + + /** + * True iff the pointcut expression can never match any join point at this + * shadow (for example, the pointcut will never match a call to the given + * method). + */ + boolean neverMatches(); + + /** + * Return the result of matching a join point at this shadow with the given + * this, target, and args. + * @param thisObject the object bound to this at the join point + * @param targetObject the object bound to target at the join point + * @param args the arguments at the join point + * @return + */ + JoinPointMatch matchesJoinPoint(Object thisObject, Object targetObject, Object[] args); + + /** + * Set a matching context to be used when matching + * join points. + * @see MatchingContext + */ + void setMatchingContext(MatchingContext aMatchContext); +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/tools/Trace.java b/org.aspectj.matcher/src/org/aspectj/weaver/tools/Trace.java new file mode 100644 index 000000000..c1a040d36 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/tools/Trace.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * 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 interface Trace { + + public void enter (String methodName, Object thiz, Object[] args); + + public void enter (String methodName, Object thiz); + + public void exit (String methodName, Object ret); + + public void exit (String methodName, Throwable th); + + public void exit (String methodName); + + public void event (String methodName); + + public void event (String methodName, Object thiz, Object[] args); + + public void debug (String message); + + public void info (String message); + + public void warn (String message); + + public void warn (String message, Throwable th); + + public void error (String message); + + public void error (String message, Throwable th); + + public void fatal (String message); + + public void fatal (String message, Throwable th); + + + /* + * Convenience methods + */ + public void enter (String methodName, Object thiz, Object arg); + + public void enter (String methodName, Object thiz, boolean z); + + public void exit (String methodName, boolean b); + + public void exit (String methodName, int i); + + public void event (String methodName, Object thiz, Object arg); + + public boolean isTraceEnabled (); + + public void setTraceEnabled (boolean b); +} \ No newline at end of file diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/tools/TraceFactory.java b/org.aspectj.matcher/src/org/aspectj/weaver/tools/TraceFactory.java new file mode 100644 index 000000000..6d819de50 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/tools/TraceFactory.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * 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 org.aspectj.util.LangUtil; + +public abstract class TraceFactory { + + public final static String DEBUG_PROPERTY = "org.aspectj.tracing.debug"; + public final static String FACTORY_PROPERTY = "org.aspectj.tracing.factory"; + public final static String DEFAULT_FACTORY_NAME = "default"; + + protected static boolean debug = getBoolean(DEBUG_PROPERTY,false); + private static TraceFactory instance; + + public Trace getTrace (Class clazz) { + return instance.getTrace(clazz); + } + + public static TraceFactory getTraceFactory () { + return instance; + } + + protected static boolean getBoolean(String name, boolean def) { + String defaultValue = String.valueOf(def); + String value = System.getProperty(name,defaultValue); + return Boolean.valueOf(value).booleanValue(); + } + + static { + + /* + * Allow user to override default behaviour or specify their own factory + */ + String factoryName = System.getProperty(FACTORY_PROPERTY); + if (factoryName != null) try { + if (factoryName.equals(DEFAULT_FACTORY_NAME)) { + instance = new DefaultTraceFactory(); + } + else { + Class factoryClass = Class.forName(factoryName); + instance = (TraceFactory)factoryClass.newInstance(); + } + } + catch (Throwable th) { + if (debug) th.printStackTrace(); + } + + /* + * Try to load external trace infrastructure using supplied factories + */ + if (instance == null) try { + if (LangUtil.is15VMOrGreater()) { + Class factoryClass = Class.forName("org.aspectj.weaver.tools.Jdk14TraceFactory"); + instance = (TraceFactory)factoryClass.newInstance(); + } else { + Class factoryClass = Class.forName("org.aspectj.weaver.tools.CommonsTraceFactory"); + instance = (TraceFactory)factoryClass.newInstance(); + } + } + catch (Throwable th) { + if (debug) th.printStackTrace(); + } + + /* + * Use default trace + */ + if (instance == null) { + instance = new DefaultTraceFactory(); + } + + if (debug) System.err.println("TraceFactory.instance=" + instance); + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/tools/Traceable.java b/org.aspectj.matcher/src/org/aspectj/weaver/tools/Traceable.java new file mode 100644 index 000000000..957786e48 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/tools/Traceable.java @@ -0,0 +1,17 @@ +/******************************************************************************* + * 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 interface Traceable { + + public String toTraceString (); + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/tools/TypePatternMatcher.java b/org.aspectj.matcher/src/org/aspectj/weaver/tools/TypePatternMatcher.java new file mode 100644 index 000000000..be3b84025 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/tools/TypePatternMatcher.java @@ -0,0 +1,23 @@ +/* ******************************************************************* + * 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; + +/** + * A compiled AspectJ type pattern that can be used to + * match against types at runtime. + */ +public interface TypePatternMatcher { + + /** + * Does this type pattern matcher match the + * given type (Class). + */ + public boolean matches(Class aClass); +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/tools/UnsupportedPointcutPrimitiveException.java b/org.aspectj.matcher/src/org/aspectj/weaver/tools/UnsupportedPointcutPrimitiveException.java new file mode 100644 index 000000000..6e0bbd661 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/tools/UnsupportedPointcutPrimitiveException.java @@ -0,0 +1,44 @@ +/* ******************************************************************* + * 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 org.aspectj.weaver.WeaverMessages; + +/** + * @author colyer + * + */ +public class UnsupportedPointcutPrimitiveException extends RuntimeException { + + private static final long serialVersionUID = 3258689888517043251L; + + private PointcutPrimitive unsupportedPrimitive; + private String pointcutExpression; + + public UnsupportedPointcutPrimitiveException(String pcExpression, PointcutPrimitive primitive) { + super(WeaverMessages.format(WeaverMessages.UNSUPPORTED_POINTCUT_PRIMITIVE,pcExpression,primitive.getName())); + this.pointcutExpression = pcExpression; + this.unsupportedPrimitive = primitive; + } + + /** + * @return Returns the unsupportedPrimitive. + */ + public PointcutPrimitive getUnsupportedPrimitive() { + return unsupportedPrimitive; + } + + public String getInvalidPointcutExpression() { + return pointcutExpression; + } + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/tools/WeavingClassLoader.java b/org.aspectj.matcher/src/org/aspectj/weaver/tools/WeavingClassLoader.java new file mode 100644 index 000000000..84987e19b --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/tools/WeavingClassLoader.java @@ -0,0 +1,31 @@ +/* ******************************************************************* + * 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 + * ******************************************************************/ + +package org.aspectj.weaver.tools; + +import java.net.URL; + +/** + * An interface for weaving class loaders to provide callbacks for a + * WeavingAdaptor. + */ +public interface WeavingClassLoader extends GeneratedClassHandler { + + /** + * Returns the aspects to be used by a WeavingAdaptor to weave classes + * defined by the class loader. + * @return the aspects used for weaving classes. + */ + public URL[] getAspectURLs (); + +} diff --git a/org.aspectj.matcher/src/org/aspectj/weaver/weaver-messages.properties b/org.aspectj.matcher/src/org/aspectj/weaver/weaver-messages.properties new file mode 100644 index 000000000..eced71a42 --- /dev/null +++ b/org.aspectj.matcher/src/org/aspectj/weaver/weaver-messages.properties @@ -0,0 +1,199 @@ +##################################################################### +# Copyright (c) 2004 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 version +##################################################################### + +# Messages output by the AspectJ Weaver + +# Pointcut related messages... +argsInDeclare=args() pointcut designator cannot be used in declare statement +cflowInDeclare=cflow{0}() pointcut designator cannot be used in declare statement +ifInDeclare=if() pointcut designator cannot be used in declare statement +thisOrTargetInDeclare={0}() pointcut designator cannot be used in declare statement +abstractPointcut={0} is abstract +abstractPointcutNotMadeConcrete=inherited abstract {0} is not made concrete in {1} +conflictingInheritedPointcuts=conflicting inherited pointcuts in {0} +circularPointcutDeclaration=circular pointcut declaration involving: {0} +cantFindPointcut=can''t find pointcut ''{0}'' on {1} +exactTypePatternRequired=exact type pattern required +pointcutNotVisible=pointcut ''{0}'' is not visible from type ''{1}'' - cannot override +cantBindType=can''t bind type name ''{0}'' +wildcardTypePatternNotAllowed=wildcard type pattern not allowed, must use type name +fieldCantBeVoid=fields cannot have a void type +noNewArrayJoinpointsByDefault=There are no join points for array construction unless -Xjoinpoints:arrayconstruction is specified +unsupportedPointcutPrimitive=Pointcut expression ''{0}'' contains unsupported pointcut primitive ''{1}'' +missingTypePreventsMatch="Unable to determine match at this join point because the type ''{0}'' cannot be found" + +# Declare parents messages... +decpObject=can''t change the parents of java.lang.Object +cantExtendSelf=type ''{0}''can not extend itself +interfaceExtendClass=interface can not extend a class +decpHierarchy=can only insert a class into hierarchy, but {0} is not a subtype of {1} + +# declare precedence messages... +multipleMatchesInPrecedence=multiple matches for {0}, matches both {1} and {2} +circularityInPrecedenceStar=circularity in declare precedence, ''*'' occurs more than once +nonAspectTypesInPrecedence=Non-aspect types can only be specified in a declare precedence statement when subtypes are included. Non-aspect type is : {0} +circularityInPrecedenceTwo=circularity in declare precedence, ''{0}'' matches two patterns + +# declare soft messages... +notThrowable={0} is not a subtype of Throwable + +# itd messages... +itdConsOnAspect=can''t declare constructor on an aspect +returnTypeMismatch=can''t override {0} with {1} return types don''t match +paramTypeMismatch=can''t override {0} with {1} parameter types don''t match +visibilityReduction=can''t override {0} with {1} visibility is reduced +cantOverrideFinalMember=can''t override final {0} +doesntThrow=overriden method doesn't throw {0} +overriddenStatic={0} cannot override {1}; overridden method is static +overridingStatic={0} cannot override {1}; overriding method is static +itdConflict=intertype declaration from {0} conflicts with intertype declaration: {1} from {2} +itdMemberConflict=inter-type declaration from {0} conflicts with existing member: {1} +itdNonExposedImplementor=type {0} must be accessible for weaving interface inter type declaration from aspect {1} +itdAbstractMustBePublicOnInterface=abstract intertype method declaration ''{0}'' on interface {1} must be declared public (compiler limitation) + +# advice messages... +nonVoidReturn=applying to join point that doesn't return void: {0} +incompatibleReturnType=incompatible return type applying to {0} +cantThrowChecked=can''t throw checked exception ''{0}'' at this join point ''{1}'' +circularDependency=circular advice precedence: can''t determine precedence between two or more pieces of advice that apply to the same join point: {0} + +# aspect messages.. +missingPerClause=expected per clause on super aspect not found on {0} +wrongPerClause=wrong kind of per clause on super, expected {0} but found {1} + +# Reweavable messages... +alreadyWoven=class ''{0}'' is already woven and has not been built in reweavable mode +reweavableMode=weaver operating in reweavable mode. Need to verify any required types exist. +processingReweavable=processing reweavable type {0}: {1} +missingReweavableType=type {0} is needed by reweavable type {1} +verifiedReweavableType=successfully verified type {0} exists. Originates from {1} +aspectNeeded=aspect {0} is needed when using type {1} +reweavableAspectNotRegistered=aspect ''{0}'' woven into ''{1}'' must be defined to the weaver (placed on the aspectpath, or defined in an aop.xml file if using LTW). + +# The infamous and deserving a category all of its own... +cantFindType=can''t find type {0} +cantFindCoreType=can''t find critical required type {0} +cantFindTypeWithinpcd=Unable to find type {0} whilst processing within() pointcut at this source location +cftDuringAroundWeave=Can't find type {0} whilst applying around advice +cftDuringAroundWeavePreinit=Can't find type {0} whilst applying around advice to preinitialization join point +cftExceptionType=Can't find exception type {0} whilst processing advice +cftArgType=Can't find type {0} whilst processing args() pcd +cantFindParentType=can''t find type {0} needed to evaluate methods inherited by subtype {1} +cantFindParentTypeNoSub=can''t find type {0} when attempting to find the set of methods it declares +cantFindTypeFields=can''t find fields of missing type {0} +cantFindTypeSuperclass=can''t determine superclass of missing type {0} +cantFindTypeInterfaces=can''t determine implemented interfaces of missing type {0} +cantFindTypeMethods=can''t determine methods of missing type {0} +cantFindTypePointcuts=can''t determine pointcuts declared in missing type {0} +cantFindTypeModifiers=can''t determine modifiers of missing type {0} +cantFindTypeAnnotation=can''t determine annotations of missing type {0} +cantFindTypeAssignable=can''t determine whether missing type {0} is an instance of {1} +cantFindTypeCoerceable=can''t determine whether missing type {0} can be coerced from {1} +cantFindTypeJoinPoint=can''t find type {0} whilst determining signatures of call or execution join point for {1}, this may cause a pointcut to fail to match at this join point +cantFindTypeInterfaceMethods=can''t find type {0} whilst determining all methods of an implementing subtype, this may cause a pointcut to fail to match at a call or execution join point, or an illegal method override via an ITD to go undetected + +# Implementation limitations... +decpBinaryLimitation=can''t use declare parents to change superclass of binary form ''{0}'' (implementation limitation) +overwriteJSR45=overwriting JSR45 information for {0} (compiler limitation) +ifInPerClause=if() pointcut designator cannot be used directly in a per clause (compiler limitation). Create a named pointcut containing the if() and refer to it +ifLexicallyInCflow=if not supported lexically within cflow (compiler limitation) +onlyBeforeOnHandler=Only before advice is supported on handler join points (compiler limitation) +noAroundOnSynchronization=Around advice is not supported on the lock and unlock join points (compiler limitation) +aroundOnPreInit=around on pre-initialization not supported (compiler limitation) +aroundOnInit=around on initialization not supported (compiler limitation) +aroundOnInterfaceStaticInit=around on staticinitialization of interface ''{0}'' not supported (compiler limitation) + +# Bytecode generation nasties... +problemGeneratingMethod=problem generating method {0}.{1} : {2} +classTooBig=The class {0} exceeds the maximum class size supported by the JVM (constant pool too big). + +# Classpath messages +zipfileEntryMissing=zipfile classpath entry does not exist: {0} +zipfileEntryInvalid=zipfile classpath entry is invalid: {0} <{1}> +directoryEntryMissing=directory classpath entry does not exist: {0} +outjarInInputPath=-outjar cannot be a member of -injars, -inpath or -aspectpath + +# Lint messages +problemLoadingXLint=problem loading Xlint properties file: {0}, {1} +unableToLoadXLintDefault=couldn''t load XlintDefault.properties +errorLoadingXLintDefault=problem loading XlintDefault.properties, {0} +invalidXLintKey=invalid Xlint key: {0} +invalidXLintMessageKind=invalid Xlint message kind (must be one of ignore, warning, error): {0} + +# Binding of formals +unboundFormalInPC=the parameter {0} is not bound in [all branches of] pointcut +ambiguousBindingInPC=the binding of parameter {0} is ambiguous in pointcut +ambiguousBindingInOrPC=ambiguous binding of parameter(s) {0} across ''||'' in pointcut +negationDoesntAllowBinding=cannot bind a parameter in a negated expression + +# Java5 + +# Enum +itdcOnEnumNotAllowed=can''t make inter-type constructor declarations on enum types +itdmOnEnumNotAllowed=can''t make inter-type method declarations on enum types +itdfOnEnumNotAllowed=can''t make inter-type field declarations on enum types +cantDecpOnEnumToImplInterface=can''t use declare parents to make enum type {0} implement an interface +cantDecpOnEnumToExtendClass=can''t use declare parents to alter supertype of enum type {0} +cantDecpToMakeEnumSupertype=can''t use declare parents to make ''java.lang.Enum'' the parent of type {0} + +# Annotation +itdcOnAnnotationNotAllowed=can''t make inter-type constructor declarations on annotation types +itdmOnAnnotationNotAllowed=can''t make inter-type method declarations on annotation types +itdfOnAnnotationNotAllowed=can''t make inter-type field declarations on annotation types +cantDecpOnAnnotationToImplInterface=can''t use declare parents to make annotation type {0} implement an interface +cantDecpOnAnnotationToExtendClass=can''t use declare parents to alter supertype of annotation type {0} +cantDecpToMakeAnnotationSupertype=can''t use declare parents to make ''java.lang.annotation.Annotation'' the parent of type {0} +incorrectTargetForDeclareAnnotation={0} is not a valid target for annotation {1}, this annotation can only be applied to these element types {2} + +referenceToNonAnnotationType=Type referred to is not an annotation type: {0} +bindingNonRuntimeRetentionAnnotation=Annotation type {0} does not have runtime retention +noMatchBecauseSourceRetention=Failing match because annotation ''{0}'' on type ''{1}'' has SOURCE retention. Matching allowed when RetentionPolicy is CLASS or RUNTIME + +# Annotation value +invalidAnnotationValue=Invalid annotation value ''{0}'', expected {1} value +unknownAnnotationValue=The annotation ''{0}'' does not define a value named ''{1}'' + +# Generics +cantDecpMultipleParameterizations=Cannot declare parent {0} onto type {1} since it already has {2} in its hierarchy +noParameterizedTypePatternInHandler=a parameterized type pattern may not be used in a handler pointcut expression +incorrectNumberOfTypeArguments=Type pattern does not match because the wrong number of type parameters are specified: Type {0} requires {1} parameter(s) +violatesTypeVariableBounds=Type {0} does not meet the specification for type parameter {1} ({2}) in generic type {3} +notAGenericType=Type pattern does not match because {0} is not a generic type +noStaticInitJPsForParameterizedTypes=no static initialization join points for parameterized types, use raw type instead +noParameterizedTypePatternInWithin=parameterized type pattern not supported by 'within', use a raw type pattern instead +noParameterizedTypesInThisAndTarget=parameterized types not supported for this and target pointcuts (erasure limitation) +noParameterizedTypesInGetAndSet=can't use parameterized type patterns for the declaring type of a get or set pointcut expression (use the raw type instead) +noInitJPsForParameterizedTypes=no [pre]initialization join points for parameterized types, use raw type instead +noGenericThrowables=invalid throws pattern: a generic class may not be a direct or indirect subclass of Throwable +noParameterizedDeclaringTypesWithinCode=can't use parameterized type patterns for the declaring type of a withincode pointcut expression (use the raw type instead) +noParameterizedDeclaringTypesInExecution=can't use parameterized type patterns for the declaring type of an execution pointcut expression (use the raw type instead) +noParameterizedDeclaringTypesInCall=can't use parameterized type patterns for the declaring type of a call pointcut expression (use the raw type instead) +noRawTypePointcutReferences=cannot use a raw type reference to refer to a pointcut in a generic type (use a parameterized reference instead) + +hasMemberNotEnabled=the type pattern {0} can only be used when the -XhasMember option is set + +# Java5 features used in pre-Java 5 environment +atannotationNeedsJava5=the @annotation pointcut expression is only supported at Java 5 compliance level or above +atwithinNeedsJava5=the @within pointcut expression is only supported at Java 5 compliance level or above +atwithincodeNeedsJava5=the @withincode pointcut expression is only supported at Java 5 compliance level or above +atthisNeedsJava5=the @this pointcut expression is only supported at Java 5 compliance level or above +attargetNeedsJava5=the @target pointcut expression is only supported at Java 5 compliance level or above +atargsNeedsJava5=the @args pointcut expression is only supported at Java 5 compliance level or above +declareAtTypeNeedsJava5=declare @type is only supported at Java 5 compliance level or above +declareAtMethodNeedsJava5=declare @method is only supported at Java 5 compliance level or above +declareAtFieldNeedsJava5=declare @field is only supported at Java 5 compliance level or above +declareAtConsNeedsJava5=declare @constructor is only supported at Java 5 compliance level or above +annotationsRequireJava5=annotation type patterns are only supported at Java 5 compliance level or above + +# @AspectJ +returningFormalNotDeclaredInAdvice=the last parameter of this advice must be named ''{0}'' to bind the returning value +thrownFormalNotDeclaredInAdvice=the last parameter of this advice must be named ''{0}'' and be of a subtype of Throwable \ No newline at end of file -- 2.39.5