Bug 127292 - pointcut definition not discriminating properly for annotated types/methods
Summary: pointcut definition not discriminating properly for annotated types/methods
Status: RESOLVED WONTFIX
Alias: None
Product: AspectJ
Classification: Tools
Component: Compiler (show other bugs)
Version: 1.5.0   Edit
Hardware: PC Windows XP
: P3 normal (vote)
Target Milestone: ---   Edit
Assignee: aspectj inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2006-02-10 12:53 EST by jody brownell CLA
Modified: 2006-06-01 03:02 EDT (History)
0 users

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description jody brownell CLA 2006-02-10 12:53:51 EST
I have created a simple test Aspect which advises methods which are annotated with @Lock.Read and @Lock.Write when belonging to a type annotated with @Lock.

When debugging, I noticed that ALL classes in my eclipse project had been instrumented with extraneous fields interface's (fields / methods) even though they were not annotated with the required annotations for the aspect to advise them.

The orginal PCD which worked but caused all classes to be instrumented (but not advised) was

	pointcut readLock(): execution(@Lock.Read * * (..));
	pointcut writeLock(): execution(@Lock.Write * * (..));

When changing these definitions to 

	pointcut readLock(): execution(@Lock.Read * (@Lock *).*(..));
	pointcut writeLock(): execution(@Lock.Write * (@Lock *).*(..));

only classes annotated with @Lock on the type and @Lock.Read/Write on methods were actually instrumented. This was the desired result. Both work equally well at runtime but the orginal pcd will cause all classes to have placeholder fields and extraneous methods (as per $ajcMighHaveAspect?) even though they will _never_ be advised. 


The aspect: 

import java.util.concurrent.locks.ReentrantReadWriteLock;

public abstract aspect LockAspect pertarget(fair(Lock) && (readLock() ||
writeLock()))
{
	declare error: execution(@Lock.Read * * (..)) && !@within(Lock):

		"@Lock.Read and @Lock.Write cannot be used unless the Class/Interface is annotated with @Lock"; 
	
	private ReentrantReadWriteLock.ReadLock read;
	private ReentrantReadWriteLock.WriteLock write;
	private ReentrantReadWriteLock lock;

	public LockAspect(boolean fair)
	{
		lock = new ReentrantReadWriteLock(fair);
		
		read = lock.readLock();
		write = lock.writeLock();
	}
	
	abstract pointcut fair(Lock lock);

// fixed pcd
//	pointcut readLock(): execution(@Lock.Read * (@Lock *).*(..));
//	pointcut writeLock(): execution(@Lock.Write * (@Lock *).*(..));

// broken? pcd
	pointcut readLock(): execution(@Lock.Read * * (..));
	pointcut writeLock(): execution(@Lock.Write * * (..));
	
	Object around(): readLock()
	{
		Object o = null;

		try
		{
			read.lock();
			o = proceed();
		}
		finally
		{
			read.unlock();
		}

		return o;
	}

	Object around(): writeLock()
	{
		Object o = null;

		try
		{
			write.lock();
			o = proceed();
		}
		finally
		{
			write.unlock();
		}

		return o;
	}
}

aspect UnfairLockAspect extends LockAspect
{
	pointcut fair(Lock lock): @within(lock) && if(lock.isFair() == false);
	
	public UnfairLockAspect()
	{
		super(false);
	}
}

aspect FairLockAspect extends LockAspect
{
	pointcut fair(Lock lock): @within(lock) && if(lock.isFair() == true);
	
	public FairLockAspect()
	{
		super(true);
	}
}
The annotation:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Lock
{
	/**
	 * Whether the locks granted are being granted in a fair manner.
	 * 
	 * @return if the lock associated with this object is fair or not.
	 * @see java.util.concurrent.locks.ReentrantReadWriteLock#ReentrantReadWriteLock(boolean)
	 */
	public boolean isFair() default false;

	/**
	 * Mark a method as having a read lock
	 */
	@Documented
	@Retention(RetentionPolicy.RUNTIME)
	@Target(ElementType.METHOD)
	public @interface Read
	{
	}

	/**
	 * Mark a method as having a write lock
	 */
	@Documented
	@Retention(RetentionPolicy.RUNTIME)
	@Target(ElementType.METHOD)
	public @interface Write
	{
	}
}
Comment 1 Andrew Clement CLA 2006-05-18 05:33:53 EDT
I think this is working as designed - possibly could be considered a compiler limitation.

For a long time we had issues with putting the ajc$mightHaveAspect interface on too many types - it is all down to how much analysis we can do (or want to do) at weave time to reduce the set of types that it gets 'attached' to.  In the 1.5.0 timeframe we did a lot of work under bug 75442 to address this for some of the per clauses - we tried an approach for pertarget but that is the one that is most difficult to optimize for and we had to remove it (see 75442 for more details).

Given that you are using execution() PCDs - can you change from pertarget to perthis() ?   For this program (a cutdown variant of yours that uses perthis):

@Retention(RetentionPolicy.RUNTIME) @interface Lock { }
@Retention(RetentionPolicy.RUNTIME) @interface Read { }
@Retention(RetentionPolicy.RUNTIME) @interface Write { }

aspect LockAspect perthis(fair(Lock) && (readLock() || writeLock())) {

// fixed pcd
//      pointcut readLock(): execution(@Read * (@Lock *).*(..));
//      pointcut writeLock(): execution(@Write * (@Lock *).*(..));

// broken? pcd
        pointcut readLock(): execution(@Read * * (..));
        pointcut writeLock(): execution(@Write * * (..));

  Object around(): readLock()  { return proceed(); }
  Object around(): writeLock() { return proceed(); }

  pointcut fair(lock): @within(Lock) && if(lock.toString().equals("foo"));
}

public class Test { }

@Lock class A {

  public void m1() {}
  @Read public void m2() {}
  @Write public void m3() {}
}

class B {
  public void m1() {}
  @Read public void m2() {}
  @Write public void m3() {}
}

Only type A gets the ajc$mightHaveAspect interface.
Comment 2 Andrew Clement CLA 2006-06-01 03:02:15 EDT
Closing as nothing to fix right now ... using execution() rather than call() will limit who gets the interface.