Bug 574282 - Simple record fails with Internal compiler error:
java.lang.ArrayIndexOutOfBoundsException

Because we add methods and remove duplicate methods during compilation
we end up having holes(missing some index entries) in the synthetic
method bindings sequence. This causes Out of bounds problems when we
depend on synthetic methods count as new index.

Removal of duplicates got added with Record feature.

To fix two changes have been done
1. Reworked sorting of synthetic methods in syntheticMethods() method.
We are directly placing bindings using index. But with missing bindings
in sequence, we end up having AIOOB issue. This sorting algorithm has
been reworked.
2. When we are creating new sythetic methods we are using count of
existing methods as new index. But with removals this is not correct
anymore. Reworked this max index in the existing synthetic methods + 1.


Conflicts:
	org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/RecordsRestrictedClassTest.java

Change-Id: If370e3219420893a67372509f4d3f99b789decbb
Signed-off-by: Sravan Kumar Lakkimsetti <sravankumarl@in.ibm.com>
Reviewed-on: https://git.eclipse.org/r/c/jdt/eclipse.jdt.core/+/182667
Tested-by: JDT Bot <jdt-bot@eclipse.org>
Reviewed-by: Manoj Palat <manpalat@in.ibm.com>
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/RecordsRestrictedClassTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/RecordsRestrictedClassTest.java
index d4ee4ce..ba6a2bd 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/RecordsRestrictedClassTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/RecordsRestrictedClassTest.java
@@ -8999,6 +8999,7 @@
 			"     9  return\n";
 	RecordsRestrictedClassTest.verifyClassFile(expectedOutput, "X$R.class", ClassFileBytesDisassembler.SYSTEM);
 }
+
 public void testBug574284_001() throws Exception {
 	runConformTest(
 			new String[] {
@@ -9053,4 +9054,29 @@
 			},
 		"0");
 }
+
+public void testBug574282_001() {
+	runConformTest(
+			new String[] {
+					"X.java",
+					"record Rec(String name) {\n" +
+					"\n" +
+					"    Rec() {\n" +
+					"        this(\"\");\n" +
+					"    }\n" +
+					"\n" +
+					"    @Override\n" +
+					"    public boolean equals(Object obj) {\n" +
+					"        return false;\n" +
+					"    }\n" +
+					"}\n" +
+					"public class X {\n"+
+					"  public static void main(String[] args){\n"+
+					"     System.out.println(0);\n" +
+					"  }\n"+
+					"}\n"
+			},
+		"0");
+}
+
 }
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java
index 19caba1..26f9a75 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java
@@ -57,6 +57,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -3312,13 +3313,15 @@
 		}
 	}
 	// sort them in according to their own indexes
-	int length;
-	SyntheticMethodBinding[] sortedBindings = new SyntheticMethodBinding[length = bindings.length];
-	for (int i = 0; i < length; i++){
-		SyntheticMethodBinding binding = bindings[i];
-		sortedBindings[binding.index] = binding;
-	}
-	return sortedBindings;
+	Arrays.sort(bindings, new Comparator<>() {
+		@Override
+		public int compare(SyntheticMethodBinding o1, SyntheticMethodBinding o2) {
+			return o1.index - o2.index;
+		}
+	});
+
+
+	return bindings;
 }
 /**
  * Answer the collection of synthetic fields to append into the classfile
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SyntheticMethodBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SyntheticMethodBinding.java
index 6607741..ad2dfe5 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SyntheticMethodBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SyntheticMethodBinding.java
@@ -87,7 +87,7 @@
 		this.tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved);
 		SourceTypeBinding declaringSourceType = (SourceTypeBinding) declaringClass;
 		SyntheticMethodBinding[] knownAccessMethods = declaringSourceType.syntheticMethods();
-		int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length;
+		int methodId = knownAccessMethods == null ? 0 : knownAccessMethods[knownAccessMethods.length - 1].index + 1; //index may miss some numbers in between. get the highest index and assign next number.;
 		this.index = methodId;
 		this.selector = CharOperation.concat(TypeConstants.SYNTHETIC_ACCESS_METHOD_PREFIX, String.valueOf(methodId).toCharArray());
 		if (isReadAccess) {
@@ -190,7 +190,7 @@
 		this.tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved);
 		SourceTypeBinding declaringSourceType = (SourceTypeBinding) declaringClass;
 		SyntheticMethodBinding[] knownAccessMethods = declaringSourceType.syntheticMethods();
-		int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length;
+		int methodId = knownAccessMethods == null ? 0 : knownAccessMethods[knownAccessMethods.length - 1].index + 1; //index may miss some numbers in between. get the highest index and assign next number.;
 		this.index = methodId;
 		this.selector = selector;
 		this.returnType = declaringSourceType.scope.createArrayType(TypeBinding.INT, 1);
@@ -274,9 +274,7 @@
 	    this.thrownExceptions = overridenMethodToBridge.thrownExceptions;
 	    this.targetMethod = targetMethod;
 	    this.purpose = SyntheticMethodBinding.BridgeMethod;
-		SyntheticMethodBinding[] knownAccessMethods = declaringClass.syntheticMethods();
-		int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length;
-		this.index = methodId;
+		this.index = nextSmbIndex();
 	}
 
 	/**
@@ -298,14 +296,18 @@
 		    this.parameters = new TypeBinding[]{ declaringEnum.scope.getJavaLangString() };
 		    this.purpose = SyntheticMethodBinding.EnumValueOf;
 		}
-		SyntheticMethodBinding[] knownAccessMethods = ((SourceTypeBinding)this.declaringClass).syntheticMethods();
-		int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length;
-		this.index = methodId;
+		this.index = nextSmbIndex();
 		if (declaringEnum.isStrictfp()) {
 			this.modifiers |= ClassFileConstants.AccStrictfp;
 		}
 	}
 
+	private int nextSmbIndex() {
+		SyntheticMethodBinding[] knownAccessMethods = ((SourceTypeBinding)this.declaringClass).syntheticMethods();
+		int methodId = knownAccessMethods == null ? 0 : knownAccessMethods[knownAccessMethods.length - 1].index + 1; //index may miss some numbers in between. get the highest index and assign next number.;
+		return methodId;
+	}
+
 	/**
 	 * Construct $deserializeLambda$ method
 	 */
