Leak in Mark Occurrences: deleted classes cause NPE
I just noticed that after I deleted a project (named foo) containing a few sources, the following error started to get logged:
or
This happened every time I opened a source on any other project in my workspace.
java.lang.NullPointerException: underlying.getLocation == null for: L/foo/src/test2/ChildScala.scala
at scala.tools.eclipse.util.EclipseResource.path(EclipseFile.scala:88)
at scala.reflect.internal.util.BatchSourceFile.equals(SourceFile.scala:167)
at scala.tools.eclipse.markoccurrences.ScalaOccurrencesFinder$.scala$tools$eclipse$markoccurrences$ScalaOccurrencesFinder$$getCachedIndex(ScalaOccurrencesFinder.scala:18)
at scala.tools.eclipse.markoccurrences.ScalaOccurrencesFinder$$anonfun$findOccurrences$1$$anonfun$apply$1.apply(ScalaOccurrencesFinder.scala:31)
at scala.tools.eclipse.markoccurrences.ScalaOccurrencesFinder$$anonfun$findOccurrences$1$$anonfun$apply$1.apply(ScalaOccurrencesFinder.scala:29)
at scala.tools.nsc.util.InterruptReq.execute(InterruptReq.scala:26)
at scala.tools.nsc.interactive.Global.pollForWork(Global.scala:331)
at scala.tools.nsc.interactive.PresentationCompilerThread.run(PresentationCompilerThread.scala:22)
or
java.lang.NullPointerException: underlying.getLocation == null for: L/foo/src/test2/ChildScala.scala
at scala.tools.eclipse.util.EclipseResource.path(EclipseFile.scala:88)
at scala.reflect.internal.util.BatchSourceFile.equals(SourceFile.scala:167)
at scala.tools.eclipse.markoccurrences.ScalaOccurrencesFinder$.scala$tools$eclipse$markoccurrences$ScalaOccurrencesFinder$$getCachedIndex(ScalaOccurrencesFinder.scala:18)
at scala.tools.eclipse.markoccurrences.ScalaOccurrencesFinder$$anonfun$findOccurrences$1$$anonfun$apply$1.apply(ScalaOccurrencesFinder.scala:31)
at scala.tools.eclipse.markoccurrences.ScalaOccurrencesFinder$$anonfun$findOccurrences$1$$anonfun$apply$1.apply(ScalaOccurrencesFinder.scala:29)
at scala.tools.nsc.util.InterruptReq.execute(InterruptReq.scala:26)
at scala.tools.nsc.interactive.Global.pollForWork(Global.scala:331)
at scala.tools.nsc.interactive.Global.checkForMoreWork(Global.scala:392)
at scala.tools.nsc.interactive.Global.signalDone(Global.scala:245)
at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5458)
at scala.tools.nsc.typechecker.Typers$Typer.typedType(Typers.scala:5554)
at scala.tools.nsc.typechecker.Typers$Typer.typedType(Typers.scala:5557)
at scala.tools.nsc.typechecker.Typers$Typer.typedDefDef(Typers.scala:2146)
at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5370)
at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5442)
at scala.tools.nsc.typechecker.Typers$Typer.scala$tools$nsc$typechecker$Typers$Typer$$typedStat$1(Typers.scala:2754)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$55.apply(Typers.scala:2854)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$55.apply(Typers.scala:2854)
at scala.collection.immutable.List.loop$1(List.scala:164)
at scala.collection.immutable.List.mapConserve(List.scala:180)
at scala.tools.nsc.typechecker.Typers$Typer.typedStats(Typers.scala:2854)
at scala.tools.nsc.typechecker.Typers$Typer.typedTemplate(Typers.scala:1856)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$27.apply(Typers.scala:1721)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$27.apply(Typers.scala:1721)
at scala.tools.nsc.typechecker.Typers$Typer.typerReportAnyContextErrors(Typers.scala:505)
at scala.tools.nsc.typechecker.Typers$Typer.typedClassDef(Typers.scala:1720)
at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5384)
at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5442)
at scala.tools.nsc.typechecker.Typers$Typer.scala$tools$nsc$typechecker$Typers$Typer$$typedStat$1(Typers.scala:2754)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$55.apply(Typers.scala:2854)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$55.apply(Typers.scala:2854)
at scala.collection.immutable.List.loop$1(List.scala:164)
at scala.collection.immutable.List.mapConserve(List.scala:180)
at scala.tools.nsc.typechecker.Typers$Typer.typedStats(Typers.scala:2854)
at scala.tools.nsc.typechecker.Typers$Typer.typedTemplate(Typers.scala:1856)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$27.apply(Typers.scala:1721)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$27.apply(Typers.scala:1721)
at scala.tools.nsc.typechecker.Typers$Typer.typerReportAnyContextErrors(Typers.scala:505)
at scala.tools.nsc.typechecker.Typers$Typer.typedClassDef(Typers.scala:1720)
at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5384)
at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5442)
at scala.tools.nsc.typechecker.Typers$Typer.scala$tools$nsc$typechecker$Typers$Typer$$typedStat$1(Typers.scala:2754)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$55.apply(Typers.scala:2854)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$55.apply(Typers.scala:2854)
at scala.collection.immutable.List.loop$1(List.scala:164)
at scala.collection.immutable.List.mapConserve(List.scala:180)
at scala.tools.nsc.typechecker.Typers$Typer.typedStats(Typers.scala:2854)
at scala.tools.nsc.typechecker.Typers$Typer.typedPackageDef$1(Typers.scala:5111)
at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5388)
at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5442)
at scala.tools.nsc.typechecker.Typers$Typer.scala$tools$nsc$typechecker$Typers$Typer$$typedStat$1(Typers.scala:2754)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$55.apply(Typers.scala:2854)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$55.apply(Typers.scala:2854)
at scala.collection.immutable.List.loop$1(List.scala:164)
at scala.collection.immutable.List.mapConserve(List.scala:180)
at scala.tools.nsc.typechecker.Typers$Typer.typedStats(Typers.scala:2854)
at scala.tools.nsc.typechecker.Typers$Typer.typedPackageDef$1(Typers.scala:5111)
at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5388)
at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5442)
at scala.tools.nsc.typechecker.Typers$Typer.scala$tools$nsc$typechecker$Typers$Typer$$typedStat$1(Typers.scala:2754)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$55.apply(Typers.scala:2854)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$55.apply(Typers.scala:2854)
at scala.collection.immutable.List.loop$1(List.scala:164)
at scala.collection.immutable.List.mapConserve(List.scala:180)
at scala.tools.nsc.typechecker.Typers$Typer.typedStats(Typers.scala:2854)
at scala.tools.nsc.typechecker.Typers$Typer.typedPackageDef$1(Typers.scala:5111)
at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5388)
at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5442)
at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5493)
at scala.tools.nsc.typechecker.Analyzer$typerFactory$$anon$3.apply(Analyzer.scala:98)
at scala.tools.nsc.Global$GlobalPhase.applyPhase(Global.scala:461)
at scala.tools.nsc.interactive.Global$TyperRun$$anonfun$applyPhase$1.apply$mcV$sp(Global.scala:1085)
at scala.tools.nsc.interactive.Global$TyperRun$$anonfun$applyPhase$1.apply(Global.scala:1085)
at scala.tools.nsc.interactive.Global$TyperRun$$anonfun$applyPhase$1.apply(Global.scala:1085)
at scala.reflect.internal.SymbolTable.atPhase(SymbolTable.scala:201)
at scala.tools.nsc.interactive.Global$TyperRun.applyPhase(Global.scala:1085)
at scala.tools.nsc.interactive.Global$TyperRun.typeCheck(Global.scala:1078)
at scala.tools.nsc.interactive.Global.scala$tools$nsc$interactive$Global$$typeCheck(Global.scala:563)
at scala.tools.nsc.interactive.Global$$anonfun$backgroundCompile$5$$anonfun$apply$7.apply(Global.scala:479)
at scala.tools.nsc.interactive.Global$$anonfun$backgroundCompile$5$$anonfun$apply$7.apply(Global.scala:475)
at scala.Option.foreach(Option.scala:236)
at scala.tools.nsc.interactive.Global$$anonfun$backgroundCompile$5.apply(Global.scala:475)
at scala.tools.nsc.interactive.Global$$anonfun$backgroundCompile$5.apply(Global.scala:475)
at scala.collection.TraversableLike$WithFilter$$anonfun$foreach$1.apply(TraversableLike.scala:778)
at scala.collection.immutable.List.foreach(List.scala:309)
at scala.collection.TraversableLike$WithFilter.foreach(TraversableLike.scala:777)
at scala.tools.nsc.interactive.Global.backgroundCompile(Global.scala:475)
at scala.tools.nsc.interactive.PresentationCompilerThread.run(PresentationCompilerThread.scala:25)
This happened every time I opened a source on any other project in my workspace.
Leave a comment
(In scala-ide:c68b3b7d926324b02fc5826029a9741eb4d2bbc0) Fixes #1001268 - No more NPE in update occurrences when a source is deleted
Deleting a source file could lead to NullPointerException (NPE) in
`ScalaOccurrencesFinder`.
The issue is actually easy to reproduce. Say you have a source `A` and that
`ScalaOccurrencesFinder.findOccurrences` was just ran against it. At this
point, the value of `ScalaOccurrencesFinder.indexCache` holds a reference to
`A`. In this scenario, deleting `A` will cause a NPE to be thrown the next
time `ScalaOccurrencesFinder.findOccurrences` is ran against any other source.
The above described scenario happens because `ScalaOccurrencesFinder` is a
singleton, and `ScalaOccurrencesFinder.getCachedIndex` is used to check for
equality of source files. In the current implementation, checking equality on
source files throws a NPE exception if the resource does no longer exist (have
a look at `EclipseResource.equals`).
The fix simply consists in linking each editor (and hence each source file) to
a different `ScalaOccurrencesFinder` instance. By doing so, the
`ScalaOccurrencesFinder.indexCache` no longer needs to hold a reference to the
`SourceFile`, and hence the NPE can no longer occur.
An additional positive aspect of not using a singleton `ScalaOccurrencesFinder`
is that now the occurrences index (i.e., `MarkOccurrencesIndex`) is no longer
rebuilt while switching among the active editors.
Last, there are a number of refactoring that have been carried out, I'll
mention the three I consider important:
priority. The reasoning is that while the information computed by "update
occurrences" are useful, they are not critical.
`InteractiveCompilationUnit`. The reasoning here is that an editor should
always have a compilation unit attached to it. The only moment when this is
not the case is during the editor initialization, but calling
`getInteractiveCompilationUnit` on a yet not fully initialized editor doesn't
make sense, and hence this should fail fast (i.e., throwing a NPE is a good
way to inform the developer that is doing the wrong thing, returning None is
not as good IMO).
Furtheremore, if you look at the implementations of
`getInteractiveCompilationUnit` in `ScalaSourceFileEditor` and
`ScalaClassFileEditor`, you will see that both implementations already assumed
that a compilation unit is already attached to the editor (the compilation unit
is wrapped in a `Some`, not in an `Option`). Therefore, returning an `Option`
appears like an unneeded overhead, and the current implementation should work
no worse than the former.
2) end-to-end update occurrences action.
Branch: master
Deleting a source file could lead to NullPointerException (NPE) in
`ScalaOccurrencesFinder`.
The issue is actually easy to reproduce. Say you have a source `A` and that
`ScalaOccurrencesFinder.findOccurrences` was just ran against it. At this
point, the value of `ScalaOccurrencesFinder.indexCache` holds a reference to
`A`. In this scenario, deleting `A` will cause a NPE to be thrown the next
time `ScalaOccurrencesFinder.findOccurrences` is ran against any other source.
The above described scenario happens because `ScalaOccurrencesFinder` is a
singleton, and `ScalaOccurrencesFinder.getCachedIndex` is used to check for
equality of source files. In the current implementation, checking equality on
source files throws a NPE exception if the resource does no longer exist (have
a look at `EclipseResource.equals`).
The fix simply consists in linking each editor (and hence each source file) to
a different `ScalaOccurrencesFinder` instance. By doing so, the
`ScalaOccurrencesFinder.indexCache` no longer needs to hold a reference to the
`SourceFile`, and hence the NPE can no longer occur.
An additional positive aspect of not using a singleton `ScalaOccurrencesFinder`
is that now the occurrences index (i.e., `MarkOccurrencesIndex`) is no longer
rebuilt while switching among the active editors.
Last, there are a number of refactoring that have been carried out, I'll
mention the three I consider important:
- The Eclipse `Job` instance used to execute the "update occurrences" task,
priority. The reasoning is that while the information computed by "update
occurrences" are useful, they are not critical.
- The signature of `InteractiveCompilationUnitEditor` used to return an
`InteractiveCompilationUnit`. The reasoning here is that an editor should
always have a compilation unit attached to it. The only moment when this is
not the case is during the editor initialization, but calling
`getInteractiveCompilationUnit` on a yet not fully initialized editor doesn't
make sense, and hence this should fail fast (i.e., throwing a NPE is a good
way to inform the developer that is doing the wrong thing, returning None is
not as good IMO).
Furtheremore, if you look at the implementations of
`getInteractiveCompilationUnit` in `ScalaSourceFileEditor` and
`ScalaClassFileEditor`, you will see that both implementations already assumed
that a compilation unit is already attached to the editor (the compilation unit
is wrapped in a `Some`, not in an `Option`). Therefore, returning an `Option`
appears like an unneeded overhead, and the current implementation should work
no worse than the former.
- Added debug output to know how much time is spent for
2) end-to-end update occurrences action.
Branch: master
on 2012-10-16 02:33 *
By Mirco Dotta
(In scala-ide:c68b3b7d926324b02fc5826029a9741eb4d2bbc0) Fixes #1001268 - No more NPE in update occurrences when a source is deleted
Deleting a source file could lead to NullPointerException (NPE) in
`ScalaOccurrencesFinder`.
The issue is actually easy to reproduce. Say you have a source `A` and that
`ScalaOccurrencesFinder.findOccurrences` was just ran against it. At this
point, the value of `ScalaOccurrencesFinder.indexCache` holds a reference to
`A`. In this scenario, deleting `A` will cause a NPE to be thrown the next
time `ScalaOccurrencesFinder.findOccurrences` is ran against any other source.
The above described scenario happens because `ScalaOccurrencesFinder` is a
singleton, and `ScalaOccurrencesFinder.getCachedIndex` is used to check for
equality of source files. In the current implementation, checking equality on
source files throws a NPE exception if the resource does no longer exist (have
a look at `EclipseResource.equals`).
The fix simply consists in linking each editor (and hence each source file) to
a different `ScalaOccurrencesFinder` instance. By doing so, the
`ScalaOccurrencesFinder.indexCache` no longer needs to hold a reference to the
`SourceFile`, and hence the NPE can no longer occur.
An additional positive aspect of not using a singleton `ScalaOccurrencesFinder`
is that now the occurrences index (i.e., `MarkOccurrencesIndex`) is no longer
rebuilt while switching among the active editors.
Last, there are a number of refactoring that have been carried out, I'll
mention the three I consider important:
priority. The reasoning is that while the information computed by "update
occurrences" are useful, they are not critical.
`InteractiveCompilationUnit`. The reasoning here is that an editor should
always have a compilation unit attached to it. The only moment when this is
not the case is during the editor initialization, but calling
`getInteractiveCompilationUnit` on a yet not fully initialized editor doesn't
make sense, and hence this should fail fast (i.e., throwing a NPE is a good
way to inform the developer that is doing the wrong thing, returning None is
not as good IMO).
Furtheremore, if you look at the implementations of
`getInteractiveCompilationUnit` in `ScalaSourceFileEditor` and
`ScalaClassFileEditor`, you will see that both implementations already assumed
that a compilation unit is already attached to the editor (the compilation unit
is wrapped in a `Some`, not in an `Option`). Therefore, returning an `Option`
appears like an unneeded overhead, and the current implementation should work
no worse than the former.
2) end-to-end update occurrences action.
Branch: platform/juno
Deleting a source file could lead to NullPointerException (NPE) in
`ScalaOccurrencesFinder`.
The issue is actually easy to reproduce. Say you have a source `A` and that
`ScalaOccurrencesFinder.findOccurrences` was just ran against it. At this
point, the value of `ScalaOccurrencesFinder.indexCache` holds a reference to
`A`. In this scenario, deleting `A` will cause a NPE to be thrown the next
time `ScalaOccurrencesFinder.findOccurrences` is ran against any other source.
The above described scenario happens because `ScalaOccurrencesFinder` is a
singleton, and `ScalaOccurrencesFinder.getCachedIndex` is used to check for
equality of source files. In the current implementation, checking equality on
source files throws a NPE exception if the resource does no longer exist (have
a look at `EclipseResource.equals`).
The fix simply consists in linking each editor (and hence each source file) to
a different `ScalaOccurrencesFinder` instance. By doing so, the
`ScalaOccurrencesFinder.indexCache` no longer needs to hold a reference to the
`SourceFile`, and hence the NPE can no longer occur.
An additional positive aspect of not using a singleton `ScalaOccurrencesFinder`
is that now the occurrences index (i.e., `MarkOccurrencesIndex`) is no longer
rebuilt while switching among the active editors.
Last, there are a number of refactoring that have been carried out, I'll
mention the three I consider important:
- The Eclipse `Job` instance used to execute the "update occurrences" task,
priority. The reasoning is that while the information computed by "update
occurrences" are useful, they are not critical.
- The signature of `InteractiveCompilationUnitEditor` used to return an
`InteractiveCompilationUnit`. The reasoning here is that an editor should
always have a compilation unit attached to it. The only moment when this is
not the case is during the editor initialization, but calling
`getInteractiveCompilationUnit` on a yet not fully initialized editor doesn't
make sense, and hence this should fail fast (i.e., throwing a NPE is a good
way to inform the developer that is doing the wrong thing, returning None is
not as good IMO).
Furtheremore, if you look at the implementations of
`getInteractiveCompilationUnit` in `ScalaSourceFileEditor` and
`ScalaClassFileEditor`, you will see that both implementations already assumed
that a compilation unit is already attached to the editor (the compilation unit
is wrapped in a `Some`, not in an `Option`). Therefore, returning an `Option`
appears like an unneeded overhead, and the current implementation should work
no worse than the former.
- Added debug output to know how much time is spent for
2) end-to-end update occurrences action.
Branch: platform/juno