]> source.dussan.org Git - aspectj.git/commitdiff
Fix 547808: npe regression for multi @Around
authorAndy Clement <aclement@pivotal.io>
Mon, 3 Jun 2019 17:06:59 +0000 (10:06 -0700)
committerAndy Clement <aclement@pivotal.io>
Mon, 3 Jun 2019 17:06:59 +0000 (10:06 -0700)
tests/bugs195/333274/Code.java [new file with mode: 0644]
tests/src/test/java/org/aspectj/systemtest/AllTests19.java
tests/src/test/java/org/aspectj/systemtest/ajc195/Ajc195Tests.java [new file with mode: 0644]
tests/src/test/java/org/aspectj/systemtest/ajc195/AllTestsAspectJ195.java [new file with mode: 0644]
tests/src/test/resources/org/aspectj/systemtest/ajc195/ajc195.xml [new file with mode: 0644]
weaver/src/main/java/org/aspectj/weaver/bcel/BcelShadow.java

diff --git a/tests/bugs195/333274/Code.java b/tests/bugs195/333274/Code.java
new file mode 100644 (file)
index 0000000..d456c0a
--- /dev/null
@@ -0,0 +1,39 @@
+import org.aspectj.lang.annotation.*;
+import org.aspectj.lang.*;
+
+public class Code {
+  public static void main(String []argv) {
+try {
+    foo();
+} catch (Throwable t) {
+   System.out.println("Caught "+t);
+}
+  }
+
+  public static void foo() {
+    print1("abc");
+    print2("def");
+    print1("ghi");
+  }
+
+  public static void print1(String s) {
+    System.out.println(s);
+  }  
+
+  public static void print2(String s) {
+    System.out.println(s);
+  }  
+}
+
+@Aspect
+class Azpect {
+  @Around("call(* print2(..))")
+  public Object one(ProceedingJoinPoint pjp) {
+    return pjp.proceed();
+  }
+  @Around("call(* print2(..))")
+  public Object two(ProceedingJoinPoint pjp) {
+    //return pjp.proceed();
+    throw new IllegalStateException("");
+  }
+}
index c2c798c325eaae54b5d45858933fcb2a5f72469d..eeff00b318351b8ef5569ab6e9e7263afac86bd8 100644 (file)
@@ -1,12 +1,9 @@
 /*******************************************************************************
- * Copyright (c) 2013, 2014 Contributors 
+ * Copyright (c) 2013, 2019 Contributors
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- *    Andy Clement - initial API and implementation
  *******************************************************************************/
 package org.aspectj.systemtest;
 
@@ -14,19 +11,25 @@ import org.aspectj.systemtest.ajc190.AllTestsAspectJ190;
 import org.aspectj.systemtest.ajc191.AllTestsAspectJ191;
 import org.aspectj.systemtest.ajc192.AllTestsAspectJ192;
 import org.aspectj.systemtest.ajc193.AllTestsAspectJ193;
+import org.aspectj.systemtest.ajc195.AllTestsAspectJ195;
 
 import junit.framework.Test;
 import junit.framework.TestSuite;
 
+/**
+ * @author Andy Clement
+ */
 public class AllTests19 {
 
        public static Test suite() {
                TestSuite suite = new TestSuite("AspectJ System Test Suite - 1.9");
-               // $JUnit-BEGIN$ 
-               suite.addTest(AllTestsAspectJ190.suite()); 
-               suite.addTest(AllTestsAspectJ191.suite()); 
-               suite.addTest(AllTestsAspectJ192.suite()); 
-               suite.addTest(AllTestsAspectJ193.suite()); 
+               // $JUnit-BEGIN$
+               suite.addTest(AllTestsAspectJ190.suite());
+               suite.addTest(AllTestsAspectJ191.suite());
+               suite.addTest(AllTestsAspectJ192.suite());
+               suite.addTest(AllTestsAspectJ193.suite());
+               // there were no new tests for 1.9.4
+               suite.addTest(AllTestsAspectJ195.suite());
                suite.addTest(AllTests18.suite());
                // $JUnit-END$
                return suite;
diff --git a/tests/src/test/java/org/aspectj/systemtest/ajc195/Ajc195Tests.java b/tests/src/test/java/org/aspectj/systemtest/ajc195/Ajc195Tests.java
new file mode 100644 (file)
index 0000000..7c6034a
--- /dev/null
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.aspectj.systemtest.ajc195;
+
+import org.aspectj.testing.XMLBasedAjcTestCase;
+
+import junit.framework.Test;
+
+/**
+ * @author Andy Clement
+ */
+public class Ajc195Tests extends XMLBasedAjcTestCase {
+
+       public void testFinallyBlocksAndUnlinkingAndExceptions() {
+               runTest("around finally blocks and unlinking");
+       }
+
+       // ---
+
+       public static Test suite() {
+               return XMLBasedAjcTestCase.loadSuite(Ajc195Tests.class);
+       }
+
+       @Override
+       protected java.net.URL getSpecFile() {
+               return getClassResource("ajc195.xml");
+       }
+
+}
diff --git a/tests/src/test/java/org/aspectj/systemtest/ajc195/AllTestsAspectJ195.java b/tests/src/test/java/org/aspectj/systemtest/ajc195/AllTestsAspectJ195.java
new file mode 100644 (file)
index 0000000..a7eb660
--- /dev/null
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2018-2019 Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Andy Clement - initial API and implementation
+ *******************************************************************************/
+package org.aspectj.systemtest.ajc195;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AllTestsAspectJ195 {
+
+       public static Test suite() {
+               TestSuite suite = new TestSuite("AspectJ 1.9.4 tests");
+               suite.addTest(Ajc195Tests.suite());
+               return suite;
+       }
+}
diff --git a/tests/src/test/resources/org/aspectj/systemtest/ajc195/ajc195.xml b/tests/src/test/resources/org/aspectj/systemtest/ajc195/ajc195.xml
new file mode 100644 (file)
index 0000000..10d1a01
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE suite SYSTEM "../tests/ajcTestSuite.dtd"[]>
+
+<suite>
+
+    <ajc-test dir="bugs195/333274" vm="1.8" title="around finally blocks and unlinking">
+        <compile files="Code.java" options="-showWeaveInfo -1.8 -XnoInline">
+                       <message kind="weave" text="Join point 'method-call(void Code.print2(java.lang.String))' in Type 'Code' (Code.java:15) advised by around advice from 'Azpect' (Code.java:35)"/>
+                       <message kind="weave" text="Join point 'method-call(void Code.print2(java.lang.String))' in Type 'Code' (Code.java:15) advised by around advice from 'Azpect' (Code.java:31)"/>
+        </compile>
+       
+        <run class="Code">
+               <stdout>
+               <line text="abc"/>
+               <line text="Caught java.lang.IllegalStateException"/>
+               </stdout>
+        </run>
+    </ajc-test>
+
+</suite>
index 6d0cb4f601246e6a6c7efe258ceee924b8f5db33..7e1e0bfc4ef31800044b0fc0cfedfb8428c8a49d 100644 (file)
@@ -73,52 +73,52 @@ import org.aspectj.weaver.patterns.ThisOrTargetPointcut;
 
 /*
  * Some fun implementation stuff:
- * 
+ *
  *   * expressionKind advice is non-execution advice
- *     * may have a target. 
- *     * if the body is extracted, it will be extracted into 
- *       a static method.  The first argument to the static 
+ *     * may have a target.
+ *     * if the body is extracted, it will be extracted into
+ *       a static method.  The first argument to the static
  *       method is the target
  *     * advice may expose a this object, but that's the advice's
  *       consideration, not ours.  This object will NOT be cached in another
  *       local, but will always come from frame zero.
- * 
- *   * non-expressionKind advice is execution advice 
+ *
+ *   * non-expressionKind advice is execution advice
  *     * may have a this.
  *     * target is same as this, and is exposed that way to advice
  *       (i.e., target will not be cached, will always come from frame zero)
  *     * if the body is extracted, it will be extracted into a method
- *       with same static/dynamic modifier as enclosing method.  If non-static, 
- *       target of callback call will be this. 
+ *       with same static/dynamic modifier as enclosing method.  If non-static,
+ *       target of callback call will be this.
  *
- *   * because of these two facts, the setup of the actual arguments (including 
+ *   * because of these two facts, the setup of the actual arguments (including
  *     possible target) callback method is the same for both kinds of advice:
  *     push the targetVar, if it exists (it will not exist for advice on static
- *     things), then push all the argVars.  
- * 
+ *     things), then push all the argVars.
+ *
  * Protected things:
  *
  *   * the above is sufficient for non-expressionKind advice for protected things,
  *     since the target will always be this.
- * 
+ *
  *   * For expressionKind things, we have to modify the signature of the callback
  *     method slightly.  For non-static expressionKind things, we modify
  *     the first argument of the callback method NOT to be the type specified
  *     by the method/field signature (the owner), but rather we type it to
  *     the currentlyEnclosing type. We are guaranteed this will be fine,
  *     since the verifier verifies that the target is a subtype of the currently
- *     enclosingType. 
- * 
+ *     enclosingType.
+ *
  * Worries:
- * 
+ *
  *    * ConstructorCalls will be weirder than all of these, since they
  *      supposedly don't have a target (according to AspectJ), but they clearly
  *      do have a target of sorts, just one that needs to be pushed on the stack,
  *      dupped, and not touched otherwise until the constructor runs.
- *  
+ *
  * @author Jim Hugunin
  * @author Erik Hilsdale
- * 
+ *
  */
 
 public class BcelShadow extends Shadow {
@@ -210,7 +210,7 @@ public class BcelShadow extends Shadow {
         * The new/dup (or new/dup_x1/swap) are removed and will be readded later (after the advice call) by the caller of this method.
         * The groovy compiler produces unusual code where the new/dup isn't visible (when making a this() call from an existing ctor),
         * an aload_0 is used to load the uninitialized object (as an example see the ctors in grails.util.BuildSettings).
-        * 
+        *
         * @return true if managed to remove them
         */
        private boolean deleteNewAndDup() {
@@ -630,7 +630,7 @@ public class BcelShadow extends Shadow {
        /**
         * Create an initialization join point associated with a constructor, but not with any body of code yet. If this is actually
         * matched, it's range will be set when we inline self constructors.
-        * 
+        *
         * @param constructor The constructor starting this initialization.
         */
        public static BcelShadow makeUnfinishedInitialization(BcelWorld world, LazyMethodGen constructor) {
@@ -882,8 +882,8 @@ public class BcelShadow extends Shadow {
        private Map<ResolvedType, AnnotationAccessVar> withinAnnotationVars = null;
        private Map<ResolvedType, AnnotationAccessVar> withincodeAnnotationVars = null;
        private boolean allArgVarsInitialized = false;
-       
-       // If in annotation style and the relevant advice is using PJP then this will 
+
+       // If in annotation style and the relevant advice is using PJP then this will
        // be set to true when the closure variable is initialized - if it gets set
        // (which means link() has been called) then we will need to call unlink()
        // after the code has been run.
@@ -1143,7 +1143,7 @@ public class BcelShadow extends Shadow {
 
        /**
         * Get the Var for the xxxxJpStaticPart, xxx = this or enclosing
-        * 
+        *
         * @param isEnclosingJp true to have the enclosingJpStaticPart
         * @return
         */
@@ -1165,7 +1165,7 @@ public class BcelShadow extends Shadow {
 
        /**
         * Get the Var for the enclosingJpStaticPart
-        * 
+        *
         * @return
         */
        public BcelVar getThisEnclosingJoinPointStaticPartBcelVar() {
@@ -1702,11 +1702,11 @@ public class BcelShadow extends Shadow {
        /**
         * The basic strategy here is to add a set of instructions at the end of the shadow range that dispatch the advice, and then
         * return whatever the shadow was going to return anyway.
-        * 
+        *
         * To achieve this, we note all the return statements in the advice, and replace them with code that: 1) stores the return value
         * on top of the stack in a temp var 2) jumps to the start of our advice block 3) restores the return value at the end of the
         * advice block before ultimately returning
-        * 
+        *
         * We also need to bind the return value into a returning parameter, if the advice specified one.
         */
        public void weaveAfterReturning(BcelAdvice munger) {
@@ -1759,11 +1759,11 @@ public class BcelShadow extends Shadow {
         * making this the ultimate return. If the shadow has a non-void return type, we also create a temporary variable to hold the
         * return value, and load the value from this var before returning (see pr148007 for why we do this - it works around a JRockit
         * bug, and is also closer to what javac generates)
-        * 
+        *
         * Sometimes the 'last return' isnt the right one - some rogue code can include the real return from the body of a subroutine
         * that exists at the end of the method. In this case the last return is RETURN but that may not be correct for a method with a
         * non-void return type... pr151673
-        * 
+        *
         * @param returns list of all the return instructions in the shadow
         * @param returnInstructions instruction list into which the return instructions should be generated
         * @return the variable holding the return value, if needed
@@ -1802,7 +1802,7 @@ public class BcelShadow extends Shadow {
 
        /**
         * Get the list of instructions used to dispatch to the after advice
-        * 
+        *
         * @param munger
         * @param firstInstructionInReturnSequence
         * @return
@@ -1821,7 +1821,7 @@ public class BcelShadow extends Shadow {
 
        /**
         * If the after() returning(Foo f) form is used, bind the return value to the parameter. If the shadow returns void, bind null.
-        * 
+        *
         * @param advice
         * @return
         */
@@ -1842,7 +1842,7 @@ public class BcelShadow extends Shadow {
 
        /**
         * Helper method for weaveAfterReturning
-        * 
+        *
         * Each return instruction in the method body is retargeted by calling this method. The return instruction is replaced by up to
         * three instructions: 1) if the shadow returns a value, and that value is bound to an after returning parameter, then we DUP
         * the return value on the top of the stack 2) if the shadow returns a value, we store it in the returnValueVar (it will be
@@ -2009,13 +2009,13 @@ public class BcelShadow extends Shadow {
                }
                ResolvedType aspectRT = munger.getConcreteAspect();
                BcelWorld.getBcelObjectType(aspectRT);
-               
+
                // Although matched, if the visibility rules prevent the aspect from seeing this type, don't
                // insert any code (easier to do it here than try to affect the matching logic, unfortunately)
                if (!(tResolved.canBeSeenBy(aspectRT) || aspectRT.isPrivilegedAspect())) {
                        return;
                }
-               
+
                final InstructionFactory fact = getFactory();
 
                InstructionList entryInstructions = new InstructionList();
@@ -2130,25 +2130,25 @@ public class BcelShadow extends Shadow {
 
        /*
         * Implementation notes:
-        * 
+        *
         * AroundInline still extracts the instructions of the original shadow into an extracted method. This allows inlining of even
         * that advice that doesn't call proceed or calls proceed more than once.
-        * 
+        *
         * It extracts the instructions of the original shadow into a method.
-        * 
+        *
         * Then it extracts the instructions of the advice into a new method defined on this enclosing class. This new method can then
         * be specialized as below.
-        * 
+        *
         * Then it searches in the instructions of the advice for any call to the proceed method.
-        * 
+        *
         * At such a call, there is stuff on the stack representing the arguments to proceed. Pop these into the frame.
-        * 
+        *
         * Now build the stack for the call to the extracted method, taking values either from the join point state or from the new
         * frame locs from proceed. Now call the extracted method. The right return value should be on the stack, so no cast is
         * necessary.
-        * 
+        *
         * If only one call to proceed is made, we can re-inline the original shadow. We are not doing that presently.
-        * 
+        *
         * If the body of the advice can be determined to not alter the stack, or if this shadow doesn't care about the stack, i.e.
         * method-execution, then the new method for the advice can also be re-lined. We are not doing that presently.
         */
@@ -2200,7 +2200,7 @@ public class BcelShadow extends Shadow {
                String extractedShadowMethodName = NameMangler.aroundShadowMethodName(getSignature(), shadowClass.getNewGeneratedNameTag());
                List<String> parameterNames = new ArrayList<String>();
                boolean shadowClassIsInterface = shadowClass.isInterface();
-               LazyMethodGen extractedShadowMethod = extractShadowInstructionsIntoNewMethod(extractedShadowMethodName, 
+               LazyMethodGen extractedShadowMethod = extractShadowInstructionsIntoNewMethod(extractedShadowMethodName,
                                shadowClassIsInterface?Modifier.PUBLIC:Modifier.PRIVATE,
                                munger.getSourceLocation(), parameterNames,shadowClassIsInterface);
 
@@ -2543,13 +2543,13 @@ public class BcelShadow extends Shadow {
 
        /**
         * Annotation style handling for inlining.
-        * 
+        *
         * Note: The proceedingjoinpoint is already on the stack (since the user was calling pjp.proceed(...)
-        * 
+        *
         * The proceed map is ignored (in terms of argument repositioning) since we have a fixed expected format for annotation style.
         * The aim here is to change the proceed() call into a call to the xxx_aroundBody0 method.
-        * 
-        * 
+        *
+        *
         */
        private InstructionList getRedoneProceedCallForAnnotationStyle(InstructionFactory fact, LazyMethodGen callbackMethod,
                        BcelAdvice munger, LazyMethodGen localAdviceMethod, List<BcelVar> argVarList, boolean isProceedWithArgs) {
@@ -2852,7 +2852,7 @@ public class BcelShadow extends Shadow {
 
                int linenumber = getSourceLine();
                // MOVE OUT ALL THE INSTRUCTIONS IN MY SHADOW INTO ANOTHER METHOD!
-               
+
                // callbackMethod will be something like: "static final void m_aroundBody0(I)"
                boolean shadowClassIsInterface = getEnclosingClass().isInterface();
                LazyMethodGen callbackMethod = extractShadowInstructionsIntoNewMethod(
@@ -2944,7 +2944,7 @@ public class BcelShadow extends Shadow {
                }
 
                closureVarInitialized = false;
-               
+
                // ATAJ for @AJ aspect we need to link the closure with the joinpoint instance
                if (munger.getConcreteAspect() != null && munger.getConcreteAspect().isAnnotationStyleAspect()
                                && munger.getDeclaringAspect() != null && munger.getDeclaringAspect().resolve(world).isAnnotationStyleAspect()) {
@@ -2952,18 +2952,18 @@ public class BcelShadow extends Shadow {
                        aroundClosureInstance = genTempVar(AjcMemberMaker.AROUND_CLOSURE_TYPE);
                        closureInstantiation.append(fact.createDup(1));
                        aroundClosureInstance.appendStore(closureInstantiation, fact);
-                       
+
                        // stick the bitflags on the stack and call the variant of linkClosureAndJoinPoint that takes an int
                        closureInstantiation.append(fact.createConstant(Integer.valueOf(bitflags)));
                        if (needAroundClosureStacking) {
                                closureInstantiation.append(Utility.createInvoke(getFactory(), getWorld(),
                                                new MemberImpl(Member.METHOD, UnresolvedType.forName("org.aspectj.runtime.internal.AroundClosure"),
                                                                Modifier.PUBLIC, "linkStackClosureAndJoinPoint", String.format("%s%s", "(I)", "Lorg/aspectj/lang/ProceedingJoinPoint;"))));
-                               
+
                        } else {
                                closureInstantiation.append(Utility.createInvoke(getFactory(), getWorld(),
                                                new MemberImpl(Member.METHOD, UnresolvedType.forName("org.aspectj.runtime.internal.AroundClosure"),
-                                                               Modifier.PUBLIC, "linkClosureAndJoinPoint", String.format("%s%s", "(I)", "Lorg/aspectj/lang/ProceedingJoinPoint;"))));                          
+                                                               Modifier.PUBLIC, "linkClosureAndJoinPoint", String.format("%s%s", "(I)", "Lorg/aspectj/lang/ProceedingJoinPoint;"))));
                        }
 
                }
@@ -2973,36 +2973,37 @@ public class BcelShadow extends Shadow {
 
                // invoke the advice
                InstructionHandle tryUnlinkPosition  = advice.append(munger.getNonTestAdviceInstructions(this));
-               
+
                if (needAroundClosureStacking) {
                        // Call AroundClosure.unlink() in a 'finally' block
                        if (munger.getConcreteAspect() != null && munger.getConcreteAspect().isAnnotationStyleAspect()
                                        && munger.getDeclaringAspect() != null
                                        && munger.getDeclaringAspect().resolve(world).isAnnotationStyleAspect()
                                        && closureVarInitialized) {
-                               
+
                                // Call unlink when 'normal' flow occurring
                                aroundClosureInstance.appendLoad(advice, fact);
                                InstructionHandle unlinkInsn = advice.append(Utility.createInvoke(getFactory(), getWorld(), new MemberImpl(Member.METHOD, UnresolvedType
                                                .forName("org.aspectj.runtime.internal.AroundClosure"), Modifier.PUBLIC, "unlink",
                                                "()V")));
-                               
+
                                InstructionHandle jumpOverHandler = advice.append(InstructionConstants.NOP);
-                               
+
                                // Call unlink in finally block
-                               InstructionHandle handlerStart = advice.append(InstructionConstants.POP);
-                               aroundClosureInstance.appendLoad(advice, fact);
+
+                               // Do not POP the exception off, we need to rethrow it
+                               InstructionHandle handlerStart = advice.append(aroundClosureInstance.createLoad(fact));
                                advice.append(Utility.createInvoke(getFactory(), getWorld(), new MemberImpl(Member.METHOD, UnresolvedType
                                                .forName("org.aspectj.runtime.internal.AroundClosure"), Modifier.PUBLIC, "unlink",
                                                "()V")));
-                               advice.append(InstructionConstants.ACONST_NULL);
+                               // After that exception is on the top of the stack again
                                advice.append(InstructionConstants.ATHROW);
                                InstructionHandle jumpTarget = advice.append(InstructionConstants.NOP);
                                jumpOverHandler.setInstruction(InstructionFactory.createBranchInstruction(Constants.GOTO, jumpTarget));
                                enclosingMethod.addExceptionHandler(tryUnlinkPosition, unlinkInsn, handlerStart, null/* ==finally */, false);
-                       }       
+                       }
                }
-               
+
                advice.append(returnConversionCode);
                if (getKind() == Shadow.MethodExecution && linenumber > 0) {
                        advice.getStart().addTargeter(new LineNumberTag(linenumber));
@@ -3111,7 +3112,7 @@ public class BcelShadow extends Shadow {
        }
 
        /**
-        * 
+        *
         * @param callbackMethod the method we will call back to when our run method gets called.
         * @param proceedMap A map from state position to proceed argument position. May be non covering on state position.
         */
@@ -3183,7 +3184,7 @@ public class BcelShadow extends Shadow {
 
        /**
         * Extract the instructions in the shadow to a new method.
-        * 
+        *
         * @param extractedMethodName name for the new method
         * @param extractedMethodVisibilityModifier visibility modifiers for the new method
         * @param adviceSourceLocation source location of the advice affecting the shadow