]> source.dussan.org Git - aspectj.git/commitdiff
tests and fixes for
authorjhugunin <jhugunin>
Tue, 22 Apr 2003 21:58:02 +0000 (21:58 +0000)
committerjhugunin <jhugunin>
Tue, 22 Apr 2003 21:58:02 +0000 (21:58 +0000)
Bugzilla Bug 29665
   Inconsistant stack height

tests/ajcTests.xml
tests/bugs/StackError.java [new file with mode: 0644]
tests/bugs/messyAround/DebugTrace.aj [new file with mode: 0644]
tests/bugs/messyAround/aspects/Trace.aj [new file with mode: 0644]
tests/bugs/messyAround/cap/OptionList.java [new file with mode: 0644]
tests/jimTests.xml
weaver/src/org/aspectj/weaver/bcel/BcelShadow.java
weaver/src/org/aspectj/weaver/bcel/LazyMethodGen.java

index 845d21687997856f5cf0112917a6deeca933ae96..8785b0b8acda5f25f9fc07b480d9833feaad86ac 100644 (file)
         <run class="tjpStaticPart.Test"/>
     </ajc-test>
     
+    <ajc-test dir="bugs" pr="29665"
+      title="Inconsistant stack height with around">
+        <compile files="StackError.java"/>
+        <run class="StackError"/>
+    </ajc-test>
+    
+
+       <ajc-test dir="bugs/messyAround" pr="36056"
+      title="Ajc 1.1 rc1 java.lang.VerifyError with messy arounds">
+        <compile files="aspects/Trace.aj,cap/OptionList.java,DebugTrace.aj">
+            <message kind="warning" line="102"/>
+               <message kind="warning" line="124"/>
+               <message kind="warning" line="138"/>
+        </compile>
+        <run class="cap.OptionList"/>
+    </ajc-test>  
+    
 </suite>