@@ -318,9 +320,7 @@
 		this.returnType = declaringClass.scope.getJavaLangObject();
 	    this.parameters = new TypeBinding[]{declaringClass.scope.getJavaLangInvokeSerializedLambda()};
 	    this.purpose = SyntheticMethodBinding.DeserializeLambda;
-		SyntheticMethodBinding[] knownAccessMethods = declaringClass.syntheticMethods();
-		int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length;
-		this.index = methodId;
+		this.index = nextSmbIndex();
 	}
 
 	/**
@@ -328,8 +328,7 @@
 	 */
 	public SyntheticMethodBinding(SourceTypeBinding declaringEnum, int startIndex, int endIndex) {
 		this.declaringClass = declaringEnum;
-		SyntheticMethodBinding[] knownAccessMethods = declaringEnum.syntheticMethods();
-		this.index = knownAccessMethods == null ? 0 : knownAccessMethods.length;
+		this.index = nextSmbIndex();
 		StringBuffer buffer = new StringBuffer();
 		buffer.append(TypeConstants.SYNTHETIC_ENUM_CONSTANT_INITIALIZATION_METHOD_PREFIX).append(this.index);
 		this.selector = String.valueOf(buffer).toCharArray();
@@ -360,9 +359,7 @@
 	    this.thrownExceptions = overridenMethodToBridge.thrownExceptions;
 	    this.targetMethod = overridenMethodToBridge;
 	    this.purpose = SyntheticMethodBinding.SuperMethodAccess;
-		SyntheticMethodBinding[] knownAccessMethods = declaringClass.syntheticMethods();
-		int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length;
-		this.index = methodId;
+		this.index = nextSmbIndex();
 	}
 
 	public SyntheticMethodBinding(int purpose, ArrayBinding arrayType, char [] selector, SourceTypeBinding declaringClass) {
@@ -382,9 +379,7 @@
 	    this.parameters = new TypeBinding[] { purpose == SyntheticMethodBinding.ArrayConstructor ? TypeBinding.INT : (TypeBinding) arrayType};
 	    this.thrownExceptions = Binding.NO_EXCEPTIONS;
 	    this.purpose = purpose;
-		SyntheticMethodBinding[] knownAccessMethods = declaringClass.syntheticMethods();
-		int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length;
-		this.index = methodId;
+		this.index = nextSmbIndex();
 	}
 
 	public SyntheticMethodBinding(LambdaExpression lambda, char [] lambdaName, SourceTypeBinding declaringClass) {
@@ -400,9 +395,7 @@
 	    	this.typeVariables = vars;
 	    this.thrownExceptions = lambda.binding.thrownExceptions;
 	    this.purpose = SyntheticMethodBinding.LambdaMethod;
-		SyntheticMethodBinding[] knownAccessMethods = declaringClass.syntheticMethods();
-		int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length;
-		this.index = methodId;
+		this.index = nextSmbIndex();
 	}
 
 	public SyntheticMethodBinding(ReferenceExpression ref, SourceTypeBinding declaringClass) {
@@ -415,9 +408,7 @@
 	    this.parameters = ref.binding.parameters;
 	    this.thrownExceptions = ref.binding.thrownExceptions;
 	    this.purpose = SyntheticMethodBinding.SerializableMethodReference;
-		SyntheticMethodBinding[] knownAccessMethods = declaringClass.syntheticMethods();
-		int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length;
-		this.index = methodId;
+		this.index = nextSmbIndex();
 	}
 
 	public SyntheticMethodBinding(MethodBinding privateConstructor, MethodBinding publicConstructor, char[] selector, TypeBinding[] enclosingInstances, SourceTypeBinding declaringClass) {
@@ -438,9 +429,7 @@
 	    this.thrownExceptions = publicConstructor.thrownExceptions;
 	    this.purpose = SyntheticMethodBinding.FactoryMethod;
 	    this.targetMethod = publicConstructor;
-		SyntheticMethodBinding[] knownAccessMethods = declaringClass.syntheticMethods();
-		int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length;
-		this.index = methodId;
+		this.index = nextSmbIndex();
 	}
 
 	public SyntheticMethodBinding(ReferenceBinding declaringClass, RecordComponentBinding[] rcb) {
@@ -460,9 +449,7 @@
 		this.thrownExceptions = Binding.NO_EXCEPTIONS;
 		this.declaringClass = declaringSourceType;
 		this.tagBits |= TagBits.IsCanonicalConstructor;
-		SyntheticMethodBinding[] knownAccessMethods = declaringSourceType.syntheticMethods();
-		int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length;
-		this.index = methodId;
+		this.index = nextSmbIndex();
 	}
 	public SyntheticMethodBinding(ReferenceBinding declaringClass, RecordComponentBinding rcb, int index) {
 		SourceTypeBinding declaringSourceType = (SourceTypeBinding) declaringClass;
@@ -484,9 +471,7 @@
 		this.purpose = SyntheticMethodBinding.FieldReadAccess;
 		this.thrownExceptions = Binding.NO_EXCEPTIONS;
 		this.declaringClass = declaringSourceType;
-		SyntheticMethodBinding[] knownAccessMethods = declaringSourceType.syntheticMethods();
-		int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length;
-		this.index = methodId;
+		this.index = nextSmbIndex();
 		this.sourceStart = rcb.sourceRecordComponent().sourceStart;
 	}
 	public SyntheticMethodBinding(ReferenceBinding declaringClass, char[] selector, int index) {
@@ -512,9 +497,7 @@
 		    this.parameters = new TypeBinding[] {declaringSourceType.scope.getJavaLangObject()};
 		    this.purpose = SyntheticMethodBinding.RecordOverrideEquals;
 		}
