* fix to semantics document to describe correct semantics * checkin of failing coverage test case for correct semanticstags/mostlyLastEclipse2xTree_20040112
@@ -1456,25 +1456,39 @@ | |||
</programlisting> | |||
<para> | |||
It is an error to try to put after returning advice on a join point that | |||
does not return the correct type. For example, | |||
If after returning does expose its returned object, then the | |||
type of the parameter is considered to be an | |||
<literal>instanceof</literal>-like constraint on the advice: it | |||
will run only when the return value is of the appropriate type. | |||
</para> | |||
<programlisting> | |||
after() returning (byte b): call(int String.length()) { | |||
// this is an error | |||
} | |||
</programlisting> | |||
<para> | |||
A value is of the appropriate type if it would be assignable to | |||
a variable of that type, in the Java sense. | |||
<literal>byte</literal>, <literal>short</literal>, | |||
<literal>char</literal> and <literal>int</literal> are | |||
assignable to each other, an <literal>int</literal> is | |||
assignable to a <literal>float</literal> parameter, | |||
<literal>boolean</literal> values are only assignable to | |||
<literal>boolean</literal> parameters, and reference types work | |||
by instanceof. | |||
</para> | |||
<para> | |||
There are two special cases: If the exposed value is typed to | |||
<literal>Object</literal>, then the advice is not constrained by | |||
that type: the actual return value is converted to an object | |||
type for the body of the advice: <literal>int</literal> values | |||
are represented as <literal>java.lang.Integer</literal> objects, | |||
etc, and no value (from void methods, for example) is | |||
represented as <literal>null</literal>. | |||
</para> | |||
<para> | |||
is not allowed. But if no return value is exposed, or the exposed return | |||
value is typed to <literal>Object</literal>, then it may be applied to | |||
any join point. If the exposed value is typed to | |||
<literal>Object</literal>, then the actual return value is converted to | |||
an object type for the body of the advice: <literal>int</literal> values | |||
are represented as <literal>java.lang.Integer</literal> objects, etc, and | |||
no value (from void methods, for example) is represented as | |||
<literal>null</literal>. | |||
Secondly, the <literal>null</literal> value is assignable to a | |||
parameter <literal>T</literal> if the join point | |||
<emphasis>could</emphasis> return something of type | |||
<literal>T</literal>. | |||
</para> | |||
<para> |
@@ -23,16 +23,6 @@ | |||
<run class="packageProtected.concern.BaseTarget"/> | |||
</ajc-test> | |||
<ajc-test dir="bugs" | |||
title="after returning type incompatible with join point return type" | |||
pr="42668" | |||
> | |||
<compile files="IncompatibleAfterReturningTypeCE.java"> | |||
<message kind="error" line="20"/> | |||
<message kind="error" line="22"/> | |||
</compile> | |||
</ajc-test> | |||
<ajc-test dir="bugs" | |||
pr="41888" | |||
title="call PCD fails when given subtype of defining type"> | |||
@@ -143,5 +133,13 @@ | |||
files="Main.java"/> | |||
<run class="Main"/> | |||
</ajc-test> | |||
<ajc-test dir="new" | |||
pr="42668" | |||
title="after returning with parameter: matching rules"> | |||
<compile files="AfterReturningParamMatching.java" /> | |||
<run class="AfterReturningParamMatching"/> | |||
</ajc-test> | |||
</suite> |
@@ -1,21 +0,0 @@ | |||
public class IncompatibleAfterReturningTypeCE { | |||
public static void main(String[] args) { | |||
System.setProperty("foo", ""+"".length()); | |||
} | |||
} | |||
class C { | |||
Integer getInteger() { | |||
return null; | |||
} | |||
} | |||
/** @testcase PR#42668 after returning type incompatible with join point return type */ | |||
aspect A { | |||
after () returning (Boolean b) : execution(Integer C.getInteger()) { } // CE 20 incompatible return type from join point | |||
after () returning (byte b) : call(int String.length()) {} // CE 22 incompatible return type | |||
} |
@@ -0,0 +1,10 @@ | |||
<!DOCTYPE suite SYSTEM "../tests/ajcTestSuite.dtd"> | |||
<suite> | |||
<ajc-test dir="new" pr="42668" | |||
title="after returning with parameter: matching rules"> | |||
<compile files="AfterReturningParamMatching.java" /> | |||
<run class="AfterReturningParamMatching"/> | |||
</ajc-test> | |||
</suite> |
@@ -0,0 +1,72 @@ | |||
import org.aspectj.testing.Tester; | |||
// this test verifies the matching behaivor for after returning with a typed parameter. | |||
public class AfterReturningParamMatching { | |||
public static void main(String[] args) { | |||
goBoolean(false); | |||
Tester.checkAndClearEvents(new String[] { "Object" }); | |||
goByte(1); | |||
Tester.checkAndClearEvents(new String[] { "byte", "int", "long", "Object"}); | |||
goInt(2); | |||
Tester.checkAndClearEvents(new String[] { "byte", "int", "long", "Object" }); | |||
goLong(3); | |||
Tester.checkAndClearEvents(new String[] { "byte", "int", "long", "Object" }); | |||
goObject(new Object()); | |||
Tester.checkAndClearEvents(new String[] { "Object" }); | |||
goObject(new Integer(4)); | |||
Tester.checkAndClearEvents(new String[] { "Object", "Number", "Integer" }); | |||
goObject(null); | |||
Tester.checkAndClearEvents(new String[] { "Object" }); | |||
goNumber(new Long(5)); | |||
Tester.checkAndClearEvents(new String[] { "Object", "Number" }); | |||
goNumber(new Integer(6)); | |||
Tester.checkAndClearEvents(new String[] { "Object", "Number", "Integer" }); | |||
goNumber(null); | |||
Tester.checkAndClearEvents(new String[] { "Object", "Number" }); | |||
goInteger(new Integer(7)); | |||
Tester.checkAndClearEvents(new String[] { "Object", "Number", "Integer" }); | |||
goInteger(null); | |||
Tester.checkAndClearEvents(new String[] { "Object", "Number", "Integer" }); | |||
} | |||
static boolean goBoolean(boolean b) { return b; } | |||
static byte goByte(int i) { return (byte) i; } | |||
static int goInt(int i) { return i; } | |||
static long goLong(int i) { return (long) i; } | |||
static Object goObject(Object o) { return o; } | |||
static Number goNumber(Number o) { return o; } | |||
static Integer goInteger(Integer o) { return o; } | |||
} | |||
aspect A { | |||
pointcut methodsInQuestion(): | |||
call(* goBoolean(*)) || | |||
call(* goByte(*)) || | |||
call(* goInt(*)) || | |||
call(* goLong(*)) || | |||
call(* goObject(*)) || | |||
call(* goNumber(*)) || | |||
call(* goInteger(*)); | |||
after() returning(byte b): methodsInQuestion() { Tester.event("byte"); } | |||
after() returning(int b): methodsInQuestion() { Tester.event("int"); } | |||
after() returning(long b): methodsInQuestion() { Tester.event("long"); } | |||
after() returning(Object b): methodsInQuestion() { Tester.event("Object"); } | |||
after() returning(Number b): methodsInQuestion() { Tester.event("Number"); } | |||
after() returning(Integer b): methodsInQuestion() { Tester.event("Integer"); } | |||
} |