Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
RE: [aspectj-dev] how hard it is to introduce a new kind of pointcut inaspectj?

Hi Wes,

The original walk through is organized nicely. It is
splitted to several parts, where each part adds extra
functionalities and can be be tested before going
further. Unfortunately, none of the intermediate parts
worked for me. I have to reach the last part, with
some extra steps to make it work. So although I have
my steps written down, it seems too ad-hoc to me since
it's not modulized and with no intermediate testing. I
also reorganized it to fit my view of this task.
Therefore, it's not something that could be simply
patched to the original walkthrough. I will be very
happy to share out my experience (my notes is attached
in case someone find it useful somehow), but I think
it will be better for a more experienced developper to
design a better walkthrough for the purpose of
official documents.

wrt to the if clause, what we want to do actually is
not a real pointcut. We just want to use the syntax of
pointcut to specify some patterns in the source code
that we want to locate. No weaving is required at all.
I will check out the refactoring framework, maybe
there is something more suitable for us. Any
suggestion will be appreciated.
 
Thanks!
Lingli  

--- Wes Isberg <wes@xxxxxxxxxxxxxx> wrote:

> Hi -
> 
> We're aware the docs are out of date.  Would you
> publish the
> changes to the steps?  We'd much appreciate it.
> 
> wrt making a join point out of bytecode, that's a
> harder
> topic.  Our concern is typically whether the
> programmer
> would recognize whether the language feature is a
> relatively
> stable and identifiable thing in the program. 
> "throws" is 
> not bad in that respect, but an "if" clause,
> particularly one
> with a constant that could be optimized away, is not
> good.
> We've not addressed the engineering difficulties
> where
> there's no good language reason to do so.
> 
> Thanks!
> Wes
> 
> > ------------Original Message------------
> > From: lingli zhang <lingli_z@xxxxxxxxx>
> > To: "AspectJ developer discussions"
> <aspectj-dev@xxxxxxxxxxx>
> > Date: Mon, Oct-24-2005 11:13 AM
> > Subject: RE: [aspectj-dev] how hard it is to
> introduce a new kind of pointcut inaspectj?
> >
> > Hi,
> > 
> > Thank you for all the replies. The walk through in
> the
> > document is a little bit out-of-date. But it was a
> big
> > help for the beginners like me. With several
> changes
> > to the steps, I was able to add the "throw"
> pointcut!
> > :)
> > 
> > About the LoopAJ, I did know about this project.
> > However, we want better IDE support like Eclipse
> with
> > more source location information and UI features.
> > That's why we choose aspjectj instead of abc
> although
> > I know abc is much more extensible than aspectj.
> > 
> > My next quesion is it is possible to express and
> > implement some pointcuts to match a code line
> like:
> > 
> > if (DEBUG == true) {
> > }
> > 
> > It seems no direct bytecode matching for this kind
> of
> > statement. Do you think it's feasible by extending
> > aspectj somehow? Any suggestions?
> > 
> > Thanks a lot!
> > Lingli
> >  
> >  
> > 
> > --- Eric Bodden <eric@xxxxxxxxx> wrote:
> > 
> > > Hi.
> > > 
> > > Generally it's not that hard using the
> AspectBench
> > > compiler, Wes mentioned
> > > already (given you have some basic knowledge
> about
> > > compiler construction). 
> > > However, with respect to loops, such an
> extension
> > > already exists in the form
> > > of "LoopsAJ" -
> > >
> >
>
http://www.cs.manchester.ac.uk/cnc/projects/loopsaj/.
> > > This
> > > actually *is* an extension to the AspectBench
> > > Compiler.
> > > 
> > > However, I am not sure what you mean by "the
> 'new'
> > > bytecode". Is that not
> > > something that can be captured by
> > > call(WhatEverType.new(..)) ?
> > > 
> > > Eric
> > > 
> > > 
> > > lingli zhang wrote:
> > > > Hi,
> > > > 
> > > > I am newbie of aspectj. I am wondering how
> hard it
> > > is to introduce a
> > > > new kind of pointcut into the current aspectj
> > > project. For example,
> > > > if I want to match the "new" bytecode, or if I
> > > want to match some
> > > > sort of loop structures, I need to introduce
> > > "new()" or "loop()" to
> > > > the syntax of the pointcut. Is there any
> grammar
> > > file I should
> > > > change, where is the scanner and parser files?
> Can
> > > anyone point me to
> > > > some example or a starting point to do it?    
>  
> > > > 
> > > > Thanks a lot!
> > > > Lingli
> > > > 
> > > > 
> > > 
> > > -- 
> > > Eric Bodden
> > > Chair I2 for Programming Languages and Program
> > > Analysis
> > > RWTH Aachen University
> > > 
> > > 
> > > _______________________________________________
> > > aspectj-dev mailing list
> > > aspectj-dev@xxxxxxxxxxx
> > >
> https://dev.eclipse.org/mailman/listinfo/aspectj-dev
> > > 
> > 
> > 
> > 
> > 	
> > 		
> > __________________________________ 
> > Yahoo! Mail - PC Magazine Editors' Choice 2005 
> > http://mail.yahoo.com
> > _______________________________________________
> > aspectj-dev mailing list
> > aspectj-dev@xxxxxxxxxxx
> >
> https://dev.eclipse.org/mailman/listinfo/aspectj-dev
> > 
> 
> _______________________________________________
> aspectj-dev mailing list
> aspectj-dev@xxxxxxxxxxx
> https://dev.eclipse.org/mailman/listinfo/aspectj-dev
> 


		
__________________________________ 
Yahoo! FareChase: Search multiple travel sites in one click.
http://farechase.yahoo.com
Title: Add a simple pointcut pattern to aspectj

