]> source.dussan.org Git - aspectj.git/commitdiff
Allow code generation hints for generated compiler names
authorAndy Clement <andrew.clement@gmail.com>
Tue, 22 Jan 2013 01:56:20 +0000 (17:56 -0800)
committerAndy Clement <andrew.clement@gmail.com>
Tue, 22 Jan 2013 01:56:20 +0000 (17:56 -0800)
aspectj5rt/java5-src/org/aspectj/lang/annotation/control/CodeGenerationHint.java [new file with mode: 0644]
org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/compiler/ast/IfPseudoToken.java
testing/newsrc/org/aspectj/testing/XMLBasedAjcTestCase.java
tests/bugs172/pr398246/Code.java [new file with mode: 0644]
tests/bugs172/pr398246/Code2.java [new file with mode: 0644]
tests/bugs172/pr398246/Code3.java [new file with mode: 0644]
tests/bugs172/pr398246/Code4.java [new file with mode: 0644]
tests/bugs172/pr398246/CodeExtra4.java [new file with mode: 0644]
tests/src/org/aspectj/systemtest/ajc172/Ajc172Tests.java
tests/src/org/aspectj/systemtest/ajc172/ajc172.xml

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 (file)
index 0000000..7fe9dea
--- /dev/null
@@ -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 "";
+
+}
index 9bf2f3171fe172be89e91daa6da9c519fbe334fd..821b8b3be416049380a5655e86ce0c719c130688 100644 (file)
@@ -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);
 
index d4e46973db9d0e4e185b7f9bf9d4772016475bb6..9ecd5d885bf9cbca769cdb50029a8bcdeab2547e 100644 (file)
@@ -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 (file)
index 0000000..beacd74
--- /dev/null
@@ -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 (file)
index 0000000..c8c0baa
--- /dev/null
@@ -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 (file)
index 0000000..5588905
--- /dev/null
@@ -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 (file)
index 0000000..488092d
--- /dev/null
@@ -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 (file)
index 0000000..e192328
--- /dev/null
@@ -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();
+  }
+}
index 292efcbee7811da202c09dadbb06a129f0f4ecbe..eea4030d0094d1f238c673c63b712211070a9031 100644 (file)
@@ -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");
        }
index 81217b60f32621575f1900ccbf1e982eea733193..7dc6ce11b052f7ae5b196655b7f0b2b0294c8108 100644 (file)
             </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">