Bug 62642 - proper handling of ExceptionInIntializer inside <clinit> in presence of after throwing advice
Summary: proper handling of ExceptionInIntializer inside <clinit> in presence of after...
Status: RESOLVED FIXED
Alias: None
Product: AspectJ
Classification: Tools
Component: Compiler (show other bugs)
Version: 1.2   Edit
Hardware: All All
: P3 normal (vote)
Target Milestone: 1.2.1   Edit
Assignee: Adrian Colyer CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2004-05-18 10:23 EDT by Laurie Hendren CLA
Modified: 2004-10-21 04:32 EDT (History)
0 users

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Laurie Hendren CLA 2004-05-18 10:23:19 EDT
When a <clinit> of a class contains after throwing advice that may catch
ExceptionInInitializer errors due to an embedded advice aspectOf() that
fails,  the ExceptionInIntializer error does not get properly reported.

For the example below:

public class Main {
  static int x = 13;
  int y;

  public Main() {
     y= 14;
  }

  public static void main(String args[])
    { Main m = new Main();
      m.y = 3;
      System.out.println("hi");
    }
}


aspect Aspect {

  before () :  within(*)
    { System.out.println("BEFORE " + thisJoinPointStaticPart.getKind() +
                         " at " + thisJoinPointStaticPart.getSourceLocation());
    }

  after ()  : within(*)
    { System.out.println("AFTER " + thisJoinPointStaticPart.getKind() +
                         " at " + thisJoinPointStaticPart.getSourceLocation());
    }
}


....  

when compiled with ajc, and then run,  the following exception is given,

Exception in thread "main" java.lang.NoClassDefFoundError
        at Main.<clinit>(Main.java:1)

when really one wants the following:

Exception in thread "main" java.lang.ExceptionInInitializerError
        at Main. ...
Caused by: org.aspectj.lang.NoAspectBoundException: Aspect
        at Aspect.aspectOf ..
        

-------------

Here is a fix ... I give the decompiled <clinit> for Main.java and indicate with
comments,
  look for ***LJH ****,  what can be woven to fix it.   Basically, the catch
block for after throwing,  when in <clinit>,  should first check if the
caught exception if a ExceptionInInitializerError, and if so just throw it.

----------- static
    {
        org.aspectj.runtime.reflect.Factory r1;
        java.lang.Throwable r2, r31;
        int $i0;

        r1 = new Factory("Main.java", Class.forName("Main"));
        ajc$tjp_0 = r1.makeSJP("field-set", r1.makeFieldSig("8-x-Main-int-"), 2);
        ajc$tjp_1 = r1.makeSJP("staticinitialization",
r1.makeInitializerSig("8--Main-"), 2);
        ajc$tjp_10 = r1.makeSJP("preinitialization",
r1.makeConstructorSig("1--Main----"), 5);
        ajc$tjp_2 = r1.makeSJP("field-set", r1.makeFieldSig("0-y-Main-int-"), 6);
        ajc$tjp_3 = r1.makeSJP("constructor-execution",
r1.makeConstructorSig("1--Main----"), 6);
        ajc$tjp_4 = r1.makeSJP("constructor-call",
r1.makeConstructorSig("1--Main----"), 10);
        ajc$tjp_5 = r1.makeSJP("field-set", r1.makeFieldSig("0-y-Main-int-"), 11);
        ajc$tjp_6 = r1.makeSJP("field-get",
r1.makeFieldSig("19-out-java.lang.System-java.io.PrintStream-"), 12);
        ajc$tjp_7 = r1.makeSJP("method-call",
r1.makeMethodSig("1-println-java.io.PrintStream-java.lang.String:-arg0:--void-"),
12);
        ajc$tjp_8 = r1.makeSJP("method-execution",
r1.makeMethodSig("9-main-Main-[Ljava.lang.String;:-args:--void-"), 10);
        ajc$tjp_9 = r1.makeSJP("initialization",
r1.makeConstructorSig("1--Main----"), 6);

 try
        {
            Aspect.aspectOf().ajc$before$Aspect$1$36f01b1c(ajc$tjp_1);
            $i0 = 13;

            try
            {
                Aspect.aspectOf().ajc$before$Aspect$1$36f01b1c(ajc$tjp_0);
                x = $i0;
            }
            catch (Throwable $r30)
            {
                /*** LJH insert here *****/
                if ($r30 instanceof ExceptionInIntializerError) //***
                  throw($r30); //***
                /***********/
                r31 = $r30;
                Aspect.aspectOf().ajc$after$Aspect$2$36f01b1c(ajc$tjp_0);
                throw r31;
            }

            Aspect.aspectOf().ajc$after$Aspect$2$36f01b1c(ajc$tjp_0);
        }
        catch (Throwable $r36)
        {
           /*** LJH insert here *****/
           if ($r36 instanceof ExceptionInIntializerError) //***
           throw($r36); //***
           /***********/
            r2 = $r36;
            Aspect.aspectOf().ajc$after$Aspect$2$36f01b1c(ajc$tjp_1);
            throw r2;
        }

        Aspect.aspectOf().ajc$after$Aspect$2$36f01b1c(ajc$tjp_1);
    }
Comment 1 Adrian Colyer CLA 2004-08-09 15:25:38 EDT
marked as target 1.2.1
Comment 2 Andrew Clement CLA 2004-08-17 10:07:17 EDT
argh!  bcel is a nightmare.  It has taken me ages to program BCEL to generate:

 if ($r36 instanceof ExceptionInInitializerError) 
   throw($r36);

This is the final incantation for BCEL:

        if (this.getEnclosingMethod().getName().equals("<clinit>")) {
            ResolvedTypeX eiieType =
              world.resolve("java.lang.ExceptionInInitializerError");
            ObjectType eiieBcelType = 
              (ObjectType)BcelWorld.makeBcelType(eiieType);
            InstructionList ih = new InstructionList(InstructionConstants.NOP);
            handler.append(exceptionVar.createLoad(fact));
            handler.append(fact.createInstanceOf(eiieBcelType));
            BranchInstruction bi = 
                InstructionFactory.createBranchInstruction(
                Constants.IFEQ,ih.getStart());
            handler.append(bi);
            handler.append(exceptionVar.createLoad(fact));
            handler.append(fact.createCheckCast(eiieBcelType));
            handler.append(InstructionConstants.ATHROW);
            handler.append(ih);
        }

I put that in the BcelShadow.weaveAfterThrowing() method.  You can see it does a
check to determine if it is in the <clinit> before generating this extra check.

Here is the extra bytecode that appears in the method:

447: aload_1
448: instanceof      #101; //class ExceptionInInitializerError
451: ifeq    459
454: aload_1
455: checkcast       #101; //class ExceptionInInitializerError
458: athrow
459: nop

Yes, there is one extra NOP but I don't want to fight with BCEL to remove it.

I've put Lauries test program into the test suite to verify it behaves and we
get the ExceptionInInitializerError rather than the NoClassDefFoundError.

Thanks Laurie for describing the right fix :)

fix checked in, waiting for build.
Comment 3 Andrew Clement CLA 2004-08-18 08:09:34 EDT
Fix available:

BUILD COMPLETE -  build.344
Date of build: 08/18/2004 11:27:31
Time to build: 92 minutes 31 seconds
Last changed: 08/18/2004 10:55:59
Latest good AspectJ jar available at:
download.eclipse.org/technology/ajdt/dev/aspectj-DEVELOPMENT.jar
Comment 4 Adrian Colyer CLA 2004-10-21 04:32:59 EDT
Fix released as part of AspectJ 1.2.1