Bug 72528 - around advice throws java.lang.VerifyError at runtime
Summary: around advice throws java.lang.VerifyError at runtime
Status: RESOLVED FIXED
Alias: None
Product: AspectJ
Classification: Tools
Component: Compiler (show other bugs)
Version: 1.2   Edit
Hardware: PC Windows XP
: P3 normal (vote)
Target Milestone: 1.2.1   Edit
Assignee: Adrian Colyer CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2004-08-24 14:21 EDT by Rohith Ajjampur CLA
Modified: 2006-05-30 08:49 EDT (History)
1 user (show)

See Also:


Attachments
Contains all the classes to reproduce this bug (1.39 KB, application/zip)
2004-08-24 14:29 EDT, Rohith Ajjampur CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Rohith Ajjampur CLA 2004-08-24 14:21:34 EDT
I have an aspect that captures around() a pcd and returns an Object[], though
the actual methods being instrumented might return any valid POJO array, i
understand that AspectJ will take care of casting at assignment.

I expected the following code to work properly, but at runtime i get a
java.lang.VerifyError as shown below:

F:\wd\Hello>ajbrowser HelloWorld.lst
java.lang.VerifyError: (class: de/rohith/PrinterWorld, method: returnArrayWithCl
oning signature: ()[Ljava/lang/Integer;) Incompatible argument to function
        at de.rohith.HelloWorld.main(HelloWorld.java:18)
Exception in thread "main"

I suspect the compiler fails to notice the return types of the methods being
caught at compile time.

I have tested this code with both ajbrowser as well as AJDT, both result in the
same error output.

-----------HelloWorldAspect.java---------------
package de.rohith;

import java.lang.Object;

public aspect HelloWorldAspect {
    
	private int callDepth = -1;

    public HelloWorldAspect() {
    }
    
    pointcut hello(): !within(HelloWorldAspect);
    
    pointcut method(): execution(public (*[]) de..*(..));
    
    pointcut cloning(): call(* java.lang.Object.clone());

    declare warning: method() && hello(): "*[] returning method called" ;
    
    Object[] around(): cflow(method()) && cloning() && hello() {
    	print("", thisEnclosingJoinPointStaticPart);
    	Object[] ret = proceed(); 
    	return (Object[])ret.clone();
    }

    private void print(String prefix, Object message) {
        for (int i = 0, spaces = callDepth * 2; i < spaces; i++) {
            System.out.print(" ");
        }
        System.out.println(prefix + message);
    }

}


-----------PrinterWorld.java------------
package de.rohith;
public class PrinterWorld {
	private Integer[] intArray = new Integer[2];
	public PrinterWorld() {
		
	}
    public void print() {
        System.out.println("Hello World!"); 
    }
    
    public Integer returnInt() {
    	return new Integer(3);
    }
    
    public Integer[] returnArrayWithCloning() {
    	for (int i = 0; i < intArray.length; i++) {
			intArray[i] = new Integer(i++);
		}
    	return (Integer[])intArray.clone();
    }
    
    public Integer[] returnArrayWithoutCloning() {
    	return intArray;
    }
}

-----------HelloWorld.java------------
package de.rohith;

public class HelloWorld {

    public static void main(String[] args) {
        PrinterWorld p = new PrinterWorld();
        p.print(); 
        Integer i = p.returnInt();
        Integer[] intArray = p.returnArrayWithCloning();
        Integer[] array2 = p.returnArrayWithoutCloning();
    }
}
Comment 1 Rohith Ajjampur CLA 2004-08-24 14:29:09 EDT
Created attachment 14155 [details]
Contains all the classes to reproduce this bug
Comment 2 Adrian Colyer CLA 2004-08-24 15:49:57 EDT
This is almost certainly related to the bug in the handling of [] in type 
patterns, as reported in bug 72531. Any verify error is serious and we will fix 
this in the 1.2.1 release. I'll post to this bug report as soon as a fix is 
available for download from the AspectJ download page.
Comment 3 Adrian Colyer CLA 2004-08-25 13:15:16 EDT
This turns out to be a really interesting bug. The problem is that in 
'marshalling' the parameters to call the around body, ajc gets confused and 
thinks that the target of the call (the intArray) is a Foo, and generates a line 
of code of the form:

Foo foo = intArray;

this is what causes the verify error.

This is all wrapped up in the special treatment of array types and of the clone 
method. Note that clone() is redefined for an array type, to be public (you 
can't just call clone on an arbitrary Object), and to return a shallow copy of 
the array. But where does this special redefinition of the clone() method happen 
(in which type??). It's this unique combination of factors - a redefined method 
on a 'special' type that is causing the confusion. I'm off to spend some time 
reading in the JVM and JLS specs to see what they have to say on the topic 
before deciding on the right fix.... 
Comment 4 Adrian Colyer CLA 2004-08-25 13:22:44 EDT
JSL 10.7.....
Comment 5 Erik Hilsdale CLA 2004-08-25 13:28:50 EDT
this is not the first time those magical JLS 10.7 array members cause trouble.
We're already special casing the length field somewhere (actually, it's 
more special since it is represented in bytecode by the ARRAYLENGTH bytecode
and not by a field at all).  
Comment 6 Erik Hilsdale CLA 2004-08-25 13:34:25 EDT
related to Bug 67665 
Comment 7 Rohith Ajjampur CLA 2004-08-26 06:06:22 EDT
I understand from JLS 10.7 that it is unclear what type represents an array type
at runtime, but if this bug is related to Bug 67665 then i think the basic
assumption that AspectJ supports all the Java types fails. According to JLS
10.1, an array is a Java type. Another reason i would assume that arrays are
supported as types is in "AspectJ 1.1 quick refernce" among TypePat it clearly
mentions that it accepts an array type.
Comment 8 Erik Hilsdale CLA 2004-08-26 10:24:02 EDT
when I said it was related to Bug 67665, I meant that I raised
_implementation_ issues of treatment of 
.length and .clone() there, and that there are some cleanup issues with
array syntax there as well.  

I don't think 10.7 is at all unclear about what types represent at runtime.
Array types are types.  They have some methods and one field.  We need to 
capture calls to those methods (and accesses and assignments to that field)
as join points.  Nobody is talking about language changes here.  We're
talking about fixing a bug, and I noted that array treatment in general
is a place where we've had bugs before.
Comment 9 Andrew Clement CLA 2004-09-03 09:51:14 EDT
Ok - fix checked in.  What I've done is what we thought we'd have to do.  When
we grab the target for a method-call shadow and we recognize it as being one of
these nasty clone calls on Object, I have a quick look before the shadow to see
what the 'thing' is on the stack.  There are a number of possibilities (that I
have captured in the testcase), basically we might encounter:

- a load instruction, in which case we ask the instruction what type it is
working with (querying the local variable tag) and this helps us work out that
it is an array.

- a field access instruction, in which case we ask the field what type it is.

- a anewarray instruction, in which case we *know* its an array (although why
anyone would write '(Integer[])new Integer[5].clone()' I dont know!)

- a multianewarray instruction, in which case *know* its an array.

I've put a lot of guards in so that we don't do this extra processing unless it
meets the criteria of being this nasty case.  It will also blow up if it
encounters an instruction other than those expected when trying to determine the
real type of the target - if we didn't blow up then we would fail later at
verify time.

its a bit messy but I think it is quite a robust solution.
Comment 10 Erik Hilsdale CLA 2004-09-03 11:41:46 EDT
I can't look at the code right now, but two reactions to the solution description:

* the localVariable tag won't work at all if someone has stripped off debugging
information.  Whatever solution we have should perhaps work better if we have
debugging info, but better not generate a bad classfile if we don't.

* even if we're lucky enough to have debugging information, ?: can surprise you

  ((testA ? exp0 : exp1) (testB ? exp2 : exp3) (testC ? exp4 : exp5)).clone()
Comment 11 Andrew Clement CLA 2004-09-03 12:24:57 EDT
Hi Erik.  
Yes, I hadn't thought about them stripping that debug info off.  
I just couldn't think of a nicer solution to this problem (unless we say
compiler restriction).  I believe that at least now we will fail at compile time
rather than runtime.  Did you have a nicer fix in mind for this?  My first go
was to try and generate the code that looked like this:

