Bug 509259 - workaround ReferenceTypeImpl fMethodTable lazy initialization race

Change-Id: I6792837ee747b14ff9f3f6a3a46f7f2405c40627
Signed-off-by: Igor Fedorenko <igor@ifedorenko.com>
diff --git a/org.eclipse.jdt.debug/jdi/org/eclipse/jdi/internal/ReferenceTypeImpl.java b/org.eclipse.jdt.debug/jdi/org/eclipse/jdi/internal/ReferenceTypeImpl.java
index 5673199..c5bff07 100644
--- a/org.eclipse.jdt.debug/jdi/org/eclipse/jdi/internal/ReferenceTypeImpl.java
+++ b/org.eclipse.jdt.debug/jdi/org/eclipse/jdi/internal/ReferenceTypeImpl.java
@@ -21,7 +21,6 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -356,7 +355,7 @@
 	/** The following are the stored results of JDWP calls. */
 	protected List<InterfaceType> fInterfaces = null;
 	private List<Method> fMethods = null;
-	private Hashtable<JdwpMethodID, Method> fMethodTable = null;
+	private Map<JdwpMethodID, Method> fMethodTable = null;
 	private List<Field> fFields = null;
 	private List<Method> fAllMethods = null;
 	private List<Method> fVisibleMethods = null;
