Bug 119543 - [waiting-on-build] StringIndexOutOfBoundsException, perhaps due to nested aspects and targets
Summary: [waiting-on-build] StringIndexOutOfBoundsException, perhaps due to nested asp...
Status: RESOLVED FIXED
Alias: None
Product: AspectJ
Classification: Tools
Component: Compiler (show other bugs)
Version: 1.5.0M5   Edit
Hardware: PC Windows XP
: P3 normal (vote)
Target Milestone: 1.5.0RC1   Edit
Assignee: Andrew Clement CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2005-12-06 21:14 EST by Wes Isberg CLA
Modified: 2012-04-03 16:18 EDT (History)
0 users

See Also:


Attachments
testcase patch (2.17 KB, patch)
2005-12-07 05:53 EST, Helen Beeken CLA
no flags Details | Diff
improved testcase patch (2.30 KB, patch)
2005-12-07 09:59 EST, Helen Beeken CLA
aclement: iplog+
Details | Diff
patch containing fix (777 bytes, patch)
2005-12-07 10:01 EST, Helen Beeken CLA
aclement: iplog+
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Wes Isberg CLA 2005-12-06 21:14:23 EST
Code and exception below.  

Same result for HEAD and AspectJ 1.5.0.20051206103951 via AJDT.

------------------------------- bugs/CachedToString.java

package bugs;

import java.lang.ref.SoftReference;

import junit.framework.TestCase;

public class CachedToString {

    /**
     * @param args
     */
    public static void main(String[] args) {
        new CacheGetterTest().testToString();
    }
    public static class CacheGetterTest extends TestCase {
        static boolean runAdvice;
        public void testToString() {
            RandomToString me = new RandomToString ();
            runAdvice = false;
            String one = me.toString();
            String two = me.toString();
            assertFalse(one.equals(two));
            runAdvice = true;
            one = me.toString();
            two = me.toString();
            assertTrue(one.equals(two));
        }
        static class RandomToString {
            static aspect CacheToString extends CachedItem<String> {
                public pointcut context() : if(runAdvice);
                public pointcut caching() :  
                    execution(String RandomToString.toString());
            }
            Random random = new Random(-1);
            public String toString() {
                return "" + random.nextFloat();
            }
        }
    }

    public abstract static aspect CachedItem<Result> pertarget(results()){
        SoftReference<Result> cache;

        boolean nullCache;

        // CODE writing pointcut trifecta separate context if extended and errors
        // getcode declaring-pointcuts-to-extend-with-caching START
        /**
         * Define join points to cache.
         * Must override, with staticly-determinable pointcuts 
         * that pick out join points that are permitted.
         */
        protected abstract pointcut caching();
            
        /** ok to use Result field-get or return value */
        private pointcut permitted() : get(Result *) || execution(Result *())
            || call(Result *()) || call(Result.new(..));

        /** Subaspects may add dynamic tests for the join points. */
        pointcut context() : !disabled();

        private pointcut disabled(); // fyi, undefined == none

        // getcode results START
        /** the pointcut composed from the user, as permitted, with context */
        pointcut results() : caching() && permitted() && context();
        // getcode results END

        Result around() :  results() {
            // ... }
            // getcode declaring-pointcuts-to-extend-with-caching END
            Result result;
            if (nullCache) {
                result = null;
            } else if ((null == cache) 
                    || (null == (result = cache.get()))){
                result = proceed();
                if (null == result) {
                    nullCache = true;
                } else {
                    cache = new SoftReference(result);
                }
            }
            return result;
        }

    }

}


------------------------------- exception
C:\home\ws\main-31\my-ajbugs\aspectj-src\bugs\CachedToString.java [error] Internal compiler error
java.lang.StringIndexOutOfBoundsException: String index out of range: -2
	at java.lang.String.substring(String.java:1768)
	at org.aspectj.weaver.TypeFactory.createTypeFromSignature(TypeFactory.java:86)
	at org.aspectj.weaver.UnresolvedType.forSignature(UnresolvedType.java:426)
	at org.aspectj.weaver.UnresolvedType.getOutermostType(UnresolvedType.java:601)
	at org.aspectj.weaver.ResolvedType.isVisible(ResolvedType.java:1259)
	at org.aspectj.weaver.ResolvedType.addPointcutsResolvingConflicts(ResolvedType.java:1743)
	at org.aspectj.weaver.ResolvedType.getExposedPointcuts(ResolvedType.java:1716)
	at org.aspectj.weaver.ResolvedType.getExposedPointcuts(ResolvedType.java:1710)
	at org.aspectj.ajdt.internal.compiler.lookup.EclipseSourceType.checkPointcutDeclarations(EclipseSourceType.java:293)
	at org.aspectj.ajdt.internal.compiler.lookup.AjLookupEnvironment.resolvePointcutDeclarations(AjLookupEnvironment.java:414)
	at org.aspectj.ajdt.internal.compiler.lookup.AjLookupEnvironment.resolvePointcutDeclarations(AjLookupEnvironment.java:419)
	at org.aspectj.ajdt.internal.compiler.lookup.AjLookupEnvironment.resolvePointcutDeclarations(AjLookupEnvironment.java:419)
	at org.aspectj.ajdt.internal.compiler.lookup.AjLookupEnvironment.resolvePointcutDeclarations(AjLookupEnvironment.java:419)
	at org.aspectj.ajdt.internal.compiler.lookup.AjLookupEnvironment.completeTypeBindings(AjLookupEnvironment.java:231)
	at org.aspectj.org.eclipse.jdt.internal.compiler.Compiler.beginToCompile(Compiler.java:301)
	at org.aspectj.org.eclipse.jdt.internal.compiler.Compiler.compile(Compiler.java:315)
	at org.aspectj.ajdt.internal.core.builder.AjBuildManager.performCompilation(AjBuildManager.java:811)
	at org.aspectj.ajdt.internal.core.builder.AjBuildManager.doBuild(AjBuildManager.java:230)
	at org.aspectj.ajdt.internal.core.builder.AjBuildManager.batchBuild(AjBuildManager.java:156)
	at org.aspectj.ajdt.ajc.AjdtCommand.doCommand(AjdtCommand.java:112)
	at org.aspectj.ajdt.ajc.AjdtCommand.runCommand(AjdtCommand.java:60)
	at org.aspectj.tools.ajc.Main.run(Main.java:326)
	at org.aspectj.tools.ajc.Main.runMain(Main.java:240)
	at org.aspectj.tools.ajc.Main.main(Main.java:83)

(no source information available)
ABORT
	
Exception thrown from AspectJ DEVELOPMENT

This might be logged as a bug already -- find current bugs at
  http://bugs.eclipse.org/bugs/buglist.cgi?product=AspectJ&component=Compiler

Bugs for exceptions thrown have titles File:line from the top stack, 
e.g., "SomeFile.java:243"

If you don't find the exception below in a bug, please add a new bug
at http://bugs.eclipse.org/bugs/enter_bug.cgi?product=AspectJ
To make the bug a priority, please include a test program
that can reproduce this exception.
String index out of range: -2

when resolving pointcut declarations CachedToString
when completing type bindings 
when batch building with classpath: c:\home\apps\jdk15\jre\lib\ext\dnsns.jar;c:\home\apps\jdk15\jre\lib\ext\localedata.jar;c:\home\apps\jdk15\jre\lib\ext\sunjce_provider.jar;c:\home\apps\jdk15\jre\lib\ext\sunpkcs11.jar;C:\home\ws\main-31\lib\junit\junit.jar;C:\home\wes\dev\tools\aspectj-1.5\lib\aspectjrt.jar;
String index out of range: -2
java.lang.StringIndexOutOfBoundsException: String index out of range: -2
	at java.lang.String.substring(String.java:1768)
	at org.aspectj.weaver.TypeFactory.createTypeFromSignature(TypeFactory.java:86)
	at org.aspectj.weaver.UnresolvedType.forSignature(UnresolvedType.java:426)
	at org.aspectj.weaver.UnresolvedType.getOutermostType(UnresolvedType.java:601)
	at org.aspectj.weaver.ResolvedType.isVisible(ResolvedType.java:1259)
	at org.aspectj.weaver.ResolvedType.addPointcutsResolvingConflicts(ResolvedType.java:1743)
	at org.aspectj.weaver.ResolvedType.getExposedPointcuts(ResolvedType.java:1716)
	at org.aspectj.weaver.ResolvedType.getExposedPointcuts(ResolvedType.java:1710)
	at org.aspectj.ajdt.internal.compiler.lookup.EclipseSourceType.checkPointcutDeclarations(EclipseSourceType.java:293)
	at org.aspectj.ajdt.internal.compiler.lookup.AjLookupEnvironment.resolvePointcutDeclarations(AjLookupEnvironment.java:414)
	at org.aspectj.ajdt.internal.compiler.lookup.AjLookupEnvironment.resolvePointcutDeclarations(AjLookupEnvironment.java:419)
	at org.aspectj.ajdt.internal.compiler.lookup.AjLookupEnvironment.resolvePointcutDeclarations(AjLookupEnvironment.java:419)
	at org.aspectj.ajdt.internal.compiler.lookup.AjLookupEnvironment.resolvePointcutDeclarations(AjLookupEnvironment.java:419)
	at org.aspectj.ajdt.internal.compiler.lookup.AjLookupEnvironment.completeTypeBindings(AjLookupEnvironment.java:231)
	at org.aspectj.org.eclipse.jdt.internal.compiler.Compiler.beginToCompile(Compiler.java:301)
	at org.aspectj.org.eclipse.jdt.internal.compiler.Compiler.compile(Compiler.java:315)
	at org.aspectj.ajdt.internal.core.builder.AjBuildManager.performCompilation(AjBuildManager.java:811)
	at org.aspectj.ajdt.internal.core.builder.AjBuildManager.doBuild(AjBuildManager.java:230)
	at org.aspectj.ajdt.internal.core.builder.AjBuildManager.batchBuild(AjBuildManager.java:156)
	at org.aspectj.ajdt.ajc.AjdtCommand.doCommand(AjdtCommand.java:112)
	at org.aspectj.ajdt.ajc.AjdtCommand.runCommand(AjdtCommand.java:60)
	at org.aspectj.tools.ajc.Main.run(Main.java:326)
	at org.aspectj.tools.ajc.Main.runMain(Main.java:240)
	at org.aspectj.tools.ajc.Main.main(Main.java:83)


1 fail|abort, 1 error
Signal 127
Comment 1 Helen Beeken CLA 2005-12-07 05:53:35 EST
Created attachment 31284 [details]
testcase patch

Apply this patch to the tests project.

This patch contains a simplified testcase which recreates the problem. In order for the problem to manifest it needs:

1. An inner generic aspect which contains more than one pointcut and some advice which uses all the pointcuts
2. An inner aspect which extends the generic aspect
Comment 2 Helen Beeken CLA 2005-12-07 08:22:29 EST
When there are two pointcuts UnresolvedType.getOuterMostType() is called on a ReferenceType with signature "LPR119543$A" and also with signature "PPR119543$A<Ljava/lang/String;>;". Since there is a "$", this method returns UnresolvedType.forSigntaure(sig.substring(0,sig.indexOf("$") + ";") which in the second case is UnresolvedType.forSignature("PPR119543"). Since "PPR119543" starts with a "P" this calls TypeFactory.createTypeFromSignature("PPR119543"). Inside this method since we start with a "P" we assume this is parameterized and consequently look for the positions of "<" and ">" which in this case don't exist. The call signature.substring(1,startOfParams) becomes "PPR119543".substring(1,-1) which blows up with the StringOutOfBoundsException. 

In the case where there is only one pointcut we never call UnresolvedType.getOuterMostType() on a ReferenceType with signature "PPR119543$A<Ljava/lang/String;>;". Instead, it's only called on a ReferenceType with signature "LPR119543$A". 
Comment 3 Helen Beeken CLA 2005-12-07 09:06:57 EST
In the case when there are two pointcuts, the method ResolvedType.addPointcutsResolvingConflicts(List acc,List added, boolean isOverriding) is called with the parameterrs:

acc = []
added = [pointcut PR119543$A<java.lang.String>.caching(), pointcut PR119543$A<java.lang.String>.permitted()]
isOverriding = true

This method iterates over the "added" list checking to see if there are any conflicts. At the end of every iteration it adds the pointcut to the acc list so it can calcualate possible conflicts with the other pointcuts it hasn't looked at yet. If there's only one pointcut then there clearly aren't any conflicts. If there are two pointcuts, then it just places the first one in the "acc" list since it hasn't discovered any conflicts yet. When it comes to look at the permitted() pointcut the caching() pointcut is in the acc list so it gets hold of this and starts comparing it for conflicts. The first check is whether they are the same. They're not so we continue. The second check is a call to 

isVisible(existing.getModifiers(), existing.getDeclaringType().resolve(getWorld()),this)

where existing.getDeclaringType().resolve(getWorld()) is the ResolvedType with signature "PPR119543$A<Ljava/lang/String;>;" and this is the ResolvedType also with signature "PPR119543$A<Ljava/lang/String;>;". It is the call getOutermostType() on these ResolvedType's which cause the StringOutOfBoundsException.
Comment 4 Helen Beeken CLA 2005-12-07 09:59:49 EST
Created attachment 31298 [details]
improved testcase patch

Apply this patch to the tests project.

This testcase replaces the previously attached one as it checks for the advice not applied anywhere warning.
Comment 5 Helen Beeken CLA 2005-12-07 10:01:27 EST
Created attachment 31299 [details]
patch containing fix

Apply to the weaver project.

The fix is to use the erasureSignature rather than the signature when getting the outermost type. The erasureSignature doesn't know about "P"'s and "<"'s and works with the raw type.
Comment 6 Andrew Clement CLA 2005-12-07 12:21:29 EST
fixes checked in.
Comment 7 Andrew Clement CLA 2005-12-08 03:55:43 EST
fix available