Bug 140191 - NPE in ClassFileReader.getSourceName logs full CU source
Summary: NPE in ClassFileReader.getSourceName logs full CU source
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.2   Edit
Hardware: PC Windows XP
: P3 major (vote)
Target Milestone: 3.3 M4   Edit
Assignee: Kent Johnson CLA
QA Contact:
URL:
Whiteboard:
Keywords:
: 166076 (view as bug list)
Depends on:
Blocks:
 
Reported: 2006-05-04 12:43 EDT by Markus Keller CLA
Modified: 2008-10-09 09:56 EDT (History)
4 users (show)

See Also:


Attachments
Backport of the fix to 3.2.2 (1.83 KB, patch)
2008-10-09 09:56 EDT, Krzysztof Daniel CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Markus Keller CLA 2006-05-04 12:43:30 EDT
I20060504-0920

I just started up the build, imported all required plugins as binary and let autobuild rebuild the workspace.

Got the NPE below in the log, with a very long problem message, starting with this: ([..] are my omissions):

Exception occurred during problem detection:----------------------------------- SOURCE BEGIN -------------------------------------
/*******************************************************************************
* Copyright (c) 2000, 2006 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
[..]
package org.eclipse.jdt.ui;
[..]
public final class JavaUI {
[.. full CU source ..]

java.lang.NullPointerException
at org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader.getSourceName(ClassFileReader.java:567)
at org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding.<init>(BinaryTypeBinding.java:162)
at org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment.createBinaryTypeFrom(LookupEnvironment.java:561)
at org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment.createBinaryTypeFrom(LookupEnvironment.java:558)
at org.eclipse.jdt.internal.compiler.Compiler.accept(Compiler.java:272)
at org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment.askForType(LookupEnvironment.java:102)
at org.eclipse.jdt.internal.compiler.lookup.UnresolvedReferenceBinding.resolve(UnresolvedReferenceBinding.java:43)
at org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding.resolveUnresolvedType(BinaryTypeBinding.java:138)
at org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding.superclass(BinaryTypeBinding.java:907)
at org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding.implementsInterface(ReferenceBinding.java:808)
at org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding.isCompatibleWith0(ReferenceBinding.java:937)
at org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding.isCompatibleWith(ReferenceBinding.java:890)
at org.eclipse.jdt.internal.compiler.ast.Assignment.resolveType(Assignment.java:196)
at org.eclipse.jdt.internal.compiler.ast.Expression.resolve(Expression.java:880)
at org.eclipse.jdt.internal.compiler.ast.Block.resolve(Block.java:101)
at org.eclipse.jdt.internal.compiler.ast.IfStatement.resolve(IfStatement.java:236)
at org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration.resolveStatements(AbstractMethodDeclaration.java:432)
at org.eclipse.jdt.internal.compiler.ast.MethodDeclaration.resolveStatements(MethodDeclaration.java:179)
at org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration.resolve(AbstractMethodDeclaration.java:403)
at org.eclipse.jdt.internal.compiler.ast.TypeDeclaration.resolve(TypeDeclaration.java:1054)
at org.eclipse.jdt.internal.compiler.ast.TypeDeclaration.resolve(TypeDeclaration.java:1101)
at org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration.resolve(CompilationUnitDeclaration.java:353)
at org.eclipse.jdt.internal.compiler.Compiler.resolve(Compiler.java:650)
at org.eclipse.jdt.internal.compiler.Compiler.resolve(Compiler.java:689)
at org.eclipse.jdt.internal.core.CompilationUnitProblemFinder.process(CompilationUnitProblemFinder.java:176)
at org.eclipse.jdt.internal.core.CompilationUnitProblemFinder.process(CompilationUnitProblemFinder.java:248)
at org.eclipse.jdt.internal.core.ReconcileWorkingCopyOperation.makeConsistent(ReconcileWorkingCopyOperation.java:152)
at org.eclipse.jdt.internal.core.ReconcileWorkingCopyOperation.executeOperation(ReconcileWorkingCopyOperation.java:71)
at org.eclipse.jdt.internal.core.JavaModelOperation.run(JavaModelOperation.java:720)
at org.eclipse.jdt.internal.core.JavaModelOperation.runOperation(JavaModelOperation.java:779)
at org.eclipse.jdt.internal.core.CompilationUnit.reconcile(CompilationUnit.java:1123)
at org.eclipse.jdt.internal.core.CompilationUnit.reconcile(CompilationUnit.java:1095)
at org.eclipse.jdt.internal.corext.util.JavaModelUtil.reconcile(JavaModelUtil.java:702)
at org.eclipse.jdt.internal.ui.actions.SelectionConverter.getElementAtOffset(SelectionConverter.java:210)
at org.eclipse.jdt.internal.ui.actions.SelectionConverter.getElementAtOffset(SelectionConverter.java:144)
at org.eclipse.jdt.internal.ui.actions.SelectionConverter.getElementAtOffset(SelectionConverter.java:136)
at org.eclipse.jdt.internal.ui.text.JavaElementProvider.getInformation2(JavaElementProvider.java:84)
at org.eclipse.jface.text.information.InformationPresenter.computeInformation(InformationPresenter.java:346)
at org.eclipse.jface.text.AbstractInformationControlManager.doShowInformation(AbstractInformationControlManager.java:821)
at org.eclipse.jface.text.AbstractInformationControlManager.showInformation(AbstractInformationControlManager.java:811)
at org.eclipse.jdt.internal.ui.javaeditor.JavaSourceViewer.doOperation(JavaSourceViewer.java:166)
at org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitEditor$AdaptedSourceViewer.doOperation(CompilationUnitEditor.java:201)
at org.eclipse.ui.texteditor.TextOperationAction$1.run(TextOperationAction.java:131)
at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:67)
at org.eclipse.ui.texteditor.TextOperationAction.run(TextOperationAction.java:129)
at org.eclipse.jface.action.Action.runWithEvent(Action.java:499)
at org.eclipse.ui.commands.ActionHandler.execute(ActionHandler.java:185)
at org.eclipse.ui.internal.handlers.LegacyHandlerWrapper.execute(LegacyHandlerWrapper.java:109)
at org.eclipse.core.commands.Command.executeWithChecks(Command.java:461)
at org.eclipse.core.commands.ParameterizedCommand.executeWithChecks(ParameterizedCommand.java:424)
at org.eclipse.ui.internal.handlers.HandlerService.executeCommand(HandlerService.java:160)
at org.eclipse.ui.internal.keys.WorkbenchKeyboard.executeCommand(WorkbenchKeyboard.java:466)
at org.eclipse.ui.internal.keys.WorkbenchKeyboard.press(WorkbenchKeyboard.java:799)
at org.eclipse.ui.internal.keys.WorkbenchKeyboard.processKeyEvent(WorkbenchKeyboard.java:846)
at org.eclipse.ui.internal.keys.WorkbenchKeyboard.filterKeySequenceBindings(WorkbenchKeyboard.java:564)
at org.eclipse.ui.internal.keys.WorkbenchKeyboard.access$3(WorkbenchKeyboard.java:506)
at org.eclipse.ui.internal.keys.WorkbenchKeyboard$KeyDownFilter.handleEvent(WorkbenchKeyboard.java:122)
at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:66)
at org.eclipse.swt.widgets.Display.filterEvent(Display.java:982)
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:927)
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:952)
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:937)
at org.eclipse.swt.widgets.Widget.sendKeyEvent(Widget.java:965)
at org.eclipse.swt.widgets.Widget.sendKeyEvent(Widget.java:961)
at org.eclipse.swt.widgets.Widget.wmChar(Widget.java:1275)
at org.eclipse.swt.widgets.Control.WM_CHAR(Control.java:3346)
at org.eclipse.swt.widgets.Control.windowProc(Control.java:3246)
at org.eclipse.swt.widgets.Display.windowProc(Display.java:4025)
at org.eclipse.swt.internal.win32.OS.DispatchMessageW(Native Method)
at org.eclipse.swt.internal.win32.OS.DispatchMessage(OS.java:1923)
at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:2966)
at org.eclipse.ui.internal.Workbench.runEventLoop(Workbench.java:1914)
at org.eclipse.ui.internal.Workbench.runUI(Workbench.java:1878)
at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:419)
at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:143)
at org.eclipse.ui.internal.ide.IDEApplication.run(IDEApplication.java:95)
at org.eclipse.core.internal.runtime.PlatformActivator$1.run(PlatformActivator.java:78)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:92)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:68)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:400)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:177)
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:585)
at org.eclipse.core.launcher.Main.invokeFramework(Main.java:336)
at org.eclipse.core.launcher.Main.basicRun(Main.java:280)
at org.eclipse.core.launcher.Main.run(Main.java:977)
at org.eclipse.core.launcher.Main.main(Main.java:952)
Comment 1 Olivier Thomann CLA 2006-05-04 12:46:18 EDT
I will investigate.
Comment 2 Olivier Thomann CLA 2006-05-04 13:16:56 EDT
Closing as WORKSFORME. Since it is not possible to reproduce it and looking at the source code, we don't see how this is possible.
Comment 3 Olivier Thomann CLA 2006-11-28 12:43:38 EST
*** Bug 166076 has been marked as a duplicate of this bug. ***
Comment 4 Olivier Thomann CLA 2006-11-28 13:01:28 EST
Reopening since we need to understand how this can happened.
Comment 5 Markus Keller CLA 2006-11-28 13:26:46 EST
(In reply to bug 166076 comment #3)
Must be a concurrency issue then. The method is not synchronized and can be called from multiple threads (e.g. compiler and type hierarchy resolver). It is possible that one thread sets this.sourceName temporarily to null just before the arraycopy takes place.
Comment 6 Olivier Thomann CLA 2006-11-28 13:58:19 EST
The creation of binding should be synchronized. I don't see why the class file reader would need to be synchronized.
Kent, is this doable?
Comment 7 Kent Johnson CLA 2006-11-28 16:03:35 EST
How is the instance of the ClassFileReader available to multiple threads?

BinaryTypeBindings create & initialize themselves from the ClassFileReader then throw it away.

Is this the source we're talking about on line 567?

  this.sourceName = new char[name.length - start];
>>System.arraycopy(name, start, this.sourceName, 0, this.sourceName.length);

Looks like a bogus VM/jit bug to me.

What VM are you guys using?
Comment 8 Benno Baumgartner CLA 2006-11-29 04:33:31 EST
(In reply to comment #7)
> How is the instance of the ClassFileReader available to multiple threads?
> 
> BinaryTypeBindings create & initialize themselves from the ClassFileReader then
> throw it away.

Are you sure about that? I find it hard to verify that there is no aliasing. i.e. HierarchyResolver.remember stores the object in an array. The object is retrieved in LookupEnvironment.askForType(PackageBinding, char[]) from NameEvironmentAnswer#getBinaryType() which looks shared to me.

> 
> Is this the source we're talking about on line 567?
> 
>   this.sourceName = new char[name.length - start];
> >>System.arraycopy(name, start, this.sourceName, 0, this.sourceName.length);
> 
> Looks like a bogus VM/jit bug to me.
> 
> What VM are you guys using?

java version "1.5.0_08"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_08-b03)
Java HotSpot(TM) Client VM (build 1.5.0_08-b03, mixed mode)

Comment 9 Markus Keller CLA 2006-11-29 18:33:17 EST
(In reply to comment #7)
> How is the instance of the ClassFileReader available to multiple threads?
Easy to see in the debugger:
- debug a cleaned runtime workspace
- create an empty Java project
- open editor for Integer
- set a breakpoint in ClassFileReader on line 554 and restart the runtime
=> debugger hangs in two threads
- resume the thread that is building a type hierarchy
=> now, you have two threads working on the same ClassFileReader, and the NPE can be provoked by "stepwise refinement"

> Is this the source we're talking about on line 567?
Yes
Comment 10 Kent Johnson CLA 2006-11-30 15:39:08 EST
Here are the stack traces from Markus' steps :

The LookupEnvironment & BinaryTypeBinding instances are not the same, but the ClassFileReader instances are.

Thread [Worker-1] (Suspended (breakpoint at line 554 in ClassFileReader))
ClassFileReader.getSourceName() line: 554
BinaryTypeBinding.<init>(PackageBinding, IBinaryType, LookupEnvironment) line: 150
LookupEnvironment.createBinaryTypeFrom(IBinaryType, PackageBinding, boolean, AccessRestriction) line: 560
LookupEnvironment.createBinaryTypeFrom(IBinaryType, PackageBinding, AccessRestriction) line: 557
CompilationUnitResolver(Compiler).accept(IBinaryType, PackageBinding, AccessRestriction) line: 272
LookupEnvironment.askForType(PackageBinding, char[]) line: 127
PackageBinding.getTypeOrPackage(char[]) line: 178
ClassScope(Scope).getTypeOrPackage(char[], int) line: 2317
ClassScope(Scope).getType(char[]) line: 2057
SingleTypeReference.getTypeBinding(Scope) line: 44
SingleTypeReference(TypeReference).resolveType(ClassScope) line: 159
SingleTypeReference(TypeReference).resolveSuperType(ClassScope) line: 110
ClassScope.findSupertype(TypeReference) line: 1106
ClassScope.connectSuperclass() line: 826
ClassScope.connectTypeHierarchy() line: 951
CompilationUnitScope.connectTypeHierarchy() line: 290
LookupEnvironment.completeTypeBindings() line: 197
CompilationUnitResolver(Compiler).beginToCompile(ICompilationUnit[]) line: 387
CompilationUnitResolver.resolve(CompilationUnitDeclaration, ICompilationUnit, NodeSearcher, boolean, boolean, boolean) line: 836
CompilationUnitResolver.resolve(ICompilationUnit, IJavaProject, NodeSearcher, Map, WorkingCopyOwner, boolean, ...) line: 510
ASTParser.internalCreateAST(IProgressMonitor) line: 835
ASTParser.createAST(IProgressMonitor) line: 628
ASTProvider$1.run() line: 605
SafeRunner.run(ISafeRunnable) line: 37
ASTProvider.createAST(IJavaElement, IProgressMonitor) line: 600
ASTProvider.getAST(IJavaElement, ASTProvider$WAIT_FLAG, IProgressMonitor) line: 514
JavaEditor$8.run(IProgressMonitor) line: 3250
Worker.run() line: 58

Thread [Worker-3] (Suspended (breakpoint at line 554 in ClassFileReader))
ClassFileReader.getSourceName() line: 554
BinaryTypeBinding.<init>(PackageBinding, IBinaryType, LookupEnvironment) line: 150
LookupEnvironment.createBinaryTypeFrom(IBinaryType, PackageBinding, boolean, AccessRestriction) line: 560
LookupEnvironment.createBinaryTypeFrom(IBinaryType, PackageBinding, AccessRestriction) line: 557
HierarchyResolver.accept(IBinaryType, PackageBinding, AccessRestriction) line: 99
LookupEnvironment.askForType(char[][]) line: 101
UnresolvedReferenceBinding.resolve(LookupEnvironment, boolean) line: 43
BinaryTypeBinding.resolveType(ReferenceBinding, LookupEnvironment, boolean) line: 97
BinaryTypeBinding.superclass() line: 909
HierarchyResolver.resolve(IGenericType) line: 528
IndexBasedHierarchyBuilder(HierarchyBuilder).buildSupertypes() line: 115
IndexBasedHierarchyBuilder.build(boolean) line: 134
TypeHierarchy.compute() line: 300
TypeHierarchy.refresh(IProgressMonitor) line: 1237
CreateTypeHierarchyOperation.executeOperation() line: 90
CreateTypeHierarchyOperation(JavaModelOperation).run(IProgressMonitor) line: 720
CreateTypeHierarchyOperation(JavaModelOperation).runOperation(IProgressMonitor) line: 779
BinaryType.newSupertypeHierarchy(WorkingCopyOwner, IProgressMonitor) line: 793
BinaryType.newSupertypeHierarchy(IProgressMonitor) line: 745
SuperTypeHierarchyCache.getTypeHierarchy(IType, IProgressMonitor) line: 120
SuperTypeHierarchyCache.getTypeHierarchy(IType) line: 80
SuperTypeHierarchyCache.getMethodOverrideTester(IType) line: 89
OverrideIndicatorLabelDecorator.getOverrideIndicators(IMethod) line: 162
OverrideIndicatorLabelDecorator.computeAdornmentFlags(Object) line: 129
OverrideIndicatorLabelDecorator.decorate(Object, IDecoration) line: 261
LightweightDecoratorDefinition.decorate(Object, IDecoration) line: 259
LightweightDecoratorManager$LightweightRunnable.run() line: 71
SafeRunner.run(ISafeRunnable) line: 37
Platform.run(ISafeRunnable) line: 850
LightweightDecoratorManager.decorate(Object, DecorationBuilder, LightweightDecoratorDefinition) line: 336
LightweightDecoratorManager.getDecorations(Object, DecorationBuilder) line: 322
DecorationScheduler$1.ensureResultCached(Object, boolean, IDecorationContext) line: 357
DecorationScheduler$1.run(IProgressMonitor) line: 322
Worker.run() line: 58
Comment 11 Kent Johnson CLA 2006-11-30 15:59:49 EST
We 3 choices to fix this problem:

1. Jerome -> change your cache to hold onto the class file bytes instead of the ClassFileReader instances. ClassFileReader was never designed to be thread safe.

2. Or set each accessor that uses lazy initialization to be a synchronized method.

3. Or fix the problem for #getSourceName() by changing it to [and any other accessors]:

public char[] getSourceName() {
  if (this.sourceName != null) 
    return this.sourceName;
  
  char[] name = getInnerSourceName(); // member or local scenario
  if (name == null) {
    name = getName(); // extract from full name
    int start;
    if (isAnonymous()) {
      start = CharOperation.indexOf('$', name, CharOperation.lastIndexOf('/', name) + 1) + 1;      
    } else {
      start = CharOperation.lastIndexOf('/', name) + 1;      
    }
    if (start > 0) {
      name = new char[name.length - start];
      System.arraycopy(name, start, name, 0, name.length);
    }
    this.sourceName = name;
  }
  return this.sourceName;  
}


My preference is #1
Comment 12 Kent Johnson CLA 2006-12-01 13:53:54 EST
The other potential fix is to pre-initialize all lazy fields when fullyInitialize is set to true (which it is in this case).

This would mean the cached ClassFileReader instance would basically be a read-only object.

Any other choices? Any opinions on which one we should do?
Comment 13 Jerome Lanneluc CLA 2006-12-04 11:03:30 EST
As discussed in JDT Core call, we should do option #3, which makes it thread safe and avoids deadlocks.
Comment 14 Kent Johnson CLA 2006-12-04 15:14:39 EST
Changed getSourceName() as described in comment #11.

To reproduce, follow the steps in comment #9.
Comment 15 Kent Johnson CLA 2006-12-04 15:15:38 EST
Released for 3.3 M4 in HEAD stream
Comment 16 David Audel CLA 2006-12-12 11:08:30 EST
Verified for 3.3M4 with I20061212-0010.
Comment 17 Krzysztof Daniel CLA 2008-10-09 09:56:28 EDT
Created attachment 114678 [details]
Backport of the fix to 3.2.2