@@ -522,8 +521,9 @@
 	 */
 	@Override
 	public int modifiers() {
-		if (fModifierBits != -1)
+		if (fModifierBits != -1) {
 			return fModifierBits;
+		}
 
 		initJdwpRequest();
 		try {
@@ -564,8 +564,9 @@
 	 */
 	@Override
 	public List<Method> visibleMethods() {
-		if (fVisibleMethods != null)
+		if (fVisibleMethods != null) {
 			return fVisibleMethods;
+		}
 
 		/*
 		 * Recursion: The methods of its own (own methods() command); All
@@ -596,9 +597,10 @@
 		// If it is a class, all methods of it's superclass.
 		if (this instanceof ClassType) {
 			ClassType superclass = ((ClassType) this).superclass();
-			if (superclass != null)
+			if (superclass != null) {
 				addVisibleMethods(superclass.visibleMethods(),
 						namesAndSignatures, visibleMethods);
+			}
 		}
 
 		fVisibleMethods = visibleMethods;
@@ -612,8 +614,9 @@
 	 */
 	@Override
 	public List<Method> allMethods() {
-		if (fAllMethods != null)
+		if (fAllMethods != null) {
 			return fAllMethods;
+		}
 
 		/*
 		 * Recursion: The methods of its own (own methods() command); All
@@ -637,8 +640,9 @@
 		// If it is a class, all methods of it's superclass.
 		if (this instanceof ClassType) {
 			ClassType superclass = ((ClassType) this).superclass();
-			if (superclass != null)
+			if (superclass != null) {
 				resultSet.addAll(superclass.allMethods());
+			}
 		}
 
 		fAllMethods = new ArrayList<>(resultSet);
@@ -710,8 +714,9 @@
 	 */
 	@Override
 	public List<Field> visibleFields() {
-		if (fVisibleFields != null)
+		if (fVisibleFields != null) {
 			return fVisibleFields;
+		}
 
 		/*
 		 * Recursion: The fields of its own (own fields() command); All fields
@@ -737,9 +742,10 @@
 		// If it is a class, all fields of it's superclass.
 		if (this instanceof ClassType) {
 			ClassType superclass = ((ClassType) this).superclass();
-			if (superclass != null)
+			if (superclass != null) {
 				addVisibleFields(superclass.visibleFields(), fieldNames,
 						visibleFields);
+			}
 		}
 
 		fVisibleFields = visibleFields;
@@ -753,8 +759,9 @@
 	 */
 	@Override
 	public List<Field> allFields() {
-		if (fAllFields != null)
+		if (fAllFields != null) {
 			return fAllFields;
+		}
 
 		/*
 		 * Recursion: The fields of its own (own fields() command); All fields
@@ -779,8 +786,9 @@
 		// If it is a class, all fields of it's superclass.
 		if (this instanceof ClassType) {
 			ClassType superclass = ((ClassType) this).superclass();
-			if (superclass != null)
+			if (superclass != null) {
 				resultSet.addAll(superclass.allFields());
+			}
 		}
 
 		fAllFields = new ArrayList<>(resultSet);
@@ -793,8 +801,9 @@
 	 */
 	@Override
 	public ClassLoaderReference classLoader() {
-		if (fClassLoader != null)
+		if (fClassLoader != null) {
 			return fClassLoader;
+		}
 
 		initJdwpRequest();
 		try {
@@ -818,8 +827,9 @@
 	 */
 	@Override
 	public ClassObjectReference classObject() {
-		if (fClassObject != null)
+		if (fClassObject != null) {
 			return fClassObject;
+		}
 
 		initJdwpRequest();
 		try {
@@ -898,8 +908,9 @@
 		Iterator<Field> iter = visibleFields().iterator();
 		while (iter.hasNext()) {
 			FieldImpl field = (FieldImpl) iter.next();
-			if (field.name().equals(name))
+			if (field.name().equals(name)) {
 				return field;
+			}
 		}
 		return null;
 	}
@@ -953,8 +964,9 @@
 		Iterator<Field> iter = fields().iterator();
 		while (iter.hasNext()) {
 			FieldImpl field = (FieldImpl) iter.next();
-			if (field.getFieldID().equals(fieldID))
+			if (field.getFieldID().equals(fieldID)) {
 				return field;
+			}
 		}
 		return null;
 	}
@@ -970,12 +982,14 @@
 					"", null, -1); //$NON-NLS-1$
 		}
 		if (fMethodTable == null) {
-			fMethodTable = new Hashtable<>();
+			// 509259 use temporary variable to workaround fMethodTable lazy initialization race
+			Map<JdwpMethodID, Method> methodTable = new HashMap<>();
 			Iterator<Method> iter = methods().iterator();
 			while (iter.hasNext()) {
 				MethodImpl method = (MethodImpl) iter.next();
-				fMethodTable.put(method.getMethodID(), method);
+				methodTable.put(method.getMethodID(), method);
 			}
+			fMethodTable = Collections.unmodifiableMap(methodTable);
 		}
 		return fMethodTable.get(methodID);
 	}
@@ -1020,9 +1034,10 @@
 			DataInputStream replyData = replyPacket.dataInStream();
 			HashMap<Field, Value> map = new HashMap<>();
 			int nrOfElements = readInt("elements", replyData); //$NON-NLS-1$
-			if (nrOfElements != fieldsSize)
+			if (nrOfElements != fieldsSize) {
 				throw new InternalError(
 						JDIMessages.ReferenceTypeImpl_Retrieved_a_different_number_of_values_from_the_VM_than_requested_3);
+			}
 
 			for (int i = 0; i < nrOfElements; i++) {
 				map.put(fields.get(i), ValueImpl.readWithTag(this, replyData));
@@ -1066,8 +1081,9 @@
 	 */
 	@Override
 	public int compareTo(ReferenceType type) {
-		if (type == null || !type.getClass().equals(this.getClass()))
+		if (type == null || !type.getClass().equals(this.getClass())) {
 			throw new ClassCastException(JDIMessages.ReferenceTypeImpl_Can__t_compare_reference_type_to_given_object_4);
+		}
 		return name().compareTo(type.name());
 	}
 
@@ -1111,8 +1127,9 @@
 	public List<Method> methods() {
 		// Note that ArrayReference overwrites this method by returning an empty
 		// list.
-		if (fMethods != null)
+		if (fMethods != null) {
 			return fMethods;
+		}
 
 		// Note: Methods are returned in the order they occur in the class file,
 		// therefore their
@@ -1263,8 +1280,9 @@
 	@Override
 	public int getClassFileVersion() {
 		virtualMachineImpl().checkHCRSupported();
-		if (fGotClassFileVersion)
+		if (fGotClassFileVersion) {
 			return fClassFileVersion;
+		}
 
 		initJdwpRequest();
 		try {
@@ -1310,8 +1328,10 @@
 			throws IOException {
 		fReferenceTypeID.write(out);
 		if (target.fVerboseWriter != null)
+		 {
 			target.fVerboseWriter.println(
 					"referenceType", fReferenceTypeID.value()); //$NON-NLS-1$
+		}
 	}
 
 	/**
@@ -1324,7 +1344,9 @@
 				target.virtualMachineImpl());
 		ID.write(out);
 		if (target.fVerboseWriter != null)
+		 {
 			target.fVerboseWriter.println("referenceType", ID.value()); //$NON-NLS-1$
+		}
 	}
 
 	/**
@@ -2367,8 +2389,9 @@
 	 */
 	protected int optionsToJdwpOptions(int options) {
 		int jdwpOptions = 0;
-		if ((options & ClassType.INVOKE_SINGLE_THREADED) != 0)
+		if ((options & ClassType.INVOKE_SINGLE_THREADED) != 0) {
 			jdwpOptions |= MethodImpl.INVOKE_SINGLE_THREADED_JDWP;
+		}
 		return jdwpOptions;
 	}
 
@@ -2396,15 +2419,18 @@
 		MethodImpl methodImpl = (MethodImpl) method;
 
 		// Perform some checks for IllegalArgumentException.
-		if (!visibleMethods().contains(method))
+		if (!visibleMethods().contains(method)) {
 			throw new IllegalArgumentException(
 					JDIMessages.ClassTypeImpl_Class_does_not_contain_given_method_1);
-		if (method.argumentTypeNames().size() != arguments.size())
+		}
+		if (method.argumentTypeNames().size() != arguments.size()) {
 			throw new IllegalArgumentException(
 					JDIMessages.ClassTypeImpl_Number_of_arguments_doesn__t_match_2);
-		if (method.isConstructor() || method.isStaticInitializer())
+		}
+		if (method.isConstructor() || method.isStaticInitializer()) {
 			throw new IllegalArgumentException(
 					JDIMessages.ClassTypeImpl_Method_is_constructor_or_intitializer_3);
+		}
 
 		// check the type and the VM of the arguments. Convert the values if
 		// needed
@@ -2454,8 +2480,9 @@
 			ValueImpl value = ValueImpl.readWithTag(this, replyData);
 			ObjectReferenceImpl exception = ObjectReferenceImpl
 					.readObjectRefWithTag(this, replyData);
-			if (exception != null)
+			if (exception != null) {
 				throw new InvocationException(exception);
+			}
 			return value;
 		} catch (IOException e) {
 			defaultIOExceptionHandler(e);