diff --git a/tests/bugs/StackError.java b/tests/bugs/StackError.java
new file mode 100644 (file)
index 0000000..2b3a04a
--- /dev/null
@@ -0,0 +1,46 @@
+import java.lang.reflect.Method;
+
+public class StackError {
+       public static void main(String args[]) {
+               new StackError().testEqualsNull();
+       }
+
+       void assertTrue(String msg, boolean b) {}
+
+       public void testEqualsNull() {
+               StackError one = new StackError();
+               StackError two = new StackError();
+               assertTrue("equal", one.equals(two));   // does not work
+               //boolean yes = one.equals(two);                        // works
+       }
+       
+       public boolean equals(Object other) {
+               return true;
+       }
+}
+
+aspect EqualsContract {
+       pointcut equalsCall(Object thisOne, Object otherOne):
+               target(Object+) && 
+       target(thisOne) &&
+               call(public boolean equals(Object+)) &&
+               args(otherOne) &&
+               !within(EqualsContract);
+       
+       boolean around(Object thisOne, Object otherOne):
+       equalsCall(thisOne, otherOne) {
+               boolean result = proceed(thisOne, otherOne);
+               Class cls = thisOne.getClass();
+               String name = cls.getName();
+               boolean hasHashCode = false;
+               try {
+                       Method m = cls.getDeclaredMethod("hashCode", null);
+                       String lookFor = "public int " + name + ".hashCode()";
+                       hasHashCode = lookFor.equals(m.toString());
+               }
+               catch (NoSuchMethodException nsme) {
+               }
+               return result;
+       }
+}
diff --git a/tests/bugs/messyAround/DebugTrace.aj b/tests/bugs/messyAround/DebugTrace.aj
new file mode 100644 (file)
index 0000000..7e8c554
--- /dev/null
@@ -0,0 +1,34 @@
+import aspects.*;\r
+//import org.apache.log4j.*;\r
+//import com.checkfree.common.util.*;\r
+import java.lang.reflect.*;\r
+import java.util.*;\r
+import org.aspectj.lang.reflect.*;\r
+/**\r
+* This concrete trace aspect specifies what we should trace.\r
+ */\r
+\r
+privileged aspect DebugTrace extends Trace\r
+                                 \r
+{\r
+    declare precedence: DebugTrace, *;\r
+    \r
+    //private static Logger _log = null;\r
+    \r
+    static\r
+    {\r
+        //String log4jPath = GlobalPaths.getPath("properties_dir")+"log4j.properties";\r
+        //PropertyConfigurator.configure(log4jPath);\r
+        //_log = Logger.getLogger(TestLog.class);    \r
+    }\r
+        \r
+    /** define the pointcut for what we trace */\r
+    protected pointcut lexicalScope() :within(cap.OptionList);\r
+        \r
+    protected void log(String data)\r
+    {\r
+        System.err.println("data: " + data);\r
+        //_log.debug(data);        \r
+    }  \r
+    \r
+}\r
diff --git a/tests/bugs/messyAround/aspects/Trace.aj b/tests/bugs/messyAround/aspects/Trace.aj
new file mode 100644 (file)
index 0000000..780ba66
--- /dev/null
@@ -0,0 +1,490 @@
+package aspects;\r
+\r
+import java.io.*;\r
+import java.util.*;\r
+import org.aspectj.runtime.*;\r
+import org.aspectj.lang.*;\r
+import org.aspectj.lang.reflect.*;\r
+import java.lang.reflect.*;\r
+\r
+/** Trace is an aspect that traces execution through code.  \r
+ * \r
+ */\r
+public abstract aspect Trace issingleton()\r
+{   \r
+    // our internal instance\r
+    private static Trace _trace;\r
+    \r
+    // Call depth on trace\r
+    private static final ThreadLocal traceDepths = new ThreadLocal();\r
+\r
+    // An object to synchronize on\r
+    protected static final Object  lock = new Object();\r
+\r
+    private static final String NL = System.getProperty("line.separator");\r
+    \r
+    // Space indentation increment\r
+    private static final int INDENT = 4;\r
+\r
+    // Used for indentation\r
+    private static final byte[] SPACES = new byte[100];\r
+    \r
+    private static boolean traceActive = true;\r
+\r
+    static\r
+    {\r
+        Arrays.fill(SPACES,(byte)' ');\r
+    }\r
+\r
+       \r
+    /** Trace constructor. Since this aspect is a singleton, we can be\r
+     * assured that only a single instance exists.  \r
+     */\r
+    protected Trace() {_trace = this;}\r
+    \r
+    /**\r
+    * This abstract pointcut indicates what classes we should trace.  Typically\r
+    * you will define this using a within() PCD. We leave that up to concrete aspects.\r
+    */\r
+    protected abstract pointcut lexicalScope();\r
+    \r
+    /**\r
+     * Common scope for all traces - includes lexicalScope\r
+     */\r
+    final pointcut scope() : if(_trace != null && _trace.canTraceJoinpoint(thisJoinPoint)) && lexicalScope() && !within(Trace+);\r
+\r
+    /**\r
+     * This pointcut designates tracing constructors within lexicalScope()\r
+     */\r
+    protected final pointcut constructorTrace() : scope() && (call( new(..) ) || execution( new(..)));\r
+    /**\r
+     * This pointcut designates tracing method executions within lexicalScope()\r
+     */\r
+    protected final pointcut methodTrace() : scope() && (call(* *(..)) || execution(* *(..)));\r
+\r
+    /**\r
+     * This pointcut designates tracing exception handlers within lexicalScope()\r
+     */\r
+    protected final pointcut handlerTrace(Exception e) : scope() &&  args(e) && handler(Exception+);\r
+\r
+\r
+    /**\r
+     * This pointcut picks out joinpoints within this aspect that implement\r
+     * the actual tracing.  Since parameters and return values are printed\r
+     * out via implicit or explicit call to Object.toString(), there is the possibility\r
+     * of an execution joinpoint on toString() causing the trace logic\r
+     * to be re-entered. This is undesireable because it makes the trace output\r
+     * difficult to read and adds unecessary overhead. <p>\r
+     * This pointcut is used within a cflowbelow pointcut to prevent recursive\r
+     * trace calls.\r
+     */\r
+    private pointcut internalMethods() : \r
+        execution( void Trace.trace*(..,(JoinPoint||JoinPoint.StaticPart),..) );\r
+\r
+    /**\r
+     * For methods, we use around() advice to capture calls/executions.\r
+     */\r
+    Object around() : methodTrace() && !cflowbelow(internalMethods())\r
+    {\r
+        traceEnter(thisJoinPoint); \r
+        try\r
+        {\r
+            Object result = proceed();\r
+            traceResult(result,thisJoinPoint);\r
+            return result;\r
+        }\r
+        finally\r
+        {\r
+            traceExit(thisJoinPoint);\r
+        }\r
+    }\r
+    \r
+    /**\r
+     * For Constructors, we use around() advice to capture calls/executions.\r
+     */\r
+    Object around() : constructorTrace() && !cflowbelow(internalMethods())\r
+    {\r
+        traceEnter(thisJoinPoint); \r
+        try\r
+        {\r
+            return proceed();\r
+        }\r
+        finally\r
+        {\r
+            traceExit(thisJoinPoint);\r
+        }\r
+    }\r
+    \r
+\r
+    /**\r
+     * Trace Exceptions that may occur with constructors or methods\r
+     */\r
+    after() throwing(Throwable e): (constructorTrace() || methodTrace()) && !cflowbelow(internalMethods())\r
+    {   \r
+        traceThrowable(e,thisJoinPoint);\r
+    }\r
+\r
+\r
+    /**\r
+     * Trace Exception handlers entry\r
+     */    \r
+    before(Exception e) : handlerTrace(e) && !cflowbelow(internalMethods())\r
+    {\r
+        traceHandlerEntry(e,thisJoinPointStaticPart);\r
+    }\r
+    /**\r
+     * Trace Exception handlers exit\r
+     */\r
+    after(Exception e) : handlerTrace(e) && !cflowbelow(internalMethods())\r
+    {\r
+        traceHandlerExit();\r
+    }\r
+\r
+\r
+    /**\r
+     * Subaspects can override this method to log the data as needed. The default\r
+     * mechanism is to log to System.out\r
+     * \r
+     * Clients should be aware that this method is not synchronized.\r
+     */\r
+    protected void log(String data) {System.out.println(data);}\r
+    \r
+    /**\r
+     * Can be overridden by subaspects to filter what constructors/methods should be \r
+     * traced at runtime.  This method is always called prior to the log()\r
+     * method. The default always returns true.<p> Note that exceptions thrown\r
+     * by constructors/methods are filtered through this method.\r
+     * @param currentlyExecutingClass The Class that is currently executing.\r
+     * @param signature The signature of the member being traced\r
+     * @param traceType The type of trace entry (see AspectJ doc for the available types)\r
+     */\r
+    protected boolean isTraceable(Class currentlyExecutingClass, CodeSignature signature,String traceType) {return true;}\r
+\r
+    /**\r
+     * Can be overridden by subaspects to filter what exception handlers should be \r
+     * traced at runtime.  This method is always called prior to the log()\r
+     * method. The default always returns false.<p>\r
+     * Note that exception handlers are catch(...){} blocks and are filtered\r
+     * independently from constructor/method calls and execution.\r
+     * @param currentlyExecutingClass The Class that is currently executing.\r
+     * @param signature The signature of the member being traced\r
+     */\r
+    protected boolean isTraceable(Class currentlyExecutingClass, CatchClauseSignature signature) {return false;}\r
+    \r
+    /**\r
+     * Retrieves the signature of the joinpoint and asks if it can be traced\r
+     */\r
+    private boolean canTraceJoinpoint(JoinPoint jp)\r
+    {\r
+        if ( !traceActive ) return false;\r
+        final Signature sig = jp.getSignature();\r
+        final Object o = jp.getThis();  // current object\r
+        Class currentType;\r
+        if ( o == null ) // must be a static\r
+            currentType = jp.getStaticPart().getSourceLocation().getWithinType();\r
+        else\r
+            currentType = o.getClass();\r
+            \r
+        // dispatch the correct filter method\r
+        if ( sig instanceof CodeSignature )            \r
+            return isTraceable(currentType,(CodeSignature)sig,jp.getKind());\r
+        else\r
+            return isTraceable(currentType,(CatchClauseSignature)sig);            \r
+    }\r
+    \r
+\r
+    /**\r
+     * This method creates a trace entry line based on information in the\r
+     * supplied join point.\r
+     */\r
+    private void traceEnter(JoinPoint thisJoinPoint)\r
+    { \r
+   \r
+        // Get the indent level (call depth for current thread * 4).\r
+        int depth = getTraceDepth(INDENT);\r
+    \r
+        Class[] parameterTypes = ((CodeSignature)thisJoinPoint.getSignature()).getParameterTypes();\r
+        String[] parameterNames = ((CodeSignature)thisJoinPoint.getSignature()).getParameterNames();\r
+    \r
+        boolean isCall = thisJoinPoint.getKind().endsWith("call");\r
+\r
+        StringBuffer enterPhrase = new StringBuffer(100);\r
+        enterPhrase.append(getSpaces(depth));\r
+        if ( isCall )\r
+            enterPhrase.append("Call ");\r
+        else\r
+            enterPhrase.append("Entering ");\r
+        enterPhrase.append(methodSignature(parameterNames,parameterTypes,thisJoinPoint));\r
+        \r
+//        if ( isCall )\r
+//            enterPhrase.append(" From: ").append(thisJoinPoint.getSourceLocation().getWithinType());\r
+\r
+        // Prepare the methods parameter list\r
+        String parmStr = null;\r
+        Object[] parameters = thisJoinPoint.getArgs();\r
+        if (parameters.length > 0)\r
+        {\r
+            String spaces = getSpaces(depth + 6);\r
+            StringBuffer parms = new StringBuffer();\r
+            for (int i = 0; i < parameters.length; i++)\r
+            {\r
+                if (parameters[i] != null && parameters[i].getClass().isArray())\r
+                {\r
+                    // arrays can be huge...limit to first 100 elements\r
+                    final int len = Math.min(Array.getLength(parameters[i]),100);\r
+                    if ( len == 0 )\r
+                    {\r
+                        parms.append(spaces);\r
+                        parms.append(parameterNames[i]);\r
+                        parms.append(": 0 length array");\r
+                        parms.append(NL);\r
+                    }\r
+                    else\r
+                    {\r
+                        Object o = null;\r
+                        for ( int x = 0; x < len; x++ )\r
+                        {\r
+                            parms.append(spaces);\r
+                            parms.append(parameterNames[i]);\r
+                            parms.append("[");\r
+                            parms.append(x);\r
+                            parms.append("]:");\r
+                            o = Array.get(parameters[i],x);\r
+                            try{parms.append(" " + (o != null?o:"null"));}  // implicit toString()\r
+                            catch(Throwable t) {parms.append(" " + parameters[i]);}\r
+                            parms.append(NL);                            \r
+                        }\r
+                    }\r
+                }\r
+                else\r
+                {\r
+                    // Not an array.\r
+                    parms.append(spaces);\r
+                    parms.append(parameterNames[i]);\r
+                    parms.append(": ");\r
+                    try\r
+                    {\r
+                        parms.append("" + parameters[i]);\r
+                    }\r
+                    catch (Throwable t ) {parms.append("" + parameters[i].getClass().getName());}\r
+                }\r
+\r
+                parmStr = parms.toString();\r
+\r
+            }\r
+\r
+        }\r
+\r
+        if (parmStr != null)\r
+            enterPhrase.append(NL).append(parmStr);\r
+        log(enterPhrase.toString());\r
+    }\r
+\r
+    /**\r
+     * This method creates an exception handler trace entry based on a Throwable\r
+     * and information contained in the join point.\r
+     */\r
+    private void traceHandlerEntry(Throwable t, JoinPoint.StaticPart thisJoinPoint)\r
+    {\r
+\r
+        int depth = getTraceDepth(INDENT);\r
+        String phrase = getSpaces(depth) +\r
+                        "Exception caught at: " +\r
+                        thisJoinPoint;\r
+        log(printStackTrace(phrase,t));\r
+\r
+    }\r
+    /**\r
+     * This method simply adjusts the trace depth - no other information printed.\r
+     */\r
+    private void traceHandlerExit()\r
+    {\r
+        getTraceDepth(-INDENT);\r
+    }\r
+    /**\r
+     * This method creates a stack trace entry based on a Throwable and\r
+     * information contained in the join point.\r
+     */\r
+    private void traceThrowable(Throwable t, JoinPoint thisJoinPoint)\r
+    {\r
+\r
+        int depth = getTraceDepth(0);\r
+        String phrase = getSpaces(depth+4) +\r
+                                        "Throwing Exception at: " +\r
+                                        thisJoinPoint;\r
+        log(printStackTrace(phrase,t));\r
+    }\r
+\r
+\r
+    private String printStackTrace(String phrase, Throwable t)\r
+      {\r
+    try {\r
+      StringWriter sw = new StringWriter(4096);\r
+      PrintWriter  pw = new PrintWriter(sw,true);\r
+\r
+      pw.println(phrase);\r
+\r
+      pw.println();\r
+\r
+      pw.println("Exception Stack Trace:");\r
+\r
+      pw.println();\r
+\r
+      t.printStackTrace(pw);\r
+\r
+      pw.println();\r
+\r
+      pw.flush();\r
+      sw.flush();\r
+\r
+      pw.close();\r
+      sw.close();\r
+      return sw.toString();\r
+    }\r
+    catch(IOException IOE) {\r
+      log(IOE.toString());\r
+      return IOE.getMessage();\r
+    }\r
+      }\r
+\r
+    /**\r
+     * This method creates a trace exit entry based on the join point\r
+     * information.\r
+     */\r
+    private void traceExit(JoinPoint thisJoinPoint)\r
+    {\r
+\r
+        int depth = getTraceDepth(-INDENT);\r
+\r
+        // Assemble the method's signature.\r
+        Class[] parameterTypes = ((CodeSignature)thisJoinPoint.getSignature()).getParameterTypes();\r
+        String[] parameterNames = ((CodeSignature)thisJoinPoint.getSignature()).getParameterNames();\r
+\r
+        boolean isCall = thisJoinPoint.getKind().endsWith("call");\r
+\r
+        StringBuffer exitPhrase = new StringBuffer(100);\r
+        exitPhrase.append(getSpaces(depth));\r
+        if ( isCall )\r
+            exitPhrase.append("Return ");\r
+        else\r
+            exitPhrase.append("Exiting ");\r
+        exitPhrase.append(methodSignature(parameterNames,parameterTypes,thisJoinPoint)).append(NL);\r
+        \r
+\r
+        log(exitPhrase.toString());\r
+\r
+    }\r
+\r
+    /**\r
+     * This method creates a trace result entry based on a result and the\r
+     * join point.\r
+     */\r
+    private void traceResult(Object thisResult, JoinPoint thisJoinPoint)\r
+    {\r
+\r
+        Class returnType = ((MethodSignature)thisJoinPoint.getSignature()).getReturnType();\r
+        if ( returnType.toString().equals("void") )\r
+            return;\r
+\r
+        int depth = getTraceDepth(0);\r
+        if ( thisResult == null )\r
+            thisResult = "null";\r
+\r
+        if ( thisResult.getClass().isArray() )\r
+        {\r
+            // arrays can be Oprah-sized - limit to 100 elements\r
+            final int len = Math.min(Array.getLength(thisResult),100);\r
+            StringBuffer buf = new StringBuffer();\r
+            if ( len == 0 )\r
+                buf.append(">>>zero-length array<<<");\r
+            else\r
+            {\r
+                Object o;\r
+                for ( int i = 0; i < len; i++ )\r
+                {\r
+                    o = Array.get(thisResult,i);\r
+                    buf.append("data[").append(i).append("] ");\r
+                    try{buf.append(o != null?o:"null");} // implicit toString() \r
+                    catch(Throwable t) {buf.append(thisResult);}\r
+                    buf.append(NL);\r
+                }\r
+            }\r
+            thisResult = buf.toString();                \r
+        }\r
+        thisResult = thisResult.toString();\r
+        \r
+        StringBuffer returnPhrase = new StringBuffer(100);\r
+        returnPhrase.append(getSpaces(depth+2)).append(thisJoinPoint);\r
+        returnPhrase.append(" returned >>>>>>> ").append(thisResult);\r
+\r
+        log(returnPhrase.toString());\r
+    }\r
+\r
+    /**\r
+     * This method returns the current trace line indentation for the\r
+     * thread.\r
+     */\r
+    private int getTraceDepth(int incr)\r
+    {\r
+            int rc = 0;\r
+            Integer depth = (Integer) traceDepths.get();\r
+            if (depth == null)\r
+            {\r
+                if ( incr > 0 )\r
+                {\r
+                    traceDepths.set(new Integer(incr));\r
+                    return incr;\r
+                }\r
+                else return rc;\r
+            }    \r
+            \r
+            rc = depth.intValue();\r
+                            \r
+            if ( incr > 0 )\r
+            {\r
+                depth = new Integer(rc += incr);            \r
+                traceDepths.set(depth);\r
+            }\r
+            else if ( incr < 0 )\r
+            {\r
+                depth = new Integer(rc + incr);            \r
+                traceDepths.set(depth);\r
+            }\r
+            \r
+            return rc;\r
+    }\r
+\r
+    /**\r
+     * This method returns a String containing the number of spaces desired to\r
+     * be used as padding for formatting trace log entries.\r
+     */\r
+    private String getSpaces(int num)\r
+    {\r
+        return new String(SPACES,0,Math.min(num,SPACES.length));\r
+    }\r
+\r
+    /**\r
+     * Create a method signature\r
+     */\r
+    private String methodSignature(String[] parameterNames,\r
+                    Class[] parameterTypes,\r
+                    JoinPoint thisJoinPoint)\r
+    {\r
+        // Assemble the method's signature.\r
+        StringBuffer signature = new StringBuffer("(");\r
+        for (int i = 0; i < parameterTypes.length; i++)\r
+        {\r
+            signature.append(parameterTypes[i].getName());\r
+            signature.append(" ");\r
+            signature.append(parameterNames[i]);\r
+            if (i < (parameterTypes.length-1))\r
+                signature.append(", ");\r
+        }\r
+        signature.append(")");\r
+\r
+        return thisJoinPoint.getSignature().getDeclaringType().getName() + "." +\r
+            thisJoinPoint.getSignature().getName() +\r
+            signature;\r
+    }\r
+    \r
+}
\ No newline at end of file
diff --git a/tests/bugs/messyAround/cap/OptionList.java b/tests/bugs/messyAround/cap/OptionList.java
new file mode 100644 (file)
index 0000000..1e4a934
--- /dev/null
@@ -0,0 +1,138 @@
+package cap; 
+
+import java.lang.reflect.*;
+import java.util.*;
+import java.net.*;
+import java.text.*;
+
+/**
+ * This class builds a list of &lt;option&gt; HTML elements given a data object,
+ * typically a GAPI output object, and a list of accessor method names.  
+ * <p>
+ * <b>Usage:</b><pre>
+ *   // Create the bank account list select
+ *   RBBankAcctList2Input acctIn = new RBBankAcctList2Input();
+ *   initApiHeader(acctIn.getHeader(),sessInfo);
+ *   ArrayList accts = new ArrayList();
+ *   getBankAccounts(acctIn,accts);
+ *   
+ *   String ol = OptionList.createListHtmlFromApi(accts.toArray(),
+ *                                   new String[]{"getBankAcctNbr","getBankRtgNbr","getBankAcctTyp"},
+ *                                   new String[]{"getBankAcctNbr"},
+ *                                   new MessageFormat("{0}"),
+ *                                   Integer.parseInt(acctIndex));
+ * 
+ * </pre>
+ * @author Rich Price
+ */
+class OptionList
+{
+       private static final String OPTION_PATTERN = "<option value=\"{0}\" {1}>{2}</option>";
+       private static final Object[] GETTER_ARGS = new Object[0];
+       private static final Class[] GETTER_ARG_TYPES = new Class[0];
+       private static final String DELIM = "&";
+    
+       /**
+        * Parses the value string and returns a HashMap of name/value pairs
+        * @return A HashMap of name/value pairs. 
+        * @see createListHtmlFromApi
+        */
+       public static HashMap getSelectedValues(String optionListValueString)
+       {
+               HashMap map = new HashMap();
+               if ( optionListValueString != null )
+               {
+                       StringTokenizer lex = new StringTokenizer(optionListValueString,DELIM + "=",false);
+                       while ( lex.hasMoreTokens() )
+                               map.put(lex.nextToken(),lex.nextToken());
+               }
+               return map;            
+       }
+    
+       /**
+        * This method creates a String of HTML &lt;option&gt; elements in the following
+        * format:<p>
+        * <pre>
+        * &lt;option value="valueName1=value1^valueName2=value2"&gt; optionValues &lt;option&gt;
+        * </pre>
+        * @param api An array of Objects, typically a GAPI output object, from which data
+        * will be retrieved by name(s).
+        * @param valueNames An array of method names declared in <code>api</code>. Only
+        * public methods taking zero arguments can be used.  Each non-null value
+        * is used to create a value string for the particular HTML option element in the form:
+        * valueName1=value1^valueName2=value2...where valueName[n] is the method name.
+        * For convenience, the getValues() method will return a HashMap of these name/value
+        * pairs.
+        * @param optionNames An array of method names declared in <code>api</code>. Only
+        * public methods taking zero arguments can be listed.  Each non-null value
+        * is used to create a parameter list to pass to a supplied MessageFormat object.
+        * Each value retrieved from the api object will be substituted using the MessageFormat
+        * object, the resulting String is used to create the optionValues string that is
+        * displayed to the user.
+        * @param selectedIndex The index of the option that should be selected. If -1, nothing
+        * will be selected.
+        * 
+        */
+       public static String createListHtmlFromApi(Object[] api, 
+                                                                       String[] valueNames,
+                                                                       String[] optionNames,
+                                                                       MessageFormat optionFormat,
+                                                                       int selectedIndex )
+       {
+               StringBuffer html = new StringBuffer();
+               for ( int apiIndex = 0; apiIndex < api.length; apiIndex++ )
+               {
+                       final String[] messageArgs = new String[3];        
+                       // for each valueName, use reflection to look up data from the api
+                       StringBuffer buf = new StringBuffer();
+                       for ( int i = 0; i < valueNames.length; i++ )
+                       {
+                               try
+                               {
+                                       Method m = api[apiIndex].getClass().getMethod(valueNames[i], GETTER_ARG_TYPES);
+                                       String value = m.invoke(api[apiIndex],GETTER_ARGS).toString();
+
+                                       if ( value != null && value.length() > 0 )
+                                       {
+                                               if ( buf.length() > 0 )
+                                                       buf.append(DELIM);
+                                               buf.append(valueNames[i]).append("=").append(value);
+                                       }    
+                               }
+                               catch (Exception e) {}
+                       }
+                       // set the first and second value arguments for the pattern
+                       messageArgs[0] = buf.toString();
+                       if ( apiIndex == selectedIndex )
+                               messageArgs[1] = "selected";
+                       else
+                               messageArgs[1] = "";
+                        
+                       // now, handle the option part
+                       buf.setLength(0);
+                       String[] optionFormatArgs = new String[optionNames.length];
+                       for ( int i = 0; i < optionNames.length; i++ )
+                       {
+                               try
+                               {
+                                       optionFormatArgs[i] = "";    
+                                       Method m = api[apiIndex].getClass().getMethod(optionNames[i],GETTER_ARG_TYPES);
+                                       String value = m.invoke(api[apiIndex],GETTER_ARGS).toString();
+                                       if ( value != null )
+                                               optionFormatArgs[i] = value;
+                               }
+                               catch(Exception e) {}
+                       }
+            
+                       messageArgs[2] = optionFormat.format(optionFormatArgs,buf,new FieldPosition(0)).toString();
+                       html.append(MessageFormat.format(OPTION_PATTERN,messageArgs));
+               }
+               return html.toString();
+       }
+    
+       public static void main(String[] args) throws Exception
+       {
+               OptionList.createListHtmlFromApi(new Object[]{new String()},new String[]{"getFoo"},new String[]{"getFoo"},new MessageFormat("{0}"),-1);
+       }
+    
+}
\ No newline at end of file
index 7664c379982f33b79e28be5dfb6be6dee5b8a25d..a58aef5fa3ba170707e668ada8520150b583d6b1 100644 (file)
@@ -1,8 +1,26 @@
 <!DOCTYPE suite SYSTEM "../tests/ajcTestSuite.dtd">
 <suite>    