Add a simple pointcut pattern to aspectj

Following is a walkthrough of adding a new simple pointcut pattern to aspectj. It's based on the walkthrough provided in "docs/developer/compiler-weaver/index.html" under the docs module of the org.aspectj project checked out from dev.org, but with many extra steps to make it work with the current version of aspectj.

Target syntax with an example

What we are about to add is a simple pointcut pattern, called "throw()", which matches every point in the bytecode that throws an exception. Following is the test case we want to pass:

// Throws.aj

public class Throws {
static void willThrow() {
throw new RuntimeException("expected exception ");
}

public static void main(String[] args) {
try {
willThrow();
} catch (RuntimeException re) {
}
}
}


aspect A {
before(): withincode(void willThrow()){
System.out.println("about to execute: " + thisJoinPoint);
}

before(Throwable t): throw() && args(t){
System.out.println("about to throw: " + t + " at " + thisJoinPoint);
}
}

The expected output from this example is:

about to execute: call(java.lang.RuntimeException(String))
about to execute: throw(throw)
about to throw: java.lang.RuntimeException: expected exception at throw(throw)

The first two lines are produced by the first before advice: the first line is before the execution of constructor of RuntimeException and the second line is before the execution of the second before advice. The third line is produced by the second before advice, which is right before the ATHROW bytecode in the willThrow() method.

Part1: make the parser recognize "throw()".

The pointcut patterns are parsed by weaver/src/org/aspectj/weaver/patterns/PatternParser.java. Let's add the following code into org.aspectj.weaver.patterns.PatternParser.parseSinglePointcut() before the last else statement:

         else  if (kind.equals("throw")) {
eat("(");
eat(")");
return new KindedPointcut(Shadow.Throw,
new SignaturePattern(Member.THROW, ModifiersPattern.ANY,
TypePattern.ANY, TypePattern.ANY, NamePattern.ANY,
TypePatternList.ANY, ThrowsPattern.ANY,
AnnotationTypePattern.ANY));
}

