diff options
-rw-r--r-- | aspectj5rt/java5-src/org/aspectj/lang/annotation/control/CodeGenerationHint.java | 34 | ||||
-rw-r--r-- | org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/compiler/ast/IfPseudoToken.java | 103 | ||||
-rw-r--r-- | testing/newsrc/org/aspectj/testing/XMLBasedAjcTestCase.java | 15 | ||||
-rw-r--r-- | tests/bugs172/pr398246/Code.java | 19 | ||||
-rw-r--r-- | tests/bugs172/pr398246/Code2.java | 20 | ||||
-rw-r--r-- | tests/bugs172/pr398246/Code3.java | 21 | ||||
-rw-r--r-- | tests/bugs172/pr398246/Code4.java | 21 | ||||
-rw-r--r-- | tests/bugs172/pr398246/CodeExtra4.java | 11 | ||||
-rw-r--r-- | tests/src/org/aspectj/systemtest/ajc172/Ajc172Tests.java | 32 | ||||
-rw-r--r-- | tests/src/org/aspectj/systemtest/ajc172/ajc172.xml | 51 |
10 files changed, 306 insertions, 21 deletions
diff --git a/aspectj5rt/java5-src/org/aspectj/lang/annotation/control/CodeGenerationHint.java b/aspectj5rt/java5-src/org/aspectj/lang/annotation/control/CodeGenerationHint.java new file mode 100644 index 000000000..7fe9dea24 --- /dev/null +++ b/aspectj5rt/java5-src/org/aspectj/lang/annotation/control/CodeGenerationHint.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2013 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * initial implementation Andy Clement + *******************************************************************************/ +package org.aspectj.lang.annotation.control; + +import java.lang.annotation.Target; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Provide code generation hints to the compiler (e.g. names to use for generated members). + * + * @author Andy Clement + */ +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.METHOD) +public @interface CodeGenerationHint { + + /** + * Defines the name suffix to use for a generated member representing an if pointcut (prefix will be 'ajc$if$'). + * If left blank, a suffix will be generated. + */ + String ifNameSuffix() default ""; + +} diff --git a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/compiler/ast/IfPseudoToken.java b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/compiler/ast/IfPseudoToken.java index 9bf2f3171..821b8b3be 100644 --- a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/compiler/ast/IfPseudoToken.java +++ b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/compiler/ast/IfPseudoToken.java @@ -14,15 +14,23 @@ package org.aspectj.ajdt.internal.compiler.ast; import java.lang.reflect.Modifier; +import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation; import org.aspectj.org.eclipse.jdt.internal.compiler.CompilationResult; +import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Annotation; import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Argument; import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Expression; import org.aspectj.org.eclipse.jdt.internal.compiler.ast.FalseLiteral; +import org.aspectj.org.eclipse.jdt.internal.compiler.ast.MemberValuePair; import org.aspectj.org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; +import org.aspectj.org.eclipse.jdt.internal.compiler.ast.NormalAnnotation; +import org.aspectj.org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; import org.aspectj.org.eclipse.jdt.internal.compiler.ast.ReturnStatement; +import org.aspectj.org.eclipse.jdt.internal.compiler.ast.SingleTypeReference; import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Statement; +import org.aspectj.org.eclipse.jdt.internal.compiler.ast.StringLiteral; import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TrueLiteral; import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; +import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TypeBinding; import org.aspectj.org.eclipse.jdt.internal.compiler.parser.Parser; @@ -35,15 +43,18 @@ import org.aspectj.weaver.patterns.Pointcut; /** * (formals*): ... if(expr) ... * - * generates the following: public static final boolean ajc$if_N(formals*, [thisJoinPoints as needed]) { return expr; } + * generates the following: public static final boolean ajc$if_N(formals*, + * [thisJoinPoints as needed]) { return expr; } * - * Here's the complicated bit, it deals with cflow: (a): ... this(a) && cflow(if (a == foo)) is an error. The way we capture this - * is: We generate the ajc$if method with an (a) parameter, we let eclipse do the proper name binding. We then, as a post pass (that - * we need to do anyway) look for the used parameters. If a is used, we signal an error because a was not one of the cflow - * variables. XXX we'll do this part after we do cflow + * Here's the complicated bit, it deals with cflow: (a): ... this(a) && cflow(if + * (a == foo)) is an error. The way we capture this is: We generate the ajc$if + * method with an (a) parameter, we let eclipse do the proper name binding. We + * then, as a post pass (that we need to do anyway) look for the used + * parameters. If a is used, we signal an error because a was not one of the + * cflow variables. XXX we'll do this part after we do cflow * - * The IfPointcut pcd then generates itself always as a dynamic test, it has to get the right parameters through any named pointcut - * references... + * The IfPointcut pcd then generates itself always as a dynamic test, it has to + * get the right parameters through any named pointcut references... */ public class IfPseudoToken extends PseudoToken { public Expression expr; @@ -61,7 +72,8 @@ public class IfPseudoToken extends PseudoToken { } else if (expr instanceof TrueLiteral) { return IfPointcut.makeIfTruePointcut(Pointcut.SYMBOLIC); } else { - pointcut = new IfPointcut(new ResolvedMemberImpl(Member.METHOD, UnresolvedType.OBJECT, 0, "if_", "()V"), 0); + pointcut = new IfPointcut(new ResolvedMemberImpl(Member.METHOD, + UnresolvedType.OBJECT, 0, "if_", "()V"), 0); } return pointcut; @@ -70,33 +82,80 @@ public class IfPseudoToken extends PseudoToken { /** * enclosingDec is either AdviceDeclaration or PointcutDeclaration */ - public void postParse(TypeDeclaration typeDec, MethodDeclaration enclosingDec) { + public void postParse(TypeDeclaration typeDec, + MethodDeclaration enclosingDec) { // typeDec.scope.problemReporter().signalError(sourceStart, sourceEnd, // "if pcd is not implemented in 1.1alpha1"); // XXX need to implement correctly if (pointcut == null) return; - testMethod = makeIfMethod(enclosingDec.compilationResult, enclosingDec, typeDec); + testMethod = makeIfMethod(enclosingDec.compilationResult, enclosingDec, + typeDec); AstUtil.addMethodDeclaration(typeDec, testMethod); } + private final static char[] CodeGenerationHint = "CodeGenerationHint".toCharArray(); + private final static char[] FullyQualifiedCodeGenerationHint = "org.aspectj.lang.annotation.control.CodeGenerationHint".toCharArray(); + private final static char[] IfNameSuffix = "ifNameSuffix".toCharArray(); + // XXX todo: make sure that errors in Arguments only get displayed once - private MethodDeclaration makeIfMethod(CompilationResult result, MethodDeclaration enclosingDec, - TypeDeclaration containingTypeDec) { + private MethodDeclaration makeIfMethod(CompilationResult result, + MethodDeclaration enclosingDec, TypeDeclaration containingTypeDec) { MethodDeclaration ret = new IfMethodDeclaration(result, pointcut); - ret.modifiers = ClassFileConstants.AccStatic | ClassFileConstants.AccFinal | ClassFileConstants.AccPublic; + ret.modifiers = ClassFileConstants.AccStatic + | ClassFileConstants.AccFinal | ClassFileConstants.AccPublic; ret.returnType = AstUtil.makeTypeReference(TypeBinding.BOOLEAN); + + String nameSuffix = null; + + if (enclosingDec!=null && enclosingDec.annotations!=null) { + NormalAnnotation interestingAnnotation = null; + Annotation[] as = enclosingDec.annotations; + if (as!=null) { + for (int a = 0; a < as.length && interestingAnnotation == null; a++) { + if (as[a] instanceof NormalAnnotation) { + TypeReference tr = as[a].type; + if (tr instanceof SingleTypeReference) { + if (CharOperation.equals(CodeGenerationHint,((SingleTypeReference)tr).token)) { + interestingAnnotation = (NormalAnnotation)as[a]; + } + } else if (tr instanceof QualifiedTypeReference) { + char[] qualifiedName = CharOperation.concatWith(((QualifiedTypeReference)tr).tokens,'.'); + if (CharOperation.equals(FullyQualifiedCodeGenerationHint,qualifiedName)) { + interestingAnnotation = (NormalAnnotation)as[a]; + } + } + } + } + } + if (interestingAnnotation!=null) { + MemberValuePair[] memberValuePairs = interestingAnnotation.memberValuePairs; + for (MemberValuePair memberValuePair: memberValuePairs) { + if (CharOperation.equals(IfNameSuffix,memberValuePair.name) && (memberValuePair.value instanceof StringLiteral)) { + nameSuffix = new String(((StringLiteral)memberValuePair.value).source()); + } + } + } + } + + // create a more stable name 277508 StringBuffer ifSelector = new StringBuffer(); ifSelector.append("ajc$if$"); - ifSelector.append(Integer.toHexString(expr.sourceStart)); + if (nameSuffix == null || nameSuffix.length()==0) { + ifSelector.append(Integer.toHexString(expr.sourceStart)); + } else { + ifSelector.append(nameSuffix); + } // possibly even better logic for more reliable name: // if (enclosingDec instanceof AdviceDeclaration) { - // // name is ajc$if$<adviceSequenceNumber>$<hashcodeOfIfExpressionInHex> + // // name is + // ajc$if$<adviceSequenceNumber>$<hashcodeOfIfExpressionInHex> // ifSelector.append("ajc$if$"); - // ifSelector.append(((AdviceDeclaration) enclosingDec).adviceSequenceNumberInType); + // ifSelector.append(((AdviceDeclaration) + // enclosingDec).adviceSequenceNumberInType); // ifSelector.append("$").append(Integer.toHexString(expr.toString().hashCode())); // } else if (enclosingDec instanceof PointcutDeclaration) { // // name is pointcut selector then $if$<hashcodeOfIfExpressionInHex> @@ -104,16 +163,19 @@ public class IfPseudoToken extends PseudoToken { // ifSelector.append("$if$"); // ifSelector.append(Integer.toHexString(expr.toString().hashCode())); // } else { - // throw new BCException("Unexpected enclosing declaration of " + enclosingDec + " for if pointcut designator"); + // throw new BCException("Unexpected enclosing declaration of " + + // enclosingDec + " for if pointcut designator"); // } // hashcode of expression ret.selector = ifSelector.toString().toCharArray(); ret.arguments = makeArguments(enclosingDec, containingTypeDec); - ret.statements = new Statement[] { new ReturnStatement(expr, expr.sourceStart, expr.sourceEnd) }; + ret.statements = new Statement[] { new ReturnStatement(expr, + expr.sourceStart, expr.sourceEnd) }; return ret; } - private Argument[] makeArguments(MethodDeclaration enclosingDec, TypeDeclaration containingTypeDec) { + private Argument[] makeArguments(MethodDeclaration enclosingDec, + TypeDeclaration containingTypeDec) { Argument[] baseArguments = enclosingDec.arguments; int len = baseArguments.length; if (enclosingDec instanceof AdviceDeclaration) { @@ -123,7 +185,8 @@ public class IfPseudoToken extends PseudoToken { Argument[] ret = new Argument[len]; for (int i = 0; i < len; i++) { Argument a = baseArguments[i]; - ret[i] = new Argument(a.name, AstUtil.makeLongPos(a.sourceStart, a.sourceEnd), a.type, Modifier.FINAL); + ret[i] = new Argument(a.name, AstUtil.makeLongPos(a.sourceStart, + a.sourceEnd), a.type, Modifier.FINAL); } ret = AdviceDeclaration.addTjpArguments(ret, containingTypeDec); diff --git a/testing/newsrc/org/aspectj/testing/XMLBasedAjcTestCase.java b/testing/newsrc/org/aspectj/testing/XMLBasedAjcTestCase.java index d4e46973d..9ecd5d885 100644 --- a/testing/newsrc/org/aspectj/testing/XMLBasedAjcTestCase.java +++ b/testing/newsrc/org/aspectj/testing/XMLBasedAjcTestCase.java @@ -31,6 +31,7 @@ import org.apache.commons.digester.Digester; import org.aspectj.apache.bcel.classfile.JavaClass; import org.aspectj.apache.bcel.classfile.LocalVariable; import org.aspectj.apache.bcel.classfile.LocalVariableTable; +import org.aspectj.apache.bcel.classfile.Method; import org.aspectj.apache.bcel.util.ClassPath; import org.aspectj.apache.bcel.util.SyntheticRepository; import org.aspectj.tools.ajc.AjcTestCase; @@ -54,7 +55,7 @@ public abstract class XMLBasedAjcTestCase extends AjcTestCase { public XMLBasedAjcTestCase() { } - + /** * You must define a suite() method in subclasses, and return the result of calling this method. (Don't you hate static methods * in programming models). For example: @@ -317,6 +318,18 @@ public abstract class XMLBasedAjcTestCase extends AjcTestCase { return repos.loadClass(clazzname); } + protected Method getMethodStartsWith(JavaClass jc, String prefix) { + Method[] meths = jc.getMethods(); + for (int i = 0; i < meths.length; i++) { + Method method = meths[i]; + System.out.println(method); + if (method.getName().startsWith(prefix)) { + return method; + } + } + return null; + } + /** * Sort it by name then start position */ diff --git a/tests/bugs172/pr398246/Code.java b/tests/bugs172/pr398246/Code.java new file mode 100644 index 000000000..beacd74b4 --- /dev/null +++ b/tests/bugs172/pr398246/Code.java @@ -0,0 +1,19 @@ +import org.aspectj.lang.annotation.control.*; +import java.lang.annotation.*; + +public class Code { + public static boolean isTrue = true; + + public void m() { + } + public static void main(String []argv) { + new Code().m(); + } +} + +aspect X { + @CodeGenerationHint(ifNameSuffix="andy") + before(): execution(* Code.*(..)) && if(Code.isTrue) { + System.out.println("advice"); + } +} diff --git a/tests/bugs172/pr398246/Code2.java b/tests/bugs172/pr398246/Code2.java new file mode 100644 index 000000000..c8c0baafb --- /dev/null +++ b/tests/bugs172/pr398246/Code2.java @@ -0,0 +1,20 @@ +import org.aspectj.lang.annotation.control.*; +import java.lang.annotation.*; + +public class Code2 { + public static boolean isTrue = true; + + public void m() { + } + public static void main(String []argv) { + new Code2().m(); + } +} + +aspect X { + @CodeGenerationHint(ifNameSuffix="fred") + pointcut p(): execution(* Code2.*(..)) && if(Code2.isTrue); + before(): p() { + System.out.println("advice"); + } +} diff --git a/tests/bugs172/pr398246/Code3.java b/tests/bugs172/pr398246/Code3.java new file mode 100644 index 000000000..5588905f1 --- /dev/null +++ b/tests/bugs172/pr398246/Code3.java @@ -0,0 +1,21 @@ +import java.lang.annotation.*; + +public class Code3 { + public static boolean isTrue = true; + + public void m() { + } + public static void main(String []argv) { + new Code3().m(); + } +} + +aspect X { + + @org.aspectj.lang.annotation.control.CodeGenerationHint(ifNameSuffix="barney") + pointcut p(): execution(* Code3.*(..)) && if(Code3.isTrue); + + before(): p() { + System.out.println("advice"); + } +} diff --git a/tests/bugs172/pr398246/Code4.java b/tests/bugs172/pr398246/Code4.java new file mode 100644 index 000000000..488092d93 --- /dev/null +++ b/tests/bugs172/pr398246/Code4.java @@ -0,0 +1,21 @@ +import java.lang.annotation.*; + +public class Code4 { + public static boolean isTrue = true; + + public void m() { + } + public static void main(String []argv) { + new Code4().m(); + } +} + +aspect X { + + @org.aspectj.lang.annotation.control.CodeGenerationHint(ifNameSuffix="sid") + pointcut p(): execution(* Code*.*(..)) && if(Code4.isTrue); + + before(): p() { + System.out.println("advice"); + } +} diff --git a/tests/bugs172/pr398246/CodeExtra4.java b/tests/bugs172/pr398246/CodeExtra4.java new file mode 100644 index 000000000..e192328d6 --- /dev/null +++ b/tests/bugs172/pr398246/CodeExtra4.java @@ -0,0 +1,11 @@ +import java.lang.annotation.*; + +public class CodeExtra4 { + public static boolean isTrue = true; + + public void m() { } + + public static void main(String []argv) { + new CodeExtra4().m(); + } +} diff --git a/tests/src/org/aspectj/systemtest/ajc172/Ajc172Tests.java b/tests/src/org/aspectj/systemtest/ajc172/Ajc172Tests.java index 292efcbee..eea4030d0 100644 --- a/tests/src/org/aspectj/systemtest/ajc172/Ajc172Tests.java +++ b/tests/src/org/aspectj/systemtest/ajc172/Ajc172Tests.java @@ -14,6 +14,8 @@ import java.io.File; import junit.framework.Test; +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.classfile.Method; import org.aspectj.testing.XMLBasedAjcTestCase; /** @@ -21,6 +23,36 @@ import org.aspectj.testing.XMLBasedAjcTestCase; */ public class Ajc172Tests extends org.aspectj.testing.XMLBasedAjcTestCase { + public void testIfPointcutNames_pr398246() throws Exception{ + runTest("if pointcut names"); + JavaClass jc = getClassFrom(ajc.getSandboxDirectory(),"X"); + Method m = getMethodStartsWith(jc,"ajc$if"); + assertEquals("ajc$if$andy",m.getName()); + } + + public void testIfPointcutNames_pr398246_2() throws Exception { + runTest("if pointcut names 2"); + JavaClass jc = getClassFrom(ajc.getSandboxDirectory(),"X"); + Method m = getMethodStartsWith(jc,"ajc$if"); + assertEquals("ajc$if$fred",m.getName()); + } + + // fully qualified annotation name is used + public void testIfPointcutNames_pr398246_3() throws Exception { + runTest("if pointcut names 3"); + JavaClass jc = getClassFrom(ajc.getSandboxDirectory(),"X"); + Method m = getMethodStartsWith(jc,"ajc$if"); + assertEquals("ajc$if$barney",m.getName()); + } + + // compiling a class later than the initial build - does it pick up the right if clause name? + public void testIfPointcutNames_pr398246_4() throws Exception{ + runTest("if pointcut names 4"); + JavaClass jc = getClassFrom(ajc.getSandboxDirectory(),"X"); + Method m = getMethodStartsWith(jc,"ajc$if"); + assertEquals("ajc$if$sid",m.getName()); + } + public void testOptionalAspects_pr398588() { runTest("optional aspects"); } diff --git a/tests/src/org/aspectj/systemtest/ajc172/ajc172.xml b/tests/src/org/aspectj/systemtest/ajc172/ajc172.xml index 81217b60f..7dc6ce11b 100644 --- a/tests/src/org/aspectj/systemtest/ajc172/ajc172.xml +++ b/tests/src/org/aspectj/systemtest/ajc172/ajc172.xml @@ -33,6 +33,57 @@ </stdout> </run> </ajc-test> + + <ajc-test dir="bugs172/pr398246" title="if pointcut names"> + <compile files="Code.java" options="-1.5"> + </compile> + <run class="Code" options="-1.5"> + <stdout> + <line text="advice"/> + <line text="advice"/> + </stdout> + </run> + </ajc-test> + + <ajc-test dir="bugs172/pr398246" title="if pointcut names 2"> + <compile files="Code2.java" options="-1.5"> + </compile> + <run class="Code2" options="-1.5"> + <stdout> + <line text="advice"/> + <line text="advice"/> + </stdout> + </run> + </ajc-test> + + <ajc-test dir="bugs172/pr398246" title="if pointcut names 3"> + <compile files="Code3.java" options="-1.5"> + </compile> + <run class="Code3" options="-1.5"> + <stdout> + <line text="advice"/> + <line text="advice"/> + </stdout> + </run> + </ajc-test> + + <ajc-test dir="bugs172/pr398246" title="if pointcut names 4"> + <compile files="Code4.java" options="-1.5"></compile> + <compile files="Code4.java" options="-1.5" outjar="aspects.jar"/> + <run class="Code4" options="-1.5"> + <stdout> + <line text="advice"/> + <line text="advice"/> + </stdout> + </run> + <compile files="CodeExtra4.java" aspectpath="aspects.jar" options="-1.5"></compile> + <run class="CodeExtra4" options="-1.5"> + <stdout> + <line text="advice"/> + <line text="advice"/> + </stdout> + </run> + </ajc-test> <ajc-test dir="bugs172/pr389750" title="inconsistent class file"> <compile files="Code.aj" options="-1.5"> |