+    <ajc-test dir="bugs" pr="29665"
+      title="Inconsistant stack height with around">
+        <compile files="StackError.java"/>
+        <run class="StackError"/>
+    </ajc-test>
+    
 
+       <ajc-test dir="bugs/messyAround" pr="36056"
+      title="Ajc 1.1 rc1 java.lang.VerifyError with messy arounds">
+        <compile files="aspects/Trace.aj,cap/OptionList.java,DebugTrace.aj">
+            <message kind="warning" line="102"/>
+               <message kind="warning" line="124"/>
+               <message kind="warning" line="138"/>
+        </compile>
+        <run class="cap.OptionList"/>
+    </ajc-test>  
 
     <!--
+  
+
+
     
     <ajc-test dir="new" pr="885"
       title="source locations within expressions">
index 6059e7d9fd7dfc0edd260d28ec7f544d86c34432..30f5b263f38f4221bc3832341ad10b35627800aa 100644 (file)
@@ -1201,7 +1201,12 @@ public class BcelShadow extends Shadow {
         BcelObjectType ot = BcelWorld.getBcelObjectType(declaringType); 
         
                LazyMethodGen adviceMethod = ot.getLazyClassGen().getLazyMethodGen(mungerSig);
-               if (!adviceMethod.getCanInline()) {
+               //TODO handle the second part of this if by creating a method for the around
+               //     advice rather than going to a full-on closure
+               if (!adviceMethod.getCanInline() ||
+                   isFallsThrough() && adviceMethod.hasExceptionHandlers() // fix for Bug 29665
+                       )
+               {
                        weaveAroundClosure(munger, hasDynamicTest);
                        return;
                }
index adf0c953a0ff130371ac8b8d1a000d60ba298832..9c7f3dc42a02c1521a5fdff1c3e9521afba94388 100644 (file)
@@ -81,6 +81,7 @@ public final class LazyMethodGen {
     private int             maxLocals; 
     
     private boolean canInline = true;
+    private boolean hasExceptionHandlers;
     
     /**
      * only used by {@link BcelClassWeaver}
@@ -167,6 +168,7 @@ public final class LazyMethodGen {
         CodeExceptionGen[] exns = gen.getExceptionHandlers();
         if (exns != null) {
             int len = exns.length;
+            if (len > 0) hasExceptionHandlers = true;
             int priority = len - 1;
             for (int i = 0; i < len; i++, priority--) {
                 CodeExceptionGen exn = exns[i];
@@ -1145,5 +1147,8 @@ public final class LazyMethodGen {
        public void setCanInline(boolean canInline) {
                this.canInline = canInline;
        }
+       public boolean hasExceptionHandlers() {
+               return hasExceptionHandlers;
+       }
 
 }