-		SyntheticMethodBinding[] knownAccessMethods = declaringSourceType.syntheticMethods();
-		int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length;
-		this.index = methodId;
+		this.index = nextSmbIndex();
 	}
 	/**
 	 * An constructor accessor is a constructor with an extra argument (declaringClass), in case of
@@ -526,8 +509,8 @@
 		this.modifiers = ClassFileConstants.AccDefault | ClassFileConstants.AccSynthetic;
 		this.tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved);
 		SourceTypeBinding sourceType = (SourceTypeBinding) accessedConstructor.declaringClass;
-		SyntheticMethodBinding[] knownSyntheticMethods = sourceType.syntheticMethods();
-		this.index = knownSyntheticMethods == null ? 0 : knownSyntheticMethods.length;
+		SyntheticMethodBinding[] knownSyntheticMethods = sourceType.syntheticMethods();   // returns synthetic methods sorted with index.
+		this.index = knownSyntheticMethods == null ? 0 : knownSyntheticMethods[knownSyntheticMethods.length - 1].index + 1; //index may miss some numbers in between. get the highest index and assign next number.
 
 		this.selector = accessedConstructor.selector;
 		this.returnType = accessedConstructor.returnType;
@@ -612,7 +595,7 @@
 		this.tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved);
 		SourceTypeBinding declaringSourceType = (SourceTypeBinding) receiverType;
 		SyntheticMethodBinding[] knownAccessMethods = declaringSourceType.syntheticMethods();
-		int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length;
+		int methodId = knownAccessMethods == null ? 0 : knownAccessMethods[knownAccessMethods.length - 1].index + 1; //index may miss some numbers in between. get the highest index and assign next number.
 		this.index = methodId;
 
 		this.selector = CharOperation.concat(TypeConstants.SYNTHETIC_ACCESS_METHOD_PREFIX, String.valueOf(methodId).toCharArray());