Browse Source

Allow code generation hints for generated compiler names

tags/V1_7_2
Andy Clement 11 years ago
parent
commit
f54f44a104

+ 34
- 0
aspectj5rt/java5-src/org/aspectj/lang/annotation/control/CodeGenerationHint.java View File

@@ -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 "";

}

+ 83
- 20
org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/compiler/ast/IfPseudoToken.java View 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);


+ 14
- 1
testing/newsrc/org/aspectj/testing/XMLBasedAjcTestCase.java View 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
*/

+ 19
- 0
tests/bugs172/pr398246/Code.java View File

@@ -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");
}
}

+ 20
- 0
tests/bugs172/pr398246/Code2.java View File

@@ -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");
}
}

+ 21
- 0
tests/bugs172/pr398246/Code3.java View File

@@ -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");
}
}

+ 21
- 0
tests/bugs172/pr398246/Code4.java View File

@@ -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");
}
}

+ 11
- 0
tests/bugs172/pr398246/CodeExtra4.java View File

@@ -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();
}
}

+ 32
- 0
tests/src/org/aspectj/systemtest/ajc172/Ajc172Tests.java View 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");
}

+ 51
- 0
tests/src/org/aspectj/systemtest/ajc172/ajc172.xml View File

@@ -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">

Loading…
Cancel
Save