public static final Object clone_aroundBody0(X x, Object y) {
  if (y instanceof Object[]) {
    return ((Object[])y).clone();
  } else {
    return y.clone();
  }
}

but that still fails verification because 'return y.clone()' exists in the bytecode.

I then thought well - is the only time we will ever try and create a method like
this:

public static final Object clone_aroundBody0(Object y) {
  return y.clone();
}

when the clone was originally on an array type.  If its true then in this case
we could always produce:

public static final Object[] clone_aroundBody0(Object[] y) {
  return (Object[])y.clone();
}

but I wasnt sure if that was one assumption too far?
Comment 12 Erik Hilsdale CLA 2004-09-09 11:26:45 EDT
Yeah, I see your point.  The proposed solution we had was worse, too, since

  ((Object) new int[3]) instanceof Object[]

is false.  I'm still worried about the fragility of this solution without
debugging info, but there it is.

This is exactly the case where a separate set of people working on the AspectJ
semantics might come up with a better solution that we could then steal if the
current one turns out to be too fragile in practice *grin*.

We should probably close this as fixed.
Comment 13 Andrew Clement CLA 2004-09-09 12:31:02 EDT
I'm going on holiday at the weekend for two weeks and want to write down my last
minute thoughts on this bug so I don't forget them whilst I'm in the Maldives. 
I may not get to finishing this bug off before I go.

The oxford guys didn't seem to think a 1.4 compiler should still be producing
the 'Object.clone()' bytecode, they seemed to think the one they are using is
producing the right '<arraytype>.clone()' bytecode.  I need to check if this is
really the case.  It could be a bug in the Eclipse compiler so I need to check
it on SUN and IBM 1.4 compilers. (And check the polyglot compiler they use?)

I do think my current solution will fail at compile time if we can't determine
the actual type on which clone() is called - but if there is no debug
information I don't think it will fail in a nice way - it will fail with an NPE
as I'm assuming the local variable tag is found.  It should gracefully fail if
the tag is missing.
Comment 14 Andrew Clement CLA 2006-02-15 03:52:10 EST
not useful targetting 1.2.1
Comment 15 Andrew Clement CLA 2006-05-30 08:49:20 EDT
not planning to do any more with this for now... basic verifyerror was fixed long ago.