Bug 392225 - NPE in TypeBinding.getDeclaredFields
Summary: NPE in TypeBinding.getDeclaredFields
Status: ASSIGNED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.8   Edit
Hardware: PC Mac OS X
: P3 normal (vote)
Target Milestone: ---   Edit
Assignee: JDT-Core-Inbox CLA
QA Contact:
URL:
Whiteboard: stalebug
Keywords:
Depends on: 526520
Blocks:
  Show dependency tree
 
Reported: 2012-10-17 16:34 EDT by Sebastian Zarnekow CLA
Modified: 2022-07-17 06:11 EDT (History)
4 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Sebastian Zarnekow CLA 2012-10-17 16:34:04 EDT
java.lang.NullPointerException
	at org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding.resolveTypeFor(SourceTypeBinding.java:1395)
	at org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding.fields(SourceTypeBinding.java:711)
	at org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding.availableFields(ReferenceBinding.java:176)
	at org.eclipse.jdt.core.dom.TypeBinding.getDeclaredFields(TypeBinding.java:224)
[snip 8< my code below]


In a CompilationParticipant, I obtain the CU for the compiled file by means of 

ICompilationUnit cu = (ICompilationUnit) JavaCore.create(javaFile /* IFile */);
ASTParser parser = ASTParser.newParser(AST.JLS4);
parser.setProject(cu.getJavaProject());
parser.setIgnoreMethodBodies(false);
IType[] allTypes = getAllTypes(cu);
IBinding[] bindings = parser.createBindings(allTypes, null);

with

private IType[] getAllTypes(ICompilationUnit compilationUnit) throws JavaModelException {
	List<IType> result = new ArrayList<IType>(4);
	for (IType type : compilationUnit.getTypes()) {
		result.add(type);
		addAllMemberTypes(type, result);
	}
	return result.toArray(new IType[result.size()]);
}

private void addAllMemberTypes(IMember container, List<IType> result) throws JavaModelException {
	for (IJavaElement child : container.getChildren()) {
		if (child instanceof IMember) {
			if (child instanceof IType) {
				result.add((IType) child);
			}
			addAllMemberTypes((IMember) child, result);
		}
	}
}

For the following two files in the default package of an IJavaProject ..

==
public class Outer {
	public class Inner extends SuperClass {
		public Inner(String s) {
		}
	}
}
==
public class SuperClass {
	String f;
}
==

.. I can correctly find two ITypeBindings if Outer.java is compiled by my participant. I get a binding for Outer and another one for Outer$Inner. 
As soon as I try to navigate to the supertype of Inner and query 'SuperClass' for its fields, I get the exception above. Is there something that I have to keep in mind when dealing with the parser and stuff in a CompilationParticipant?
Comment 1 Stephan Herrmann CLA 2012-10-17 19:18:01 EDT
Preliminary answer from locking at your snippets and the implementation in SourceTypeBinding:

- while compiling Outer we've touched SuperClass to the degree that we created the type binding and connected it (supers, members), we haven't yet fully resolved 'f' (it was not requested).
- at the end of this compilation SourceTypeBinding.scope is discarded
- when later outside the compilation proper you navigate to SuperClass and ask for its 'fields()', each of these has to resolve its type which it does using the scope -> NPE

Do we really really need the scope? Without it we
1. cannot find the compiler options to determine the sourceLevel
2. cannot find the corresponding TypeDeclaration
3. report any errors using the typical scope.problemReporter() idiom.

1&3 could be circumvented somehow, but for 2 I see no alternative.

I don't think the API makes any promises about referenced bindings which are not *contained* in allTypes.
Comment 2 Sebastian Zarnekow CLA 2012-10-18 06:12:51 EDT
(In reply to comment #1)
> I don't think the API makes any promises about referenced bindings which are
> not *contained* in allTypes.


That's suprising. IMHO it would not make much sense to restrict the resolution of bindings to the initially requested data. This would raise the question: How would I know which bindings are valid and for which of those transitive bindings do I have to re-request the resolution. Is there even a method that tells me about the internal state of a binding, e.g. if it's valid / resolved or not? Can I somehow trigger further resolution?
Comment 3 Stephan Herrmann CLA 2012-10-18 09:37:42 EDT
Disclaimer: I did not invent any of these API, I'm only interpreting what I see.

(In reply to comment #2)
> (In reply to comment #1)
> > I don't think the API makes any promises about referenced bindings which are
> > not *contained* in allTypes.
> 
> 
> That's suprising. IMHO it would not make much sense to restrict the
> resolution of bindings to the initially requested data.

I can understand your surprise from a client's p.o.v., but from an implementation p.o.v. guaranteeing fully resolved everything would mean the ASTParser can only convert entire universes, never just one class at a time.

Mind you: the primary responsibility of the ASTParser is to create DOM AST, which it does completely. Providing bindings as a side-effect is - a welcome *side-effect*.

> This would raise the
> question: How would I know which bindings are valid and for which of those
> transitive bindings do I have to re-request the resolution.

Very good question.

I believe the following can be taken as a rule of thumb:
- you get bindings for all directly referenced types plus their supers
- members of your supers will have bindings, specifically:
  - signatures of methods are resolved
  - field types are not guaranteed (as you are experiencing)
  - types used in method bodies are not guaranteed
- members of referenced types not in your set of compilation units
  can be unresolved.

> Is there even a
> method that tells me about the internal state of a binding, e.g. if it's
> valid / resolved or not? Can I somehow trigger further resolution?

Hm, for the compiler bindings we have the Unresolved* variants. Right now I don't know any public API that would expose this info, although I agree there should be some.
Comment 4 Stephan Herrmann CLA 2012-10-18 09:50:27 EDT
FYI: bug 392333 :)

With that, do you agree to close this as WORKSFORME, since your trying things that are not guaranteed by the API?

Would a more specific exception help (making this limitation explicit)?
Comment 5 Sebastian Zarnekow CLA 2012-10-18 09:59:44 EDT
I'd prefer a more meaningful exception instead of the NPE, e.g. a JavaModelException or IllegalStateException with a reasonable message. Is that possible?
Comment 6 Olivier Thomann CLA 2012-10-18 10:08:06 EDT
The exception is simply logged, but it doesn't stop the application.
What would be needed to "fix" this issue is the ability to create scopes on the fly to be able to resolve more bindings. In the past, we use to keep scopes as long as the whole tree was not discarded, but this was holding to too much memory. This is why we now remove all scopes before returning the tree after all bindings have been resolved.
Creating scopes on the fly as its own set of issues that we never had time to work on.
Comment 7 Sebastian Zarnekow CLA 2012-10-18 10:15:02 EDT
You're right, the exception did not surface to the user. My compile participant simply stopped working - silently. It took some time to figure out what was wrong :-( Therefore a more helpful entry in the log is greatly appreciated.
Comment 8 Stephan Herrmann CLA 2012-10-18 13:24:08 EDT
I know the pains of uninformative error messages so let's see what we can do:

- TypeBinding.getDeclaredFields() is API so we cannot add exceptions
  -> we can only log a better message

- SourceTypeBinding, the source of the problem is part of ecj and must not
  depend on any classes from model or other packages
  -> we need to invent a new Exception

- SourceTypeBinding.scope is accessed all over the place, but we might cut
  this down to to a meaningful list like this:
  - getMethods(char[])
  - methods()
  - resolveTypeFor(FieldBinding)
  - resolveTypesFor(MethodBinding)
  - sourceEnd()
  - sourceStart()
  - storedAnnotations(boolean) (?)
This should be the intersection of
- dereferences scope
- could potentially be called from DOM

Srikanth: I'm marking this for review even before I have a patch to ask your opinion on adding specific error handling along the lines in this comment.
Comment 9 Stephan Herrmann CLA 2012-10-18 20:01:55 EDT
(In reply to comment #6)
> ... In the past, we use to keep
> scopes as long as the whole tree was not discarded, but this was holding to
> too much memory. This is why we now remove all scopes before returning the
> tree after all bindings have been resolved.

Does this mean by proposing bug 392333 I'm going in circles, or could this make sense as an off-by-default option?
Comment 10 Stephan Herrmann CLA 2013-08-29 11:01:23 EDT
I found this while skimming through my review requests:

(In reply to comment #8)
> Srikanth: I'm marking this for review even before I have a patch to ask your
> opinion on adding specific error handling along the lines in this comment.
Comment 11 Srikanth Sankaran CLA 2014-10-21 03:03:42 EDT
(In reply to Stephan Herrmann from comment #10)
> I found this while skimming through my review requests:
> 
> (In reply to comment #8)
> > Srikanth: I'm marking this for review even before I have a patch to ask your
> > opinion on adding specific error handling along the lines in this comment.

I am sorry, this somehow slipped my radar and the reminder too. Apologies.

How about not clearing the scopes if there is a compilation participant ?
Comment 12 Srikanth Sankaran CLA 2014-10-21 09:02:16 EDT
I just noticed that during our test runs, org.eclipse.jdt.core.tests.dom.BatchASTCreationTests.test070 throws this
exception to apparently no deleterious end.

java.lang.NullPointerException
	at org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding.resolveTypeFor(SourceTypeBinding.java:1684)
	at org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding.fields(SourceTypeBinding.java:883)
	at org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding.availableFields(ReferenceBinding.java:223)
	at org.eclipse.jdt.core.dom.TypeBinding.getDeclaredFields(TypeBinding.java:244)
	at org.eclipse.jdt.core.tests.dom.BatchASTCreationTests.test070(BatchASTCreationTests.java:1661)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:483)
	at junit.framework.TestCase.runTest(TestCase.java:176)
	at junit.framework.TestCase.runBare(TestCase.java:141)
	at junit.framework.TestResult$1.protect(TestResult.java:122)
	at junit.framework.TestResult.runProtected(TestResult.java:142)
	at junit.framework.TestResult.run(TestResult.java:125)
	at junit.framework.TestCase.run(TestCase.java:129)
	at junit.framework.TestSuite.runTest(TestSuite.java:255)
	at org.eclipse.jdt.core.tests.model.SuiteOfTestCases$Suite.runTest(SuiteOfTestCases.java:104)
	at junit.framework.TestSuite.run(TestSuite.java:250)
	at org.eclipse.jdt.core.tests.model.SuiteOfTestCases$Suite.superRun(SuiteOfTestCases.java:88)
	at org.eclipse.jdt.core.tests.model.SuiteOfTestCases$1.protect(SuiteOfTestCases.java:76)
	at junit.framework.TestResult.runProtected(TestResult.java:142)
	at org.eclipse.jdt.core.tests.model.SuiteOfTestCases$Suite.run(SuiteOfTestCases.java:85)
	at junit.framework.TestSuite.runTest(TestSuite.java:255)
	at junit.framework.TestSuite.run(TestSuite.java:250)
	at junit.framework.TestSuite.runTest(TestSuite.java:255)
	at junit.framework.TestSuite.run(TestSuite.java:250)
	at junit.framework.TestSuite.runTest(TestSuite.java:255)
	at junit.framework.TestSuite.run(TestSuite.java:250)
	at junit.framework.TestSuite.runTest(TestSuite.java:255)
	at junit.framework.TestSuite.run(TestSuite.java:250)
	at org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run(JUnit3TestReference.java:131)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
	at org.eclipse.pde.internal.junit.runtime.RemotePluginTestRunner.main(RemotePluginTestRunner.java:62)
	at org.eclipse.pde.internal.junit.runtime.CoreTestApplication.run(CoreTestApplication.java:23)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:483)
	at org.eclipse.equinox.internal.app.EclipseAppContainer.callMethodWithException(EclipseAppContainer.java:587)
	at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:198)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:134)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:104)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:382)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:236)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:483)
	at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:648)
	at org.eclipse.equinox.launcher.Main.basicRun(Main.java:603)
	at org.eclipse.equinox.launcher.Main.run(Main.java:1465)
	at org.eclipse.equinox.launcher.Main.main(Main.java:1438)
Comment 13 Eclipse Genie CLA 2022-07-16 17:59:07 EDT
This bug hasn't had any activity in quite some time. Maybe the problem got resolved, was a duplicate of something else, or became less pressing for some reason - or maybe it's still relevant but just hasn't been looked at yet.

If you have further information on the current state of the bug, please add it. The information can be, for example, that the problem still occurs, that you still want the feature, that more information is needed, or that the bug is (for whatever reason) no longer relevant.

--
The automated Eclipse Genie.
Comment 14 Stephan Herrmann CLA 2022-07-17 06:11:06 EDT
Significant work in this area had started in bug 150657, where I dropped the ball two years ago.