We need to add couple things more to make the added code compilable:

  • Modify runtime/src/org/aspectj/lang/JoinPoint.java to add a name for the Throw shadow kind.
    static String THROW = "throw";
  • Modify weaver/src/org/aspectj/weaver/Shadow.java to add the Throw shadow kind. This adds a static typesafe enum for the Throw Kind. The constructor uses the name from the runtime API to ensure that these names will always match. The '12' is used for serialization of this kind to classfiles and is part of the binary API for aspectj. The final 'true' indicates that this joinpoint has its arguments on the stack. This is because the throw bytecode in Java operates on a single argument that is a Throwable which must be the top element on the stack. This argument is removed from the stack by the bytecode.
    public static final Kind Throw = new Kind(JoinPoint.THROW, 12, true);

    Also, modify next couple lines to

    public static final int MAX_SHADOW_KIND = 12;

    public static final Kind[] SHADOW_KINDS = new Kind[] {
    MethodCall, ConstructorCall, MethodExecution, ConstructorExecution,
    FieldGet, FieldSet, StaticInitialization, PreInitialization,
    AdviceExecution, Initialization, ExceptionHandler, Throw,
    };
  • Also modify the neverHasTarget method of Shadow.Kind in the same file to include the Throw kind because in Java there is no target for the throwing of an exception.
    public boolean neverHasTarget() {
    return this == ConstructorCall
    || this == ExceptionHandler
    || this == PreInitialization
    // here
    || this == Throw
    //
    || this == StaticInitialization;
    }

  • In the read method on Shadow.Kind, add another case to read in our new Shadow.Kind.
    case 12: return Throw;
  • Modify weaver/src/org/aspectj/weaver/Member.java as follow:

    In the read method on Member.Kind, add another case

    case 8: return THROW;

    After line

    public static final Kind HANDLER = new Kind("HANDLER", 7);

    add line

    public static final Kind THROW = new Kind("THROW", 8);

OK, the new pointcut pattern "throw()" is now accepted by the parser now. The result of the parsing process is an instance of KindedPointCut which will be processed later.

Part2: matching the shadows.

After a class is compiled, org.aspectj.weaver.bcel.BcelClassWeaver.weave() will be called to perform the weaving process. The entry point for our new throw pointcut is in the method

private void match(
LazyMethodGen mg,
InstructionHandle ih,
BcelShadow enclosingShadow,
List shadowAccumulator)

Add following code after the test of "i instanceof InvokeInstruction"

                else if (i instanceof ATHROW) {
match(BcelShadow.makeThrow(world, mg, ih, enclosingShadow),
shadowAccumulator);
}

This statement creates a new BcelShadow for the current ATHROW instruction, checkes whether it matches any shadowMungers, and if so, adds it to the shadowAccumulator.

Obviously, we need to add the makeThrow method to BcelShadow (weaver/src/org/aspectj/weaver/bcel/BcelShadow.java) to make this work:

        public static BcelShadow makeThrow(
BcelWorld world,
LazyMethodGen enclosingMethod,
InstructionHandle throwHandle,
BcelShadow enclosingShadow)
{
final InstructionList body = enclosingMethod.getBody();
UnresolvedType throwType = UnresolvedType.THROWABLE; //!!! not as precise as we'd like
UnresolvedType inType = enclosingMethod.getEnclosingClass().getType();
BcelShadow s =
new BcelShadow(
world,
Throw,
MemberImpl.makeThrowSignature(inType, throwType),
enclosingMethod,
enclosingShadow);
ShadowRange r = new ShadowRange(body);
r.associateWithShadow(s);
r.associateWithTargets(
Range.genStart(body, throwHandle),
Range.genEnd(body, throwHandle));
retargetAllBranches(throwHandle, r.getStart());
return s;
}

The MemberImpl.makeThrowSignature() method is implemented as following (in file weaver/src/org/aspectj/weaver/MemberImpl.java):

    public static Member makeThrowSignature(UnresolvedType inType, UnresolvedType throwType) {
return new MemberImpl(
THROW,
inType,
Modifier.STATIC,
"throw",
throwType.getSignature());
}

Finally, to make any ATHROW instruction always a matching shadow for our new pointcut pattern throw(), we need to add code to the matches(...) method of org.aspectj.weaver.patterns.SignaturePattern class (weaver/src/org/aspectj/weaver/patterns/SignaturePattern.java) after line

if (kind == Member.ADVICE) return true;

Add

if (kind == Member.THROW) return true;

Part3: weaving and code generation.

Part2 enables shadow matching of our new pointcut. The actual weaving of the advice body does not need to be changed. However, we will get null point exceptions if we try to compile our test case using current implementation, starting at line 1039, within method org.aspectj.weaver.bcel.LazyClassGen.initializeTjp(..) (in file weaver/src/org/aspectj/weaver/bcel/LazyClassGen.java), which is the initialization code for ThisJoinPoint. In particular, this method assume the signature of the BcelShadow we just created has more information, such as SignatureString, SignatureMakerName, SignatureType, etc. We need to fix this using following steps:

  • Modify weaver/src/org/aspectj/weaver/MemberImpl.java as following:
    • Add
               } else if (kind == THROW){
      this.returnType = UnresolvedType.forSignature(signature);
      this.parameterTypes = UnresolvedType.NONE;

      to the constructor

      public MemberImpl(
      Kind kind,
      UnresolvedType declaringType,
      int modifiers,
      String name,
      String signature)
    • Add
               } else if (kind == THROW){
      this.signature = returnType.getErasureSignature();
      this.declaredSignature = returnType.getSignature();

      to constructor

      public  MemberImpl(
      Kind kind,
      UnresolvedType declaringType,
      int modifiers,
      UnresolvedType returnType,
      String name,
      UnresolvedType[] parameterTypes)
    • Add to method getSignatureMakerName()
              }else if (kind == THROW){
      return "makeThrowSig";
    • Add to getSignatureType()
              } else if (kind == THROW) {
      return "org.aspectj.lang.reflect.ThrowSignature";
    • Add to getSignatureString()
              } else if (kind == THROW) {
      return getThrowSignatureString(world);
    • Add
      private String getThrowSignatureString(World world) {
      StringBuffer buf = new StringBuffer();
      buf.append('-'); // no modifiers
      buf.append('-'); // no name
      buf.append(makeString(getDeclaringType()));
      buf.append('-');
      return buf.toString();
      }
  • Create a new file ThrowSignature.java under runtime/src/org/aspectj/lang/reflect.
    /* ***************
    * Copyright (c) 2004 Contributors.
    * All rights reserved.
    * This program and the accompanying materials are made available
    * under the terms of the Common Public License v1.0
    * which accompanies this distribution and is available at
    * http://www.eclipse.org/legal/cpl-v10.html
    *
    * Contributors:
    * Jim Hugunin initial implementation
    * **************/

    package org.aspectj.lang.reflect;
    import org.aspectj.lang.Signature;

    public interface ThrowSignature extends Signature { }
  • Create a new file ThrowSignatureImpl.java under runtime/src/org/aspectj/runtime/reflect
    /* ***************
    * Copyright (c) 2004 Contributors.
    * All rights reserved.
    * This program and the accompanying materials are made available
    * under the terms of the Common Public License v1.0
    * which accompanies this distribution and is available at
    * http://www.eclipse.org/legal/cpl-v10.html
    *
    * Contributors:
    * Jim Hugunin initial implementation
    * **************/

    package org.aspectj.runtime.reflect;
    import org.aspectj.lang.reflect.ThrowSignature;

    class ThrowSignatureImpl extends SignatureImpl implements ThrowSignature {

    ThrowSignatureImpl(Class declaringType) {
    super(0, "throw", declaringType);
    }

    ThrowSignatureImpl(String stringRep) {
    super(stringRep);
    }

    protected String createToString(StringMaker sm) {
    return "throw";
    }
    }
  • Add makeThrowSig() method to org.aspectj.runtime.reflect.Factory (runtime/src/org/aspectj/runtime/reflect/Factory.java).
    public ThrowSignature makeThrowSig(String stringRep) {
    ThrowSignatureImpl ret = new ThrowSignatureImpl(stringRep);
    ret.setLookupClassLoader(lookupClassLoader);
    return ret;
    }
  • Modify weaver/src/org/aspectj/weaver/Shadow.java as following:
    • Add to getArgTypes()
      if (getKind() == Throw) return new UnresolvedType[] { getSignature().getReturnType() };
    • Add to getGenericArgTypes()
      if (getKind() == Throw) return new UnresolvedType[] { getSignature().getReturnType() };
    • Add to getArgType(..)
      if (getKind() == Throw) return getSignature().getReturnType();
    • Add to getArgCount()
      if (getKind() == Throw) return 1;
    • Add to getReturnType()
      else if (kind == Throw) return ResolvedType.VOID;

    These modifications basically try to fix the wrong assumption that any pointcut, except for the FieldGetSet ones, has method like signature. We mimic what has been done for FieldGetSet pointcuts to workaround the problems.

Done!

Congratulations! We've added a very simple pointcut pattern to aspectj. Build the aspectj using the build.xml file under the build module, and link ajdt with the new jars as described in the previous article, and then start a new Eclipse instance, copy our test case there and run it. You should be able to get the expected outputs.


Back to the top