Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
RE: [aspectj-users] args() and @args() for indeterminite lists ofparameters?

Hi Howard,

It's good to see you writing an interesting aspect like this. A few quick
comments and answers follow. I'd be interested to see what you end up
with...

Most importantly, note that args(.., argumentValue) will only match the last
argument to a method. You would like to use args(.., argumentValue, ..) to
match an arbitrary position. However, the AspectJ compiler doesn't support
this form, because of questions about ambiguity since, obviously it can
match at any position. In this case, you would want it to match for each
argument, but currently that's not supported.

For example, consider

public aspect Two {
    pointcut endArgs(Object argValue) : args(.., argValue) && execution(*
*(..));
    before(Object argValue) : endArgs(argValue) {
        System.out.println(thisJoinPoint+(argValue == null ? "object arg is
null" : "ok"));
    }

    before(Object argValue) : endArgs(argValue) && !args(.., int) {
        System.out.println("  with object argument");
    }

    static void unary(Object x) {}
    static void primitive(int y) {}
    static void binary(Object x, String y) {}     

    public static void main(String args[]) {
        unary(new Integer(1));
        primitive(1);
        binary("x", null);
        binary(null, "x");
    }
}

C:\devel\scratch\args>java Two
execution(void Two.main(String[]))ok
  with object argument
execution(void Two.unary(Object))ok
  with object argument
execution(void Two.primitive(int))ok
execution(void Two.binary(Object, String))object arg is null
  with object argument
execution(void Two.binary(Object, String))ok
  with object argument        

The most general alternative would be to use thisJoinPoint.getArgs(), which
would give you an array of Object, so you could check each one for null.
That would also let you determine the parameter index. To exclude parameters
annotated with @SuppressNullCheck, you could use Java reflection based on
the method, which you can get from the join point signature.

To be more efficient and more static you could write special cases for the
first N arguments. You could even exclude primitives, though only by
enumeration, e.g.,

pointcut firstObjectArgument(Object arg) : 
   args(arg, ..) && !@args(SupressNullCheck, ...) && 
   !args(int, ..) && !args(boolean, ..) && ...;
pointcut secondObjectArgument(Object arg) : 
   args(*, arg, ..) && !@args(*, SuppressNullCheck, ..) &&
   !args(*, int, ..) && !args(*, boolean, ..) && ...;
...
pointcut overFiveArguments(Object arg) : 
   args(*, *, *, *, *, *, ..); 
// the advice for this one uses thisJoinPoint.getArgs() as described before

The !@args(...) form prevents matching parameters with the annotation

To get the parameter index in this case, you'd need one piece of advice for
each pointcut you used.

You also shouldn't need to exclude no-parameter methods or constructors,
since they won't match an args pointcut with any number of parameters.

-----Original Message-----
From: aspectj-users-bounces@xxxxxxxxxxx
[mailto:aspectj-users-bounces@xxxxxxxxxxx] On Behalf Of Howard Lewis Ship
Sent: Saturday, April 22, 2006 11:22 PM
To: aspectj-users@xxxxxxxxxxx
Subject: [aspectj-users] args() and @args() for indeterminite lists
ofparameters?

I'm continuing to play around with some design-by-contract ideas.

I've been implementing an abstract that catches null parameters and
throws IllegalArgumentException.

Been learning a lot, here's what I've got so far:

@SuppressNullCheck
public abstract aspect AbstractCatchNullParametersAspect
{
    /**
     * Overridden in concrete aspects to identify which classes are
affected (typically, all within
     * a particular package).
     */
    abstract pointcut targetClasses();

    pointcut typeNotMarkedAsSuppressed() :
        !within(@SuppressNullCheck Object+);

    /** Look for methods and constructors that do NOT have the
SuppressNullCheck annotation. */
    pointcut codeNotMarkedAsSuppressed() :
        execution(!@SuppressNullCheck !private * *(..)) ||
        execution(!@SuppressNullCheck !private new(..));

    /**
     * Exclude methods (and constructors) with no parameters. Also,
exclude equals() since it has to
     * accept null (and we don't want to force you to put the
SupportNullCheck annotation on every
     * class that overrides equals().
     */
    pointcut excludedMethods() :
        execution(* *()) ||
        execution(new ()) ||
        execution(boolean equals(Object));

    /**
     * Because this aspect can apply so widely, we explicitly exclude
the aspect from advising
     * itself.
     */
    pointcut methodsToCheck()  :
        targetClasses() &&
        typeNotMarkedAsSuppressed() &&
        codeNotMarkedAsSuppressed() &&
        !excludedMethods();

    pointcut parametersToCheck(Object argumentValue) :
        args(.., argumentValue);

    /**
     * Here's where the real work gets done. I think there may be a
way to do this more efficiently
     * where much more static logic is injected, triggered by arguments.
     */
    before(Object argumentValue) : methodsToCheck() &&
parametersToCheck(argumentValue)
    {
        if (argumentValue == null)
        {
            // The method signature could provide the parameterName
(which would be awesome),
            // but we need to know the parameter index.

            String message = String.format(
                    "Parameter passed to method %s (at %s) was null.",
                    thisJoinPoint.getSignature().toString(),
                    thisJoinPoint.getSourceLocation());

            throw new IllegalArgumentException(message);

        }
    }
}

It works quite well (though I haven't tried to, for example, check the
performance hit).

A couple of things I'd like:

1) According to AJDT, methods that take primitive parameters are still
being advised.

2) I'd love it if there was some way to determine the parameter index,
because then my exception message could include the parameter name
(i.e., by casting to MethodSignature)

3) I'd like to be able to skip the check for arguments what are
annotated with an @AllowNull annotation,  but I haven't quite seen how
I can coordinate the check (given that I'm doing these arbitrary
method matches).

And ideas?  It's been fun.

--
Howard M. Lewis Ship
Independent J2EE / Open-Source Java Consultant
Creator, Jakarta Tapestry
Creator, Jakarta HiveMind

Professional Tapestry training, mentoring, support
and project work.  http://howardlewisship.com
_______________________________________________
aspectj-users mailing list
aspectj-users@xxxxxxxxxxx
https://dev.eclipse.org/mailman/listinfo/aspectj-users




Back to the top