Bug 334119 - AIOOBE in BindingKeyParser.parseInnerType (was: Copy Qualified Name throws ArrayIndexOutOfBoundsException)
Summary: AIOOBE in BindingKeyParser.parseInnerType (was: Copy Qualified Name throws Ar...
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.7   Edit
Hardware: PC Windows XP
: P3 normal (vote)
Target Milestone: 3.7 M5   Edit
Assignee: Ayushman Jain CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-01-12 10:20 EST by Dani Megert CLA
Modified: 2011-01-25 04:04 EST (History)
3 users (show)

See Also:


Attachments
proposed fix (2.44 KB, patch)
2011-01-13 06:50 EST, Ayushman Jain CLA
no flags Details | Diff
proposed fix + regression tests (4.33 KB, patch)
2011-01-20 04:43 EST, Ayushman Jain CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Dani Megert CLA 2011-01-12 10:20:22 EST
I20110111-0800.

Copy From Clipboard throws ArrayIndexOutOfBoundsException.

1. paste this:
package pp;
public class A$ {
	void a() {
	}
}
2. select "a" in the editor
3. Copy > Copy Qualified Name
STRANGE: sometimes nothing at all happens
4. Copy > Copy Qualified Name
still no feedback but:
!ENTRY org.eclipse.ui 4 0 2011-01-12 15:53:48.908
!MESSAGE Unhandled event loop exception
!STACK 0
java.lang.ArrayIndexOutOfBoundsException: 0
	at org.eclipse.jdt.internal.core.util.BindingKeyParser.parseInnerType(BindingKeyParser.java:734)
	at org.eclipse.jdt.internal.core.util.BindingKeyParser.parse(BindingKeyParser.java:608)
	at org.eclipse.jdt.internal.core.util.BindingKeyParser.parse(BindingKeyParser.java:590)
	at org.eclipse.jdt.core.BindingKey.toSignature(BindingKey.java:305)
	at org.eclipse.jdt.internal.ui.viewsupport.JavaElementLabelComposer.appendMethodLabel(JavaElementLabelComposer.java:322)
	at org.eclipse.jdt.internal.ui.viewsupport.JavaElementLabelComposer.appendElementLabel(JavaElementLabelComposer.java:257)
	at org.eclipse.jdt.ui.JavaElementLabels.getElementLabel(JavaElementLabels.java:510)
	at org.eclipse.jdt.ui.JavaElementLabels.getElementLabel(JavaElementLabels.java:483)
	at org.eclipse.jdt.ui.JavaElementLabels.getTextLabel(JavaElementLabels.java:387)
	at org.eclipse.jdt.internal.ui.actions.CopyQualifiedNameAction.getQualifiedName(CopyQualifiedNameAction.java:261)
	at org.eclipse.jdt.internal.ui.actions.CopyQualifiedNameAction.run(CopyQualifiedNameAction.java:193)
	at org.eclipse.jface.action.Action.runWithEvent(Action.java:498)
	at org.eclipse.jface.action.ActionContributionItem.handleWidgetSelection(ActionContributionItem.java:584)
	at org.eclipse.jface.action.ActionContributionItem.access$2(ActionContributionItem.java:501)
	at org.eclipse.jface.action.ActionContributionItem$5.handleEvent(ActionContributionItem.java:411)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:84)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1053)
	at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:4089)
	at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3680)
	at org.eclipse.ui.internal.Workbench.runEventLoop(Workbench.java:2697)
	at org.eclipse.ui.internal.Workbench.runUI(Workbench.java:2661)
	at org.eclipse.ui.internal.Workbench.access$4(Workbench.java:2495)
	at org.eclipse.ui.internal.Workbench$7.run(Workbench.java:674)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:332)
	at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:667)
	at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:149)
	at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:115)
	at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:110)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:79)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:344)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:179)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:592)
	at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:622)
	at org.eclipse.equinox.launcher.Main.basicRun(Main.java:577)
	at org.eclipse.equinox.launcher.Main.run(Main.java:1410)
	at org.eclipse.equinox.launcher.Main.main(Main.java:1386)

!ENTRY org.eclipse.ui 4 0 2011-01-12 15:53:57.346
!MESSAGE Unhandled event loop exception
!STACK 0
java.lang.ArrayIndexOutOfBoundsException: 0
	at org.eclipse.jdt.internal.core.util.BindingKeyParser.parseInnerType(BindingKeyParser.java:734)
	at org.eclipse.jdt.internal.core.util.BindingKeyParser.parse(BindingKeyParser.java:608)
	at org.eclipse.jdt.internal.core.util.BindingKeyParser.parse(BindingKeyParser.java:590)
	at org.eclipse.jdt.core.BindingKey.isParameterizedType(BindingKey.java:279)
	at org.eclipse.jdt.internal.ui.viewsupport.JavaElementLabelComposer.appendTypeLabel(JavaElementLabelComposer.java:855)
	at org.eclipse.jdt.internal.ui.viewsupport.JavaElementLabelComposer.appendElementLabel(JavaElementLabelComposer.java:272)
	at org.eclipse.jdt.ui.JavaElementLabels.getElementLabel(JavaElementLabels.java:510)
	at org.eclipse.jdt.ui.JavaElementLabels.getElementLabel(JavaElementLabels.java:483)
	at org.eclipse.jdt.ui.JavaElementLabels.getTextLabel(JavaElementLabels.java:387)
	at org.eclipse.jdt.internal.ui.actions.CopyQualifiedNameAction.getQualifiedName(CopyQualifiedNameAction.java:261)
	at org.eclipse.jdt.internal.ui.actions.CopyQualifiedNameAction.run(CopyQualifiedNameAction.java:193)
	at org.eclipse.jface.action.Action.runWithEvent(Action.java:498)
	at org.eclipse.jface.action.ActionContributionItem.handleWidgetSelection(ActionContributionItem.java:584)
	at org.eclipse.jface.action.ActionContributionItem.access$2(ActionContributionItem.java:501)
	at org.eclipse.jface.action.ActionContributionItem$5.handleEvent(ActionContributionItem.java:411)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:84)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1053)
	at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:4089)
	at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3680)
	at org.eclipse.ui.internal.Workbench.runEventLoop(Workbench.java:2697)
	at org.eclipse.ui.internal.Workbench.runUI(Workbench.java:2661)
	at org.eclipse.ui.internal.Workbench.access$4(Workbench.java:2495)
	at org.eclipse.ui.internal.Workbench$7.run(Workbench.java:674)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:332)
	at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:667)
	at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:149)
	at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:115)
	at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:110)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:79)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:344)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:179)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:592)
	at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:622)
	at org.eclipse.equinox.launcher.Main.basicRun(Main.java:577)
	at org.eclipse.equinox.launcher.Main.run(Main.java:1410)
	at org.eclipse.equinox.launcher.Main.main(Main.java:1386)
Comment 1 Ayushman Jain CLA 2011-01-13 02:08:05 EST
The problem is that JDT/Core assumes everywhere that a $ in the binding key would imply an innner type, and so a binding key such as A$B is always treated as type B enclosed by type A. Because of this whenever there's actually a $ in the type name, we split the type name at $. 
We should ideally change the representation of binding key for type names containing $ by inserting some escape character (preferably '\'), but this might break a lot of existing things which wouldnt expect a '\' in the binding key.

Specifically in this bug, the problem starts at SourceTypeBinding.computeUniqueKey(boolean). Since here we have the binding we can always query it for binding.isMemberType() and treat the dollar correctly, but then again in BindingKeyParser$Scanner.getNextToken(), we fumble on the dollar!
Comment 2 Ayushman Jain CLA 2011-01-13 02:17:23 EST
(In reply to comment #1)
Just an aferthought, why should java allow specifying a '$' in the name at all. I wish this could be changed. There are other currencies one can use. ;)
Comment 3 Dani Megert CLA 2011-01-13 03:34:10 EST
(In reply to comment #2)
> (In reply to comment #1)
> Just an aferthought, why should java allow specifying a '$' in the name at all.
> I wish this could be changed. There are other currencies one can use. ;)

The name is indeed discouraged (see new wizard).
Comment 4 Deepak Azad CLA 2011-01-13 04:46:53 EST
An extreme case

--------------------------------
class $ {
	class $$ extends ${
		class $$$ {
			
		}
	}
}
-------------------------------

Try Copy qualified name, Type Hierarchy on '$$' etc :)
Comment 5 Stephan Herrmann CLA 2011-01-13 05:46:16 EST
(In reply to comment #2)
> (In reply to comment #1)
> Just an aferthought, why should java allow specifying a '$' in the name at all.
> I wish this could be changed. There are other currencies one can use. ;)

For Java, '$' is just a normal char, it's the encoding of inner types
that's a major PITA introduced in Java 1.1.

Some related bugs: bug 127739, bug 127749, bug 145598.
We'll be adding workarounds for the rest of our lives :-/
Note that bug 127749 isn't resolved as of today.
Comment 6 Ayushman Jain CLA 2011-01-13 06:50:30 EST
Created attachment 186715 [details]
proposed fix

This patch takes care of all the above cases by making sure that the generated key from SourceTypeBinding.getUniqueKey() isn't bad in case of $ occuring in the name.

But this isn't fullproof. There can be other places that still break because of '$'. Like Stephan said, we could be coming across this forever, unless we add an escape character to the binding key. But since this is exposed to the outside world, doing so would break client code.
Comment 7 Ayushman Jain CLA 2011-01-13 07:10:39 EST
(In reply to comment #6)
> But this isn't fullproof.

Even with the patch, the following still throws an exception:

class A$$ {
    void a() {
   }
}

at a completely different place:
java.lang.IllegalArgumentException
	at org.eclipse.jdt.core.Signature.getParameterCount(Signature.java:995)
Comment 8 Ayushman Jain CLA 2011-01-20 04:43:26 EST
Created attachment 187170 [details]
proposed fix + regression tests

Same patch with tests.
Comment 9 Ayushman Jain CLA 2011-01-21 02:17:46 EST
Released in HEAD for 3.7M5
Comment 10 Ayushman Jain CLA 2011-01-21 02:18:18 EST
To verify, use comment 0 and comment 4
Comment 11 Dani Megert CLA 2011-01-25 04:04:32 EST
Verified in I20110124-1800.