Bug 98583 - Compile generates invalid bytecode when return inside try block in try/catch/finally
Summary: Compile generates invalid bytecode when return inside try block in try/catch/...
Status: RESOLVED DUPLICATE of bug 91499
Alias: None
Product: AspectJ
Classification: Tools
Component: Compiler (show other bugs)
Version: 1.2.1   Edit
Hardware: PC Windows XP
: P3 major (vote)
Target Milestone: ---   Edit
Assignee: Adrian Colyer CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2005-06-06 16:37 EDT by jamesiry CLA
Modified: 2005-06-06 23:05 EDT (History)
0 users

See Also:


Attachments
Test case files WinZipped (2.31 KB, application/octet-stream)
2005-06-06 16:40 EDT, jamesiry CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description jamesiry CLA 2005-06-06 16:37:11 EDT
When code has a try/catch/finally sequence and there's a return path inside the
try block and the method is instrumented with before and after advice, the iajc
ant task creates code that does not catch exceptions.  When javac is used to
compile the code and iajc just weaves the aspects it works as expected.

Below is sample code that creates this problem.  The structure is assumed as
follows, but can easily be changed by modifying build.xml

- devtools
  \
   -aspectj-1.2.1
   -junit3.8.1
- project
   \
    -build.xml
    -src
      \
       -test
         \     
          - TestAspect.aj
          - TestClass.java


Change to the directory with build.xml.  Run 'ant javac' - all unit tests pass.
 Run 'ant iajc' - 2 unit tests succeed but the third results in an error.  The
only difference between the two ant runs is that, with 'ant javac' javac does
the compile and lets iajc just weave the aspects into the resulting class files.
 With 'ant iajc' the iajc task does both the compile and weave and the resulting
bytecode doesn't work quite right.

--- build.xml -------
<project name="test" default="all">
	<property name="bin.dir" value="bin" />
	<property name="src.dir" value="src" />

	<property name="tools.dir" value="/devtools" />
	<property name="aspectj.dir" value="${tools.dir}/aspectj-1.2.1" />
	<property name="junit.dir" value="${tools.dir}/junit3.8.1"/>

	<taskdef 
		resource="org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties">
		<classpath>
			<pathelement location="${aspectj.dir}/lib/aspectjtools.jar"/>
		</classpath>
	</taskdef>		
		
	<path id="class.path">
		<pathelement location="${aspectj.dir}/lib/aspectjtools.jar" />
		<pathelement location="${aspectj.dir}/lib/aspectjrt.jar" />
		<pathelement location="${junit.dir}/junit.jar" />
	</path>

	<target name="all">
		<echo level="info">Run 'ant javac' to see the results using javac compiler
followed by iajc weaving.</echo>
		<echo level="info">Run 'ant iajc' to see the results using the iajc compiler
without javac compiling.</echo>
	</target>
		
	<target name="javac" depends="clean,javac-compile,run-unit-tests"/>
	<target name="iajc" depends="clean,iajc-compile,run-unit-tests"/>

	<target name="clean">
		<delete dir="${bin.dir}"/>
	</target>
			
	<target name="javac-compile">
		<mkdir dir="${bin.dir}" />

		<javac srcdir="${src.dir}" destdir="${bin.dir}" debug="on" deprecation="off">
			<classpath refid="class.path" />
		</javac>

		<copy todir="${bin.dir}">
			<fileset dir="${src.dir}" includes="**/*.aj" />
		</copy>
		<echo message="Weaving aspects into class files" level="info" />
		<iajc srcdir="${bin.dir}" inpath="${bin.dir}" destdir="${bin.dir}" debug="on"
deprecation="off" X="reweavable">
			<classpath refid="class.path" />
		</iajc>
		<delete>
			<fileset dir="${bin.dir}" includes="**/*.aj"/>
		</delete>
	</target>

	<target name="iajc-compile">
		<mkdir dir="${bin.dir}" />

		<echo message="Compiling and weaving class files" level="info" />
		<iajc sourceroots="${src.dir}" destdir="${bin.dir}" debug="on"
deprecation="off" X="reweavable">
			<classpath refid="class.path" />
		</iajc>
	</target>

	<target name="run-unit-tests">
		<junit printsummary="on" haltonfailure="yes">
			<formatter type="brief" usefile="false"/>
			<test name="test.TestClass"/>
			<classpath refid="class.path"/>
			<classpath path="${bin.dir}"/>				
		</junit>
	</target>
	
</project>

--- TraceAspect.aj ---
package test;

import org.aspectj.lang.Signature;

public aspect TraceAspect {
		pointcut traceMethods() : execution(public * *(..)) 
			&& within(TestClass+)
			&& !execution(*.new(..));

		before() : traceMethods() {
			Signature sig = thisJoinPoint.getSignature();
			trace("Enter", sig);
		}
		
		after() : traceMethods() {
			Signature sig = thisJoinPoint.getSignature();
			trace("Exit", sig);
		}
		
		private void trace(String action, Signature sig) {
			StringBuffer message = new StringBuffer(" Trace: ");
			message.append(action);
			message.append(": ");
			message.append(sig.toString());
			System.out.println(message.toString());
		}
		
}

--- TestClass.java ---
package test;

import junit.framework.TestCase;


public class TestClass extends TestCase {
	/* 
	 * This variable is used later for testing.  It is always
	 * false but a non-final variable is used so that the compiler can't
	 * optimize it away
	 */
	private boolean condition = false;
	
	/**
	 * Constructor required by junit
	 * 
	 * @param name
	 */
	public TestClass(String name) {
		super(name);
	}

	/**
	 * Main method used for running the junit test from the command line
	 * 
	 * @param args not used
	 */
    public static void main( String[] args ) {

        junit.swingui.TestRunner.run( TestCase.class );

    }
	
    /**
     * Test that always works.  It does not have a return in the try block
     */
	public void testExceptionHandling1() {
		boolean exceptionHandled = false;
		
		try {
			throwException();
		} catch (Exception e) {
			System.out.println("Exception caught and handled");
			exceptionHandled = true;
		}
		
		assertTrue("Exception was not handled", exceptionHandled);
	}
	
    /**
     * Test that always works.  It does not have a finally block
     */
	public void testExceptionHandling2() {
		boolean exceptionHandled = false;
		
		try {
			if (condition) {
				return;
			}			
			throwException();
		} catch (Exception e) {
			System.out.println("Exception caught and handled");
			exceptionHandled = true;
		}
		
		assertTrue("Exception was not handled", exceptionHandled);
	}
	
    /**
     * Test that does not work when iajc does the compile but does work
     * when javac does the compile followed by a weave from iajc.  It has
     * both a return in the try block and a finally block.
     */
	public void testExceptionHandling3() {
		boolean exceptionHandled = false;
		
		try {
			// this condition is always false so the return is never excecuted.
			// if this 'if' block is commented out, the test case always works
			if (condition) {
				return;
			}			
			throwException();
		} catch (Exception e) {
			System.out.println("Exception caught and handled");
			exceptionHandled = true;
		} finally {
			// this finally block does nothing important
			// if it is commented out the test case works
			System.out.println("Finally performed");
		}
		
		assertTrue("Exception was not handled", exceptionHandled);
	}
	
	/**
	 * Method used by all the tests that just throws an exception
	 * @throws Exception
	 */
	private void throwException() throws Exception {
		throw new Exception();
	}
}
Comment 1 jamesiry CLA 2005-06-06 16:40:14 EDT
Created attachment 22453 [details]
Test case files WinZipped
Comment 2 jamesiry CLA 2005-06-06 23:05:00 EDT

*** This bug has been marked as a duplicate of 91499 ***