Bug 498925: NullPointerException in Config.delegateGetMethodBodies
- assumed fix in 2 parts
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/Compiler.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/Compiler.java
index 7de8fdf..2863b92 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/Compiler.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/Compiler.java
@@ -433,6 +433,8 @@
 				this.totalUnits);
 //{ObjectTeams: record 'place' for external invocation of getMethodBodies:
         parsedUnit.place = this.totalUnits;
+        if (parsedUnit.scope != null)
+        	parsedUnit.scope.parser = this.parser;
 // SH}
 		this.unitsToProcess[this.totalUnits++] = parsedUnit;
 	}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/CompilationUnitScope.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/CompilationUnitScope.java
index f965ff6..c6e2a02 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/CompilationUnitScope.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/CompilationUnitScope.java
@@ -96,6 +96,8 @@
 	private ArrayList<Invocation> inferredInvocations;
 //{ObjectTeams: when used as a baseimport scope, remember the original scope during this current lookup
 	public Scope originalScope;
+	// store parser for on-demand Dependencies.setup() lateron:
+	public Parser parser;
 // SH}
 
 public CompilationUnitScope(CompilationUnitDeclaration unit, LookupEnvironment environment) {
@@ -902,7 +904,7 @@
   boolean createdConfig = false;
   if (!Config.hasLookupEnvironment()) {
 	  createdConfig = true;
-	  Dependencies.setup(this, null, environment(), false, true);
+	  Dependencies.setup(this, this.parser, environment(), false, true);
   }
   try {
 //SH}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java
index bc58cff..503776a 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java
@@ -3736,7 +3736,7 @@
 	  boolean createdConfig = false;
 	  if (!Config.hasLookupEnvironment()) {
 		  createdConfig = true;
-		  Dependencies.setup(this, null, environment(), false, true);
+		  Dependencies.setup(this, compilationUnitScope().parser, environment(), false, true);
 	  }
 	  try {
 // SH}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/control/Config.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/control/Config.java
index 046d47b..93b2dc4 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/control/Config.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/control/Config.java
@@ -20,7 +20,9 @@
  **********************************************************************/
 package org.eclipse.objectteams.otdt.internal.core.compiler.control;
 
+import java.lang.ref.WeakReference;
 import java.util.Stack;
+import java.util.WeakHashMap;
 
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Status;
@@ -62,7 +64,7 @@
 		}
 	}
 
-    Object            client;
+    WeakReference<Object> client;
     Parser            parser;
     Parser            plainParser; // alternate parser when client is a MatchLocator
     LookupEnvironment lookupEnvironment;
@@ -116,6 +118,8 @@
 	// (entries are removed explicitly using release)
 	private static final ThreadLocal<Stack<Config>> _configs = new ThreadLocal<Stack<Config>>();
 
+	static final WeakHashMap<Object, Config> configsByClient = new WeakHashMap<>();
+
 	public static void addConfig(Config config)
 	{
 	    synchronized (_configs) {
@@ -134,6 +138,8 @@
 
 	        configStack.push(config);
 	    }
+	    if (config.client != null && config.client.get() != null)
+	    	configsByClient.put(config.client.get(), config);
 	}
 
 	/**
@@ -157,7 +163,7 @@
 		        _configs.set(configStack);
 
 		        Config config = new Config();
-		    	config.client = client;
+		    	config.client = new WeakReference<>(client);
 		    	configStack.push(config);
 		    	return null; // no old config
 		    } else {
@@ -167,6 +173,7 @@
 		    	clone.castRequired = existing.castRequired;
 		    	clone.loweringRequired = existing.loweringRequired;
 		    	clone.loweringPossible = existing.loweringPossible;
+		    	clone.client = new WeakReference<>(null);
 		    	existing.castRequired = null;
 		    	existing.loweringRequired = false;
 		    	existing.loweringPossible = false;
@@ -209,7 +216,8 @@
     	    {
     	        Config config = configStack.pop(); // remove Config
     		    assert(config != null);
-    	        if (config.client != client && config.client != null) // bad balance of addConfig and removeConfig calls
+    		    Object theClient = config.client.get();
+    	        if (theClient != client && theClient != null) // bad balance of addConfig and removeConfig calls
     	        {
     	            assert(false);
     	            configStack.push(config); // be defensive, put it back
@@ -395,12 +403,13 @@
 	public static void delegateGetMethodBodies(CompilationUnitDeclaration unit) {
 		Config config = getConfig();
 		Parser parser = config.parser();
-		if (config.client instanceof ITypeRequestor) {
+		Object theClient = config.client.get();
+		if (theClient instanceof ITypeRequestor) {
 			// MatchLocator.getMethodBodies and MatchLocatorParser.getMethodBodies
 			// both contribute to locating matches. Unit parser on behalf of
 			// Dependencies should however be parsed using a plain Parser:
 			if (config.plainParser == null)
-				config.plainParser = ((ITypeRequestor)config.client).getPlainParser();
+				config.plainParser = ((ITypeRequestor)theClient).getPlainParser();
 			if (config.plainParser != null)
 				parser = config.plainParser;
 		}
@@ -433,14 +442,16 @@
 	 */
 	public static boolean clientIsCompiler() {
 		Config config = getConfig();
-		return (config != null && config.client instanceof Compiler);
+		return (config != null && config.client.get() instanceof Compiler);
 	}
 
 	public static boolean clientIsBatchCompiler() {
 		Config config = safeGetConfig();
-		return (   config != null
-				&& config.client instanceof Compiler
-				&& ((Compiler)config.client).isBatchCompiler);
+		if (config == null)
+			return false;
+		Object client = config.client.get();
+		return (   client instanceof Compiler
+				&& ((Compiler)client).isBatchCompiler);
 	}
 
 	/**
@@ -474,4 +485,9 @@
 			exception.printStackTrace(System.err);
 	}
 
+	public boolean clientHasExactClass(Class<?> clazz) {
+		Object theClient = this.client.get();
+		return theClient != null && theClient.getClass() == clazz;
+	}
+
 }
\ No newline at end of file
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/control/Dependencies.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/control/Dependencies.java
index f5fa74b..94e13ab 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/control/Dependencies.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/control/Dependencies.java
@@ -20,8 +20,10 @@
 package org.eclipse.objectteams.otdt.internal.core.compiler.control;
 
 
+import java.lang.ref.WeakReference;
 import java.util.Iterator;
 
+import org.eclipse.jdt.internal.compiler.Compiler;
 import org.eclipse.jdt.internal.compiler.ast.ASTNode;
 import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
 import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
@@ -127,8 +129,11 @@
 			boolean           bundledCompleteTypeBindings,
 			boolean           strictDiet)
     {
-    	Config config = new Config();
-    	config.client = client;
+    	Config config = Config.configsByClient.get(client);
+    	if (config != null && (config.parser != null || parser == null))
+    		return config;
+    	config = new Config();
+    	config.client = new WeakReference<>(client);
     	config.parser = parser;
     	config.lookupEnvironment = lookupEnvironment;
     	config.verifyMethods = verifyMethods;
@@ -1108,7 +1113,7 @@
 		for (int j = 0; j < unit.types.length; j++) {
 			if (unit.types[j].isRole()) {
 				if (unit.types.length != 1) {
-					if (Config.getConfig().client.getClass() == Compiler.class) // exact match.
+					if (Config.getConfig().clientHasExactClass(Compiler.class))
 					{
 						environment.problemReporter.roleFileMustDeclareOneType(unit);
 						return;
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/DefaultBindingResolver.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/DefaultBindingResolver.java
index c83304e..6cf13a0 100644
--- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/DefaultBindingResolver.java
+++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/DefaultBindingResolver.java
@@ -818,7 +818,7 @@
 					BlockScope blockScope = (BlockScope) this.astNodesToBlockScope.get(expression);
 					if (blockScope != null) {
 //{ObjectTeams: calling into the compiler needs dependencies configured:
-					  Dependencies.setup(this, null, lookupEnvironment(), false, false, false, true, true, false);
+					  Dependencies.setup(this, blockScope.compilationUnitScope().parser, lookupEnvironment(), false, false, false, true, true, false);
 					  try {
 // orig:
 						return this.getTypeBinding(thisReference.resolveType(blockScope));
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/TypeBinding.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/TypeBinding.java
index a869e24..2b4efd1 100644
--- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/TypeBinding.java
+++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/TypeBinding.java
@@ -41,6 +41,7 @@
 import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
 import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
 import org.eclipse.jdt.internal.compiler.lookup.WildcardBinding;
+import org.eclipse.jdt.internal.compiler.parser.Parser;
 import org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
 import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
 import org.eclipse.jdt.internal.core.JavaElement;
@@ -133,18 +134,36 @@
 		}
 		if (refType != null) {
 //{ObjectTeams: calling getAnnotations() requires Dependencies control:
-		  Dependencies.setup(this, null, this.resolver.lookupEnvironment(), true, true); // TODO(SH): parser, flags?
+		  setupDependencies();
 		  try {
 // orig:
 			return this.annotations = resolveAnnotationBindings(refType.getAnnotations(), false);
 // :giro
 		  } finally {
-			Dependencies.release(this);
+			Dependencies.release(this.resolver);
 		  }
 // SH}
 		}
 		return this.annotations = AnnotationBinding.NoAnnotations;
 	}
+
+//{ObjectTeams: utilities for methods needing configured Dependencies:
+	void setupDependencies() {
+		setupDependencies(true, true, true, true, false, true);
+	}
+
+	Config setupDependencies(boolean	          verifyMethods,
+			boolean           analyzeCode,
+			boolean           generateCode,
+			boolean           buildFieldsAndMethods,
+			boolean           bundledCompleteTypeBindings,
+			boolean           strictDiet) {
+		Parser parser = (this.resolver.scope() != null) ? this.resolver.scope().parser : null;
+		return Dependencies.setup(this.resolver, parser, this.resolver.lookupEnvironment(),
+				verifyMethods, analyzeCode, generateCode, buildFieldsAndMethods, bundledCompleteTypeBindings, strictDiet);
+	}
+// SH}
+
 	private IAnnotationBinding[] resolveAnnotationBindings(org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding[] internalAnnotations, boolean isTypeUse) {
 		int length = internalAnnotations == null ? 0 : internalAnnotations.length;
 		if (length != 0) {
@@ -284,7 +303,7 @@
 		try {
 			if (isClass() || isInterface() || isEnum()) {
 //{ObjectTeams: calling methods() requires Dependencies control:
-				Dependencies.setup(this, null, this.resolver.lookupEnvironment(), true, true); // TODO(SH): parser, flags?
+				setupDependencies();
 				usesDependencies = true;
 // SH}
 				ReferenceBinding referenceBinding = (ReferenceBinding) this.binding;
@@ -325,7 +344,7 @@
 //{ObjectTeams: cleanup:
 		finally {
 			if (usesDependencies)
-				Dependencies.release(this);
+				Dependencies.release(this.resolver);
 		}
 // SH}
 		return this.fields = NO_VARIABLE_BINDINGS;
@@ -347,7 +366,7 @@
 		try {
 			if (isClass() || isInterface() || isEnum()) {
 //{ObjectTeams: calling methods() requires Dependencies control:
-				Dependencies.setup(this, null, this.resolver.lookupEnvironment(), true, true); // TODO(SH): parser, flags?
+				setupDependencies();
 				usesDependencies = true;
 // SH}
 				ReferenceBinding referenceBinding = (ReferenceBinding) this.binding;
@@ -391,7 +410,7 @@
 //{ObjectTeams: cleanup:
 		finally {
 			if (usesDependencies)
-				Dependencies.release(this);
+				Dependencies.release(this.resolver);
 		}
 // SH}
 		return this.methods = NO_METHOD_BINDINGS;
@@ -569,7 +588,7 @@
 //{ObjectTeams: protect with minimally configured Dependencies:
 		Config cfg = null;
 		if (!Dependencies.isSetup())
-			cfg = Dependencies.setup(this, null, this.resolver.lookupEnvironment(), true, false, false, true, false, true);
+			cfg = setupDependencies(true, false, false, true, false, true);
 	  try {
 // orig:
 		MethodBinding sam = this.binding.getSingleAbstractMethod(scope, true);
@@ -579,7 +598,7 @@
 // :giro
 	  } finally {
 		  if (cfg != null)
-			  Dependencies.release(this);
+			  Dependencies.release(this.resolver);
 	  }
 // SH}
 	}
@@ -604,7 +623,7 @@
 // see Bug 352605 - Eclipse is reporting "Could not retrieve superclass" every few minutes
 		Config cfg = null;
 		if (!Dependencies.isSetup())
-			cfg = Dependencies.setup(this, null, this.resolver.lookupEnvironment(), false, false, false, false, false, true);
+			cfg = setupDependencies(false, false, false, false, false, true);
 	  try {
 // - continued at bottom of method - SH}
 		try {
@@ -671,7 +690,7 @@
 //{ObjectTeams: conclude try-finally from above
 	  } finally {
 		if (cfg != null)
-			Dependencies.release(this);
+			Dependencies.release(this.resolver);
 	  }
 // SH}	
 	}
@@ -1038,14 +1057,14 @@
 // see Bug 352605 - Eclipse is reporting "Could not retrieve superclass" every few minutes
 		  Config cfg = null;
 		  if (!Dependencies.isSetup())
-			cfg = Dependencies.setup(this, null, this.resolver.lookupEnvironment(), false, false, false, false, false, true);
+			cfg = setupDependencies(false, false, false, false, false, true);
 		  try {
 // orig:
 			superclass = ((ReferenceBinding)this.binding).superclass();
 // :giro
 		  } finally {
 			if (cfg != null)
-				Dependencies.release(this);
+				Dependencies.release(this.resolver);
 		  }
 // SH}
 		} catch (RuntimeException e) {
@@ -1480,7 +1499,7 @@
 	    ReferenceBinding roleBinding = (ReferenceBinding) this.binding;
 	    Config cfg = null;
 	    if (!Dependencies.isSetup())
-	    	cfg = Dependencies.setup(this, null, this.resolver.lookupEnvironment(), false, false, false, false, false, true);
+	    	cfg = setupDependencies(false, false, false, false, false, true);
 	    try {
 		    ReferenceBinding baseclass = roleBinding.baseclass();
 		    if (baseclass == null) {
@@ -1489,7 +1508,7 @@
 		    return this.resolver.getTypeBinding(baseclass);
 	    } finally {
 	    	if (cfg != null)
-	    		Dependencies.release(this);
+	    		Dependencies.release(this.resolver);
 	    }
 	}
 //	ira+SH}