Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/RecordsRestrictedClassTest.java38
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ClassFile.java170
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CompactConstructorDeclaration.java10
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldDeclaration.java3
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/RecordDeclaration.java326
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java32
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/ConstantPool.java8
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java21
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java2
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java17
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java141
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SyntheticMethodBinding.java61
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeConstants.java1
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java7
14 files changed, 435 insertions, 402 deletions
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 aa549b1844..b91b9ad53a 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
@@ -33,7 +33,7 @@ public class RecordsRestrictedClassTest extends AbstractRegressionTest {
static {
// TESTS_NUMBERS = new int [] { 40 };
// TESTS_RANGE = new int[] { 1, -1 };
-// TESTS_NAMES = new String[] { "testBug553567" };
+// TESTS_NAMES = new String[] { "testBug550750_037" };
}
public static Class<?> testClass() {
@@ -1016,16 +1016,16 @@ public class RecordsRestrictedClassTest extends AbstractRegressionTest {
" }\n"+
"}\n"+
"record Point(int myInt, int myZ) implements I {\n"+
- " public int myInt() throws IOException {;\n" +
+ " public int myInt() throws Exception {;\n" +
" return this.myInt;\n" +
" }\n"+
"}\n" +
"interface I {}\n"
},
"----------\n" +
- "1. ERROR in X.java (at line 0)\n" +
- " public class X {\n" +
- " ^\n" +
+ "1. ERROR in X.java (at line 7)\n" +
+ " public int myInt() throws Exception {;\n" +
+ " ^^^^^^^^^^^^^^^^^^^^^^^^\n" +
"Throws clause not allowed for explicitly declared accessor method\n" +
"----------\n");
}
@@ -1628,11 +1628,12 @@ public void testBug558494_003() throws Exception {
"public class X {\n"+
" public static void main(String[] args) {\n"+
" Forts p = new Forts(new String[] {\"Amber\", \"Nahargarh\", \"Jaigarh\"});\n"+
- " System.out.println(p.toString());\n"+
+ " if (!p.toString().startsWith(\"Forts[wonders=[Ljava.lang.String;@\"))\n"+
+ " System.out.println(\"Error\");\n"+
" }\n"+
"}\n"
},
- "Forts@c77c674d");
+ "");
String expectedOutput = "Record: #Record\n" +
"Components:\n" +
" \n";
@@ -1642,24 +1643,25 @@ public void testBug558494_004() throws Exception {
runConformTest(
new String[] {
"X.java",
- "record Forts(String[] wonders, int x){\n"+
+ "record Forts(int x, String[] wonders){\n"+
"}\n"+
"public class X {\n"+
" public static void main(String[] args) {\n"+
- " Forts p = new Forts(new String[] {\"Amber\", \"Nahargarh\", \"Jaigarh\"}, 3);\n"+
- " System.out.println(p.toString());\n"+
+ " Forts p = new Forts(3, new String[] {\"Amber\", \"Nahargarh\", \"Jaigarh\"});\n"+
+ " if (!p.toString().startsWith(\"Forts[x=3, wonders=[Ljava.lang.String;@\"))\n"+
+ " System.out.println(\"Error\");\n"+
" }\n"+
"}\n"
},
- "Forts@28108256");
+ "");
String expectedOutput =
- "Record: #Record\n" +
- "Components:\n" +
- " \n" +
- "// Component descriptor #6 [Ljava/lang/String;\n" +
- "java.lang.String[] wonders;\n" +
- "// Component descriptor #8 I\n" +
- "int x;\n";
+ "Record: #Record\n" +
+ "Components:\n" +
+ " \n" +
+ "// Component descriptor #6 I\n" +
+ "int x;\n" +
+ "// Component descriptor #8 [Ljava/lang/String;\n" +
+ "java.lang.String[] wonders;\n";
RecordsRestrictedClassTest.verifyClassFile(expectedOutput, "Forts.class", ClassFileBytesDisassembler.SYSTEM);
}
public void testBug558764_001() {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ClassFile.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ClassFile.java
index 71de6cf5eb..d1590219b5 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ClassFile.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ClassFile.java
@@ -46,7 +46,6 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
-
import org.eclipse.jdt.core.compiler.CategorizedProblem;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.compiler.IProblem;
@@ -164,6 +163,7 @@ public class ClassFile implements TypeConstants, TypeIds {
public int headerOffset;
public Map<TypeBinding, Boolean> innerClassesBindings;
public List bootstrapMethods = null;
+ public List<TypeBinding> recordBootstrapMethods = null;
public int methodCount;
public int methodCountOffset;
// pool managment
@@ -424,9 +424,10 @@ public class ClassFile implements TypeConstants, TypeIds {
}
attributesNumber += generateHierarchyInconsistentAttribute();
}
- // Functional expression and lambda bootstrap methods
- if (this.bootstrapMethods != null && !this.bootstrapMethods.isEmpty()) {
- attributesNumber += generateBootstrapMethods(this.bootstrapMethods);
+ // Functional expression, lambda bootstrap methods and record bootstrap methods
+ if ((this.bootstrapMethods != null && !this.bootstrapMethods.isEmpty()) ||
+ (this.recordBootstrapMethods != null && !this.recordBootstrapMethods.isEmpty())) {
+ attributesNumber += generateBootstrapMethods(this.bootstrapMethods, this.recordBootstrapMethods);
}
// Inner class attribute
int numberOfInnerClasses = this.innerClassesBindings == null ? 0 : this.innerClassesBindings.size();
@@ -1102,6 +1103,11 @@ public class ClassFile implements TypeConstants, TypeIds {
case SyntheticMethodBinding.SerializableMethodReference:
// Nothing to be done
break;
+ case SyntheticMethodBinding.RecordOverrideEquals:
+ case SyntheticMethodBinding.RecordOverrideHashCode:
+ case SyntheticMethodBinding.RecordOverrideToString:
+ addSyntheticRecordOverrideMethods(syntheticMethod, syntheticMethod.purpose);
+ break;
}
}
emittedSyntheticsCount = currentSyntheticsCount;
@@ -1132,6 +1138,48 @@ public class ClassFile implements TypeConstants, TypeIds {
}
}
+ private void addSyntheticRecordOverrideMethods(SyntheticMethodBinding methodBinding, int purpose) {
+ if (this.recordBootstrapMethods == null)
+ this.recordBootstrapMethods = new ArrayList<>(3);
+ if (!this.recordBootstrapMethods.contains(methodBinding.declaringClass))
+ this.recordBootstrapMethods.add(methodBinding.declaringClass);
+ int index = this.bootstrapMethods != null ? this.bootstrapMethods.size() +
+ this.recordBootstrapMethods.size() - 1 : 0;
+ generateMethodInfoHeader(methodBinding);
+ int methodAttributeOffset = this.contentsOffset;
+ // this will add exception attribute, synthetic attribute, deprecated attribute,...
+ int attributeNumber = generateMethodInfoAttributes(methodBinding);
+ // Code attribute
+ int codeAttributeOffset = this.contentsOffset;
+ attributeNumber++; // add code attribute
+ generateCodeAttributeHeader();
+ this.codeStream.init(this);
+ switch (purpose) {
+ case SyntheticMethodBinding.RecordOverrideEquals:
+ this.codeStream.generateSyntheticBodyForRecordEquals(methodBinding, index);
+ break;
+ case SyntheticMethodBinding.RecordOverrideHashCode:
+ this.codeStream.generateSyntheticBodyForRecordHashCode(methodBinding, index);
+ break;
+ case SyntheticMethodBinding.RecordOverrideToString:
+ this.codeStream.generateSyntheticBodyForRecordToString(methodBinding, index);
+ break;
+ default:
+ break;
+ }
+ completeCodeAttributeForSyntheticMethod(
+ methodBinding,
+ codeAttributeOffset,
+ ((SourceTypeBinding) methodBinding.declaringClass)
+ .scope
+ .referenceCompilationUnit()
+ .compilationResult
+ .getLineSeparatorPositions());
+ // update the number of attributes
+ this.contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8);
+ this.contents[methodAttributeOffset] = (byte) attributeNumber;
+ }
+
public void addSyntheticArrayConstructor(SyntheticMethodBinding methodBinding) {
generateMethodInfoHeader(methodBinding);
int methodAttributeOffset = this.contentsOffset;
@@ -2814,12 +2862,12 @@ public class ClassFile implements TypeConstants, TypeIds {
if (record == null || !record.isRecord())
return 0;
int localContentsOffset = this.contentsOffset;
- List<FieldBinding> recordComponents = this.referenceBinding.getRecordComponents();
+ FieldBinding[] recordComponents = this.referenceBinding.getRecordComponents();
if (recordComponents == null)
return 0;
// could be an empty record also, account for zero components as well.
- int numberOfRecordComponents = recordComponents.size() ;
+ int numberOfRecordComponents = recordComponents.length;
int exSize = 8 + 2 * numberOfRecordComponents;
if (exSize + localContentsOffset >= this.contents.length) {
@@ -2843,7 +2891,7 @@ public class ClassFile implements TypeConstants, TypeIds {
this.contents[localContentsOffset++] = (byte) numberOfRecordComponents;
this.contentsOffset = localContentsOffset;
for (int i = 0; i < numberOfRecordComponents; i++) {
- addComponentInfo(recordComponents.get(i));
+ addComponentInfo(recordComponents[i]);
}
int attrLength = this.contentsOffset - base;
this.contents[attrLengthOffset++] = (byte) (attrLength >> 24);
@@ -3469,7 +3517,7 @@ public class ClassFile implements TypeConstants, TypeIds {
return 1;
}
- private int generateBootstrapMethods(List functionalExpressionList) {
+ private int generateBootstrapMethods(List functionalExpressionList, List<TypeBinding> recordBootstrapMethods2) {
/* See JVM spec 4.7.21
The BootstrapMethods attribute has the following format:
BootstrapMethods_attribute {
@@ -3486,13 +3534,10 @@ public class ClassFile implements TypeConstants, TypeIds {
ReferenceBinding methodHandlesLookup = this.referenceBinding.scope.getJavaLangInvokeMethodHandlesLookup();
if (methodHandlesLookup == null) return 0; // skip bootstrap section, class path problem already reported, just avoid NPE.
recordInnerClasses(methodHandlesLookup); // Should be done, it's what javac does also
- ReferenceBinding javaLangInvokeLambdaMetafactory = this.referenceBinding.scope.getJavaLangInvokeLambdaMetafactory();
-
- // Depending on the complexity of the expression it may be necessary to use the altMetafactory() rather than the metafactory()
- int indexForMetaFactory = 0;
- int indexForAltMetaFactory = 0;
- int numberOfBootstraps = functionalExpressionList.size();
+ int nfunctionalExpressions = functionalExpressionList != null ? functionalExpressionList.size() : 0;
+ int nRecordBootStraps = recordBootstrapMethods2 != null ? recordBootstrapMethods2.size() : 0;
+ int numberOfBootstraps = nfunctionalExpressions + nRecordBootStraps;
int localContentsOffset = this.contentsOffset;
// Generate the boot strap attribute - since we are only making lambdas and
// functional expressions, we know the size ahead of time - this less general
@@ -3511,8 +3556,35 @@ public class ClassFile implements TypeConstants, TypeIds {
// leave space for attribute_length and remember where to insert it
int attributeLengthPosition = localContentsOffset;
localContentsOffset += 4;
+
this.contents[localContentsOffset++] = (byte) (numberOfBootstraps >> 8);
this.contents[localContentsOffset++] = (byte) numberOfBootstraps;
+
+ if (nfunctionalExpressions > 0)
+ localContentsOffset = generateLambdaMetaFactoryBootStrapMethods(functionalExpressionList,
+ localContentsOffset, contentsEntries);
+ if (nRecordBootStraps > 0)
+ localContentsOffset = generateObjectMethodsBootStrapMethods(recordBootstrapMethods2, localContentsOffset,
+ contentsEntries);
+
+ int attributeLength = localContentsOffset - attributeLengthPosition - 4;
+ this.contents[attributeLengthPosition++] = (byte) (attributeLength >> 24);
+ this.contents[attributeLengthPosition++] = (byte) (attributeLength >> 16);
+ this.contents[attributeLengthPosition++] = (byte) (attributeLength >> 8);
+ this.contents[attributeLengthPosition++] = (byte) attributeLength;
+ this.contentsOffset = localContentsOffset;
+ return 1;
+ }
+
+ private int generateLambdaMetaFactoryBootStrapMethods(List functionalExpressionList,
+ int localContentsOffset, final int contentsEntries) {
+ ReferenceBinding javaLangInvokeLambdaMetafactory = this.referenceBinding.scope.getJavaLangInvokeLambdaMetafactory();
+ int numberOfBootstraps = functionalExpressionList.size();
+
+ // Depending on the complexity of the expression it may be necessary to use the altMetafactory() rather than the metafactory()
+ int indexForMetaFactory = 0;
+ int indexForAltMetaFactory = 0;
+
for (int i = 0; i < numberOfBootstraps; i++) {
FunctionalExpression functional = (FunctionalExpression) functionalExpressionList.get(i);
MethodBinding [] bridges = functional.getRequiredBridges();
@@ -3625,14 +3697,68 @@ public class ClassFile implements TypeConstants, TypeIds {
this.contents[localContentsOffset++] = (byte) methodTypeIndex;
}
}
-
- int attributeLength = localContentsOffset - attributeLengthPosition - 4;
- this.contents[attributeLengthPosition++] = (byte) (attributeLength >> 24);
- this.contents[attributeLengthPosition++] = (byte) (attributeLength >> 16);
- this.contents[attributeLengthPosition++] = (byte) (attributeLength >> 8);
- this.contents[attributeLengthPosition++] = (byte) attributeLength;
- this.contentsOffset = localContentsOffset;
- return 1;
+ return localContentsOffset;
+ }
+ private int generateObjectMethodsBootStrapMethods(List<TypeBinding> recordList,
+ int localContentsOffset, final int contentsEntries) {
+ ReferenceBinding javaLangRuntimeObjectMethods = this.referenceBinding.scope.getJavaLangRuntimeObjectMethods();
+ int numberOfBootstraps = recordList.size();
+ int indexForObjectMethodBootStrap = 0;
+ for (int i = 0; i < numberOfBootstraps; i++) {
+ if (contentsEntries + localContentsOffset >= this.contents.length) {
+ resizeContents(contentsEntries);
+ }
+ if (indexForObjectMethodBootStrap == 0) {
+ indexForObjectMethodBootStrap = this.constantPool.literalIndexForMethodHandle(ClassFileConstants.MethodHandleRefKindInvokeStatic, javaLangRuntimeObjectMethods,
+ ConstantPool.BOOTSTRAP, ConstantPool.JAVA_LANG_RUNTIME_OBJECTMETHOD_BOOTSTRAP_SIGNATURE, false);
+ }
+ this.contents[localContentsOffset++] = (byte) (indexForObjectMethodBootStrap >> 8);
+ this.contents[localContentsOffset++] = (byte) indexForObjectMethodBootStrap;
+
+ // u2 num_bootstrap_arguments
+ int numArgsLocation = localContentsOffset;
+ localContentsOffset += 2;
+
+ TypeBinding type = recordList.get(i);
+ assert type.isRecord(); // sanity check
+ int recordIndex = this.constantPool.literalIndexForType(type.constantPoolName());
+ this.contents[localContentsOffset++] = (byte) (recordIndex >> 8);
+ this.contents[localContentsOffset++] = (byte) recordIndex;
+
+ assert type instanceof SourceTypeBinding;
+ SourceTypeBinding sourceType = (SourceTypeBinding) type;
+ FieldBinding[] recordComponents = sourceType.getRecordComponents();
+
+ int numArgs = 2 + recordComponents.length;
+ this.contents[numArgsLocation++] = (byte) (numArgs >> 8);
+ this.contents[numArgsLocation] = (byte) numArgs;
+
+ String names =
+ Arrays.stream(recordComponents)
+ .map(f -> new String(f.name))
+ .reduce((s1, s2) -> { return s1 + ";" + s2;}) //$NON-NLS-1$
+ .orElse(Util.EMPTY_STRING);
+ int namesIndex = this.constantPool.literalIndex(names);
+ this.contents[localContentsOffset++] = (byte) (namesIndex >> 8);
+ this.contents[localContentsOffset++] = (byte) namesIndex;
+
+ List<MethodBinding> getters = new ArrayList<>();
+ for (FieldBinding field : recordComponents) {
+ MethodBinding[] candidates = sourceType.getMethods(field.name);
+ for (MethodBinding candidate : candidates) {
+ if (candidate.parameters == null || candidate.parameters.length == 0) {
+ getters.add(candidate);
+ break;
+ }
+ }
+ }
+ for (MethodBinding getter : getters) {
+ int getterIndex = this.constantPool.literalIndexForMethodHandle(getter);
+ this.contents[localContentsOffset++] = (byte) (getterIndex >> 8);
+ this.contents[localContentsOffset++] = (byte) getterIndex;
+ }
+ }
+ return localContentsOffset;
}
private int generateLineNumberAttribute() {
int localContentsOffset = this.contentsOffset;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CompactConstructorDeclaration.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CompactConstructorDeclaration.java
index 0bf3a2e61e..5973d44db9 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CompactConstructorDeclaration.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CompactConstructorDeclaration.java
@@ -27,7 +27,6 @@ import org.eclipse.jdt.internal.compiler.parser.Parser;
public class CompactConstructorDeclaration extends ConstructorDeclaration {
- public boolean isImplicit;
public RecordDeclaration recordDeclaration;
public CompactConstructorDeclaration(CompilationResult compilationResult) {
@@ -35,12 +34,6 @@ public class CompactConstructorDeclaration extends ConstructorDeclaration {
}
@Override
public void parseStatements(Parser parser, CompilationUnitDeclaration unit) {
- if (this.isImplicit && this.constructorCall == null) {
- this.constructorCall = SuperReference.implicitSuperConstructorCall();
- this.constructorCall.sourceStart = this.sourceStart;
- this.constructorCall.sourceEnd = this.sourceEnd;
- return;
- }
parser.parse(this, unit, false);
ASTVisitor visitor = new ASTVisitor() {
@Override
@@ -63,8 +56,7 @@ public class CompactConstructorDeclaration extends ConstructorDeclaration {
return false;
}
};
- if (!this.isImplicit)
- unit.traverse(visitor, unit.scope);
+ unit.traverse(visitor, unit.scope);
}
@Override
protected void checkAndGenerateFieldAssignment(FlowContext flowContext, FlowInfo flowInfo, FieldBinding field) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldDeclaration.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldDeclaration.java
index 7bda6fe890..1ef006e78f 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldDeclaration.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldDeclaration.java
@@ -77,7 +77,8 @@ public FieldDeclaration( char[] name, int sourceStart, int sourceEnd) {
public FlowInfo analyseCode(MethodScope initializationScope, FlowContext flowContext, FlowInfo flowInfo) {
if (this.binding != null && !this.binding.isUsed() && this.binding.isOrEnclosedByPrivateType()) {
if (!initializationScope.referenceCompilationUnit().compilationResult.hasSyntaxError) {
- initializationScope.problemReporter().unusedPrivateField(this);
+ if (!this.isARecordComponent) // record component used by implicit methods
+ initializationScope.problemReporter().unusedPrivateField(this);
}
}
// cannot define static non-constant field inside nested class
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/RecordDeclaration.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/RecordDeclaration.java
index 5b96270e07..ed8e1f6aae 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/RecordDeclaration.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/RecordDeclaration.java
@@ -17,10 +17,8 @@
package org.eclipse.jdt.internal.compiler.ast;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Set;
import org.eclipse.jdt.core.compiler.CharOperation;
@@ -29,17 +27,12 @@ import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
-import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.eclipse.jdt.internal.compiler.parser.Parser;
-import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
public class RecordDeclaration extends TypeDeclaration {
private Argument[] args;
public int nRecordComponents;
- private long defaultPosition = 0L;
- private long[] default_long_pos = {0L, 0L, 0L};
-
public static Set<String> disallowedComponentNames;
static {
disallowedComponentNames = new HashSet<>(6);
@@ -80,10 +73,7 @@ public class RecordDeclaration extends TypeDeclaration {
this.typeParameters = t.typeParameters;
this.sourceStart = t.sourceStart;
this.sourceEnd = t.sourceEnd;
- this.restrictedIdentifierStart = t.restrictedIdentifierStart;
- this.defaultPosition = this.sourceStart << 32 + (this.sourceStart + 1);
- this.default_long_pos = new long[]{this.defaultPosition, this.defaultPosition, this.defaultPosition};
- }
+}
public ConstructorDeclaration getConstructor(Parser parser) {
if (this.methods != null) {
for (int i = this.methods.length; --i >= 0;) {
@@ -156,7 +146,7 @@ public class RecordDeclaration extends TypeDeclaration {
/* The body of the implicitly declared canonical constructor initializes each field corresponding
* to a record component with the corresponding formal parameter in the order that they appear
* in the record component list.*/
- int l = this.args != null ? this.args.length : 0;
+ int l = this.args.length;
Statement[] statements = new Statement[l];
for (int i = 0; i < l; ++i) {
Argument arg = this.args[i];
@@ -185,113 +175,10 @@ public class RecordDeclaration extends TypeDeclaration {
return constructor;
}
- public void createImplicitAccessors(ProblemReporter problemReporter) {
- // JLS 14 8.10.3 Item 2 create the accessors for the fields if required
- /*
- * An implicitly declared public accessor method with the same name as the record component,
- * whose return type is the declared type of the record component,
- * unless a public method with the same signature is explicitly declared in the body of the declaration of R.
- */
-
- if (this.fields == null)
- return;
- Map<String, Set<AbstractMethodDeclaration>> accessors = new HashMap<>();
- for (int i = 0; i < this.nRecordComponents; i++) {
- FieldDeclaration f = this.fields[i] ;
- if (f != null && f.name != null && f.name.length > 0) {
- accessors.put(new String(f.name), new HashSet<>());
- }
- }
- if (this.methods != null) {
- for (int i = 0; i < this.methods.length; i++) {
- AbstractMethodDeclaration m = this.methods[i];
- if (m != null && m.selector != null & m.selector.length > 0) {
- String name1 = new String(m.selector);
- Set<AbstractMethodDeclaration> acc = accessors.get(name1);
- if (acc != null)
- acc.add(m);
- }
- }
- }
- for (int i = this.nRecordComponents - 1; i >= 0; i--) {
- FieldDeclaration f = this.fields[i] ;
- if (f != null && f.name != null && f.name.length > 0) {
- String name1 = new String(f.name);
- Set<AbstractMethodDeclaration> acc = accessors.get(name1);
- MethodDeclaration m = null;
- if (acc.size() > 0) {
- for (AbstractMethodDeclaration amd : acc) {
- m = (MethodDeclaration) amd;
- /* JLS 14 Sec 8.10.3 Item 1, Subitem 2
- * An implicitly declared public accessor method with the same name as the record component, whose return
- * type is the declared type of the record component, unless a public method with the same signature is
- * explicitly declared in the body of the declaration of R
- */
- // Here the assumption is method signature implies the method signature in source ie the return type
- // is not being considered - Given this, type resolution is not required and hence its a simple name and
- // parameter number check.
- if (m.arguments == null || m.arguments.length == 0) {
- // found the explicitly declared accessor.
- /*
- * JLS 14 Sec 8.10.3 Item 1 Sub-item 2 Para 3
- * It is a compile-time error if an explicitly declared accessor method has a throws clause.
- */
- if (m.thrownExceptions != null && m.thrownExceptions.length > 0)
- problemReporter.recordAccessorMethodHasThrowsClause(m);
- break; // found
- }
- m = null;
- }
- }
- if (m == null) // no explicit accessor method found - declare one.
- createNewMethod(f);
- }
- }
- }
@Override
public void generateCode(ClassFile enclosingClassFile) {
super.generateCode(enclosingClassFile);
}
- private AbstractMethodDeclaration createNewMethod(FieldDeclaration f) {
- MethodDeclaration m = new MethodDeclaration(this.compilationResult);
- m.selector = f.name;
- m.bits |= ASTNode.IsImplicit;
- m.modifiers = ClassFileConstants.AccPublic;
-
- m.returnType = f.type;
- FieldReference fr = new FieldReference(f.name, -1);
- fr.receiver = new ThisReference(-1, -1);
- ReturnStatement ret = new ReturnStatement(fr, -1, -1);
- m.statements = new Statement[] { ret };
- m.isImplicit = true;
- /*
- * JLS 14 Sec 8.10.3 Item 2 states that:
- * "The implicitly declared accessor method is annotated with the annotation
- * that appears on the corresponding record component, if this annotation type
- * is applicable to a method declaration or type context."
- *
- * However, at this point in compilation, sufficient information to determine
- * the ElementType targeted by the annotation doesn't exist and hence a blanket
- * copy of annotation is done for now, and later (binding stage) irrelevant ones
- * are weeded out.
- */
- m.annotations = f.annotations;
-
- if (this.methods == null) { // Where is the constructor?
- this.methods = new AbstractMethodDeclaration[] { m };
- } else {
- AbstractMethodDeclaration[] newMethods;
- System.arraycopy(
- this.methods,
- 0,
- newMethods = new AbstractMethodDeclaration[this.methods.length + 1],
- 1,
- this.methods.length);
- newMethods[0] = m;
- this.methods = newMethods;
- }
- return m;
- }
@Override
public boolean isRecord() {
return true;
@@ -381,16 +268,6 @@ public class RecordDeclaration extends TypeDeclaration {
}
}
}
- public void createImplicitRecordOverrideMethods(ProblemReporter problemReporter) {
- TypeReference superClass = new QualifiedTypeReference(TypeConstants.JAVA_LANG_RECORD, new long[] {0});
- superClass.bits |= ASTNode.IsSuperType;
- this.superclass = superClass;
-
- // TODO Auto-generated method stub
- checkAndCreateImplicitequals(problemReporter);
- checkAndCreateImplicitHashCode(problemReporter);
- checkAndCreateImplicitToString(problemReporter);
- }
AbstractMethodDeclaration[] getMethod(char[] name1) {
if (name1 == null || name1.length == 0 || this.methods == null)
return null;
@@ -401,203 +278,4 @@ public class RecordDeclaration extends TypeDeclaration {
}
return amList.toArray(new AbstractMethodDeclaration[0]);
}
- private void checkAndCreateImplicitToString(ProblemReporter problemReporter) {
- if (null != getMethodByName(TypeConstants.TOSTRING))
- return;
- QualifiedTypeReference returnType = new QualifiedTypeReference(JAVA_LANG_STRING, this.default_long_pos);
- MethodDeclaration md = createMethodDeclaration(TypeConstants.TOSTRING, returnType);
- MarkerAnnotation overrideAnnotation = new MarkerAnnotation(new SingleTypeReference(TypeConstants.JAVA_LANG_OVERRIDE[2], 0), 0);
- md.annotations = new Annotation[] { overrideAnnotation };
- // getClass().getName() + "@" + Integer.toHexString(hashCode())
- Expression left = new StringLiteral(this.name, -1, -1, -1);
- Expression right = new StringLiteral(new char[] {'@'}, -1, -1, -1);
- left = new BinaryExpression(left, right, OperatorIds.PLUS);
- MessageSend m = new MessageSend();
- m.receiver = new QualifiedNameReference(
- TypeConstants.JAVA_LANG_INTEGER,
- this.default_long_pos, -1, -1);
- m.selector = "toHexString".toCharArray(); //$NON-NLS-1$
- MessageSend hc = new MessageSend();
- hc.receiver = new ThisReference(-1, -1);
- hc.selector = TypeConstants.HASHCODE;
- m.arguments = new Expression[] {hc};
- right = m;
- CombinedBinaryExpression cbe = new CombinedBinaryExpression(left, right, OperatorIds.PLUS, 1);
- md.statements = new Statement[] { new ReturnStatement(cbe, -1, -1) };
- md.isImplicit = true;
- }
- private static char[][] getBoxedName(char[] token) {
- return
- token == BYTE ? JAVA_LANG_BYTE :
- token == SHORT ? JAVA_LANG_SHORT :
- token == CHAR ? JAVA_LANG_CHARACTER :
- token == INT ? JAVA_LANG_INTEGER :
- token == LONG ? JAVA_LANG_LONG :
- token == FLOAT ? JAVA_LANG_FLOAT :
- token == DOUBLE ? JAVA_LANG_DOUBLE :
- token == BOOLEAN ? JAVA_LANG_BOOLEAN : null;
- }
- private void checkAndCreateImplicitHashCode(ProblemReporter problemReporter) {
- if (null != getMethodByName(TypeConstants.HASHCODE))
- return;
- MethodDeclaration md = createMethodDeclaration(TypeConstants.HASHCODE, TypeReference.baseTypeReference(TypeIds.T_int, 0));
- MarkerAnnotation overrideAnnotation = new MarkerAnnotation(new SingleTypeReference(TypeConstants.JAVA_LANG_OVERRIDE[2], 0), 0);
- md.annotations = new Annotation[] { overrideAnnotation };
-
- List<Expression> initVals = new ArrayList<>();
- if (this.args != null) {
- for (Argument arg : this.args) {
- if (RecordDeclaration.disallowedComponentNames.contains(new String(arg.name)))
- continue;
- FieldReference fr = new FieldReference(arg.name, -1);
- fr.receiver = new ThisReference(-1, -1);
- Expression receiver = null;
- if (arg.type instanceof ArrayTypeReference) {
- MessageSend arraysHashCode = new MessageSend();
- arraysHashCode.selector = HASHCODE;
- arraysHashCode.receiver =
- new QualifiedNameReference(JAVA_UTIL_ARRAYS,
- this.default_long_pos, -1, -1);
- arraysHashCode.arguments = new Expression[] { fr};
- initVals.add(arraysHashCode);
- continue;
- } else if (arg.type.isBaseTypeReference()) {
- QualifiedNameReference boxReference = new QualifiedNameReference(
- RecordDeclaration.getBoxedName(arg.type.getLastToken()),
- this.default_long_pos, -1, -1);
- MessageSend m = new MessageSend();
- m.receiver = boxReference;
- m.selector = TypeConstants.VALUEOF; // Integer.valueOf(int)
- m.arguments = new Expression[] { fr };
- receiver = m;
- } else {
- receiver = fr; //this.field
- }
- MessageSend messageSend = new MessageSend();
- messageSend.receiver = receiver;
- messageSend.selector = HASHCODE;
- initVals.add(messageSend);
- }
- }
- ArrayInitializer ai = new ArrayInitializer();
- ai.expressions = initVals.toArray(new Expression[0]);
- ArrayAllocationExpression aae = new ArrayAllocationExpression();
- aae.dimensions = new Expression[1];
- aae.dimensions[0] = null;
- aae.initializer = ai;
- aae.type = new SingleTypeReference(INT, -1);
- MessageSend arraysHashCode = new MessageSend();
- arraysHashCode.selector = HASHCODE;
- arraysHashCode.receiver =
- new QualifiedNameReference(JAVA_UTIL_ARRAYS,
- this.default_long_pos, -1, -1);
- arraysHashCode.arguments = new Expression[] { aae };
- md.statements = new Statement[] { new ReturnStatement(arraysHashCode, -1, -1) };
- md.isImplicit = true;
- }
- private AbstractMethodDeclaration getMethodByName(char[] name1) {
- AbstractMethodDeclaration[] ams = getMethod(name1);
- for (AbstractMethodDeclaration amd : ams) {
- Argument[] args1 = amd.arguments;
- if (args1 == null || args1.length == 0)
- return amd; // explicit method exists, no need to create an implicit one.
- }
- return null;
- }
- private void checkAndCreateImplicitequals(ProblemReporter problemReporter) {
- AbstractMethodDeclaration[] ams = getMethod(TypeConstants.EQUALS);
- for (AbstractMethodDeclaration amd : ams) {
- Argument[] args1 = amd.arguments;
- if (args1 == null || args1.length != 1)
- continue;
- char[][] typeRef = args1[0].type != null ? args1[0].type.getTypeName() : null;
- if (typeRef == null)
- continue;
- if (CharOperation.equals(typeRef, JAVA_LANG_OBJECT)) {
- return; // explicit method exists, no need to create an implicit one.
- }
- }
- // In case of just object, we can never be sure - so create one implicit method
- // and at resolution time, remove the implicit one if there is a conflict.
- // so, at this point we create one anyway.
- MethodDeclaration md = createMethodDeclaration(TypeConstants.EQUALS, TypeReference.baseTypeReference(TypeIds.T_boolean, 0));
- MarkerAnnotation overrideAnnotation = new MarkerAnnotation(new SingleTypeReference(TypeConstants.JAVA_LANG_OVERRIDE[2], 0), 0);
- md.annotations = new Annotation[] { overrideAnnotation };
- TypeReference objectTypeReference = new QualifiedTypeReference(JAVA_LANG_OBJECT, new long[] {0L, 0L, 0L});
- char[] objName = new char[] {'o','b','j'};
- md.arguments = new Argument[] { new Argument(objName, 0, objectTypeReference, 0)};
-
- List<Statement> stmts = new ArrayList<>();
- // if (!(obj instanceof recordName))
- // return false;
- InstanceOfExpression ioe = new InstanceOfExpression(new SingleNameReference(objName, 0), new SingleTypeReference(this.name, 0L));
- Expression condition = new UnaryExpression(ioe, OperatorIds.NOT);
- Statement thenStatement = new ReturnStatement(new FalseLiteral(-1, -1), -1, -1);
- IfStatement ifStatement = new IfStatement(condition, thenStatement, -1, -1);
- stmts.add(ifStatement);
-
- // recordType o = (recordType) obj;
- LocalDeclaration ld = new LocalDeclaration(new char[] {'o'}, -1, -1);
- ld.type = new SingleTypeReference(this.name, -1);
- ld.initialization = new CastExpression(new SingleNameReference(objName, -1), new SingleTypeReference(this.name, 0L));
- stmts.add(ld);
-
- // check each field - ref javadoc of java.lang.Record#equals
- /*
- * Impl Spec: The implicitly provided implementation returns true if and only if the argument is an
- * instance of the same record type as this object, and each component of this record is equal to the
- * corresponding component of the argument, according to java.util.Objects.equals(Object, Object) for
- * components whose types are reference types, and according to the semantics of the equals method on
- * the corresponding primitive wrapper type.
- */
- if (this.args != null) {
- for (Argument arg : this.args) {
- if (RecordDeclaration.disallowedComponentNames.contains(new String(arg.name)))
- continue;
- FieldReference fr = new FieldReference(arg.name, -1);
- fr.receiver = new ThisReference(-1, -1);
- char [][] qfrName = new char[][] { {'o'}, arg.name };
- long [] qfrPos = {-1, -1};
- QualifiedNameReference ofr = new QualifiedNameReference(qfrName, qfrPos, -1, -1);
- if (arg.type.isBaseTypeReference()) {
- condition = new EqualExpression(fr, ofr, OperatorIds.NOT_EQUAL);
- } else {
- MessageSend messageSend = new MessageSend();
- messageSend.selector = TypeConstants.EQUALS;
- messageSend.arguments = new Expression[] {fr, ofr};
- messageSend.receiver = new QualifiedNameReference(JAVA_UTIL_OBJECTS, new long[] {-1, -1, -1}, -1, -1);
- condition = new UnaryExpression(messageSend, OperatorIds.NOT);
- }
- thenStatement = new ReturnStatement(new FalseLiteral(-1, -1), -1, -1);
- ifStatement = new IfStatement(condition, thenStatement, -1, -1);
- stmts.add(ifStatement);
- }
- }
- stmts.add(new ReturnStatement(new TrueLiteral(-1, -1), -1, -1));
- md.statements = stmts.toArray(new Statement[0]);
- md.isImplicit = true;
- }
- private MethodDeclaration createMethodDeclaration(char[] name1, TypeReference returnType) {
- MethodDeclaration m = new MethodDeclaration(this.compilationResult);
- m.selector = name1;
- m.modifiers = ClassFileConstants.AccPublic;
-
- m.returnType = returnType;
-
- if (this.methods == null) { // Where is the constructor?
- this.methods = new AbstractMethodDeclaration[] { m };
- } else {
- AbstractMethodDeclaration[] newMethods;
- System.arraycopy(
- this.methods,
- 0,
- newMethods = new AbstractMethodDeclaration[this.methods.length + 1],
- 1,
- this.methods.length);
- newMethods[0] = m;
- this.methods = newMethods;
- }
- m.bits |= ASTNode.IsImplicit;
- return m;
- }
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java
index 42b9d2600a..fc295e1f29 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2019 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -8,6 +8,10 @@
*
* SPDX-License-Identifier: EPL-2.0
*
+ * This is an implementation of an early-draft specification developed under the Java
+ * Community Process (JCP) and is made available for testing and evaluation purposes
+ * only. The code is not compatible with any specification of the JCP.
+ *
* Contributors:
* IBM Corporation - initial API and implementation
* Stephan Herrmann - Contribution for
@@ -1972,7 +1976,6 @@ public void generateEmulationForMethod(Scope scope, MethodBinding methodBinding)
iconst_1();
invokeAccessibleObjectSetAccessible();
}
-
/**
* Generates the sequence of instructions which will perform the conversion of the expression
* on the stack into a different type (e.g. long l = someInt; --> i2l must be inserted).
@@ -3171,6 +3174,31 @@ public void generateSyntheticOuterArgumentValues(BlockScope currentScope, Refere
}
}
}
+public void generateSyntheticBodyForRecordEquals(SyntheticMethodBinding methodBinding, int index) {
+ initializeMaxLocals(methodBinding);
+ aload_0();
+ aload_1();
+ String sig = new String(methodBinding.signature());
+ sig = sig.substring(0, 1)+ new String(methodBinding.declaringClass.signature()) + sig.substring(1);
+ invokeDynamic(index, methodBinding.parameters.length, 1, methodBinding.selector, sig.toCharArray());
+ ireturn();
+}
+public void generateSyntheticBodyForRecordHashCode(SyntheticMethodBinding methodBinding, int index) {
+ initializeMaxLocals(methodBinding);
+ aload_0();
+ String sig = new String(methodBinding.signature());
+ sig = sig.substring(0, 1)+ new String(methodBinding.declaringClass.signature()) + sig.substring(1);
+ invokeDynamic(index, methodBinding.parameters.length, 4, methodBinding.selector, sig.toCharArray());
+ ireturn();
+}
+public void generateSyntheticBodyForRecordToString(SyntheticMethodBinding methodBinding, int index) {
+ initializeMaxLocals(methodBinding);
+ aload_0();
+ String sig = new String(methodBinding.signature());
+ sig = sig.substring(0, 1)+ new String(methodBinding.declaringClass.signature()) + sig.substring(1);
+ invokeDynamic(index, methodBinding.parameters.length, 8, methodBinding.selector, sig.toCharArray());
+ areturn();
+}
public void generateUnboxingConversion(int unboxedTypeID) {
switch (unboxedTypeID) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/ConstantPool.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/ConstantPool.java
index 700a70daa5..9170fe1425 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/ConstantPool.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/ConstantPool.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2019 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -8,6 +8,10 @@
*
* SPDX-License-Identifier: EPL-2.0
*
+ * This is an implementation of an early-draft specification developed under the Java
+ * Community Process (JCP) and is made available for testing and evaluation purposes
+ * only. The code is not compatible with any specification of the JCP.
+ *
* Contributors:
* IBM Corporation - initial API and implementation
* Jesper S Moller - Contributions for
@@ -306,6 +310,8 @@ public class ConstantPool implements ClassFileConstants, TypeIds {
public static final char[] AddSuppressedSignature = "(Ljava/lang/Throwable;)V".toCharArray(); //$NON-NLS-1$
public static final char[] Clone = "clone".toCharArray(); //$NON-NLS-1$
public static final char[] CloneSignature = "()Ljava/lang/Object;".toCharArray(); //$NON-NLS-1$
+ public static final char[] BOOTSTRAP = "bootstrap".toCharArray(); //$NON-NLS-1$
+ public static final char[] JAVA_LANG_RUNTIME_OBJECTMETHOD_BOOTSTRAP_SIGNATURE = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/TypeDescriptor;Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/invoke/MethodHandle;)Ljava/lang/Object;".toCharArray(); //$NON-NLS-1$
/**
* ConstantPool constructor comment.
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java
index a4359d4f43..00e777a48a 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java
@@ -331,7 +331,7 @@ public class ClassScope extends Scope {
if (sourceType.areMethodsInitialized()) return;
boolean isEnum = TypeDeclaration.kind(this.referenceContext.modifiers) == TypeDeclaration.ENUM_DECL;
- if (this.referenceContext.methods == null && !isEnum) {
+ if (this.referenceContext.methods == null && !(isEnum || sourceType.isRecord())) {
this.referenceContext.binding.setMethods(Binding.NO_METHODS);
return;
}
@@ -384,6 +384,11 @@ public class ClassScope extends Scope {
if (hasAbstractMethods)
problemReporter().abstractMethodInConcreteClass(sourceType);
}
+ if (sourceType.isRecord()) {
+ assert this.referenceContext instanceof RecordDeclaration;
+ methodBindings = sourceType.checkAndAddSyntheticRecordMethods(methodBindings, count);
+ count = methodBindings.length;
+ }
if (count != methodBindings.length)
System.arraycopy(methodBindings, 0, methodBindings = new MethodBinding[count], 0, count);
sourceType.tagBits &= ~(TagBits.AreMethodsSorted|TagBits.AreMethodsComplete); // in case some static imports reached already into this type
@@ -1007,6 +1012,8 @@ public class ClassScope extends Scope {
} else if (superclass.erasure().id == TypeIds.T_JavaLangRecord) {
if (!(this.referenceContext instanceof RecordDeclaration)) {
problemReporter().recordCannotExtendRecord(sourceType, superclassRef, superclass);
+ } else {
+ return connectRecordSuperclass();
}
} else if ((superclass.tagBits & TagBits.HierarchyHasProblems) != 0
|| !superclassRef.resolvedType.isValidBinding()) {
@@ -1024,7 +1031,7 @@ public class ClassScope extends Scope {
}
}
sourceType.tagBits |= TagBits.HierarchyHasProblems;
- sourceType.setSuperClass(getJavaLangObject());
+ sourceType.setSuperClass(sourceType.isRecord() ? getJavaLangRecord() : getJavaLangObject());
if ((sourceType.superclass.tagBits & TagBits.BeginHierarchyCheck) == 0)
detectHierarchyCycle(sourceType, sourceType.superclass, null);
return false; // reported some error against the source type
@@ -1067,6 +1074,16 @@ public class ClassScope extends Scope {
return !foundCycle;
}
+ private boolean connectRecordSuperclass() {
+ SourceTypeBinding sourceType = this.referenceContext.binding;
+ ReferenceBinding rootRecordType = getJavaLangRecord();
+ sourceType.setSuperClass(rootRecordType);
+ if ((rootRecordType.tagBits & TagBits.HasMissingType) != 0) {
+ sourceType.tagBits |= TagBits.HierarchyHasProblems; // mark missing supertpye
+ return false;
+ }
+ return !detectHierarchyCycle(sourceType, rootRecordType, null);
+ }
/*
Our current belief based on available JCK 1.3 tests is:
inherited member types are visible as a potential superclass.
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java
index 12446b0c1c..dab1383591 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java
@@ -2120,7 +2120,7 @@ protected int applyCloseableClassWhitelists(CompilerOptions options) {
protected boolean hasMethodWithNumArgs(char[] selector, int numArgs) {
for (MethodBinding methodBinding : unResolvedMethods()) {
- if (CharOperation.equals(methodBinding.selector, TypeConstants.CLOSE)
+ if (CharOperation.equals(methodBinding.selector, selector)
&& methodBinding.parameters.length == numArgs) {
return true;
}
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 0742835abe..525857c264 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
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2019 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -8,6 +8,10 @@
*
* SPDX-License-Identifier: EPL-2.0
*
+ * This is an implementation of an early-draft specification developed under the Java
+ * Community Process (JCP) and is made available for testing and evaluation purposes
+ * only. The code is not compatible with any specification of the JCP.
+ *
* Contributors:
* IBM Corporation - initial API and implementation
* Stephan Herrmann - Contributions for
@@ -2859,6 +2863,11 @@ public abstract class Scope {
return unitScope.environment.getResolvedJavaBaseType(TypeConstants.JAVA_LANG_ENUM, this);
}
+ public final ReferenceBinding getJavaLangRuntimeObjectMethods() {
+ CompilationUnitScope unitScope = compilationUnitScope();
+ unitScope.recordQualifiedReference(TypeConstants.JAVA_LANG_RUNTIME_OBJECTMETHODS);
+ return unitScope.environment.getResolvedJavaBaseType(TypeConstants.JAVA_LANG_RUNTIME_OBJECTMETHODS, this);
+ }
public final ReferenceBinding getJavaLangInvokeLambdaMetafactory() {
CompilationUnitScope unitScope = compilationUnitScope();
unitScope.recordQualifiedReference(TypeConstants.JAVA_LANG_INVOKE_LAMBDAMETAFACTORY);
@@ -2889,6 +2898,12 @@ public abstract class Scope {
return unitScope.environment.getResolvedJavaBaseType(TypeConstants.JAVA_LANG_OBJECT, this);
}
+ public final ReferenceBinding getJavaLangRecord() {
+ CompilationUnitScope unitScope = compilationUnitScope();
+ unitScope.recordQualifiedReference(TypeConstants.JAVA_LANG_RECORD);
+ return unitScope.environment.getResolvedJavaBaseType(TypeConstants.JAVA_LANG_RECORD, this);
+ }
+
public final ReferenceBinding getJavaLangString() {
CompilationUnitScope unitScope = compilationUnitScope();
unitScope.recordQualifiedReference(TypeConstants.JAVA_LANG_STRING);
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 1ccd6b5eb6..23415bf888 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
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2019 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -58,6 +58,7 @@
package org.eclipse.jdt.internal.compiler.lookup;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
@@ -133,6 +134,7 @@ public class SourceTypeBinding extends ReferenceBinding {
public HashSet<SourceTypeBinding> nestMembers;
private boolean isRecordDeclaration = false;
+ private FieldBinding[] recordComponents; // cache
public SourceTypeBinding(char[][] compoundName, PackageBinding fPackage, ClassScope scope) {
this.compoundName = compoundName;
this.fPackage = fPackage;
@@ -844,6 +846,110 @@ public SyntheticMethodBinding addSyntheticBridgeMethod(MethodBinding inheritedMe
}
return accessMethod;
}
+/* JLS 14 Record - Preview - begin */
+public MethodBinding[] checkAndAddSyntheticRecordMethods(MethodBinding[] methodBindings, int count) {
+ if (!this.isRecordDeclaration)
+ return methodBindings;
+ List<MethodBinding> implicitMethods = checkAndAddSyntheticRecordComponentAccessors(methodBindings);
+ implicitMethods = checkAndAddSyntheticRecordOverrideMethods(methodBindings, implicitMethods);
+ for (int i = 0; i < count; ++i)
+ implicitMethods.add(methodBindings[i]);
+ return implicitMethods.toArray(new MethodBinding[0]);
+}
+public List<MethodBinding> checkAndAddSyntheticRecordOverrideMethods(MethodBinding[] methodBindings, List<MethodBinding> implicitMethods) {
+ if (!hasMethodWithNumArgs(TypeConstants.TOSTRING, 0)) {
+ MethodBinding m = addSyntheticRecordOverrideMethod(TypeConstants.TOSTRING, implicitMethods.size());
+ implicitMethods.add(m);
+ }
+ if (!hasMethodWithNumArgs(TypeConstants.HASHCODE, 0)) {
+ MethodBinding m = addSyntheticRecordOverrideMethod(TypeConstants.HASHCODE, implicitMethods.size());
+ implicitMethods.add(m);
+ }
+ boolean isEqualsPresent = Arrays.stream(methodBindings)
+ .filter(m -> CharOperation.equals(TypeConstants.EQUALS, m.selector))
+ .anyMatch(m -> m.parameters != null || m.parameters.length == 1 &&
+ m.parameters[0].equals(this.scope.getJavaLangObject()));
+ if (!isEqualsPresent) {
+ MethodBinding m = addSyntheticRecordOverrideMethod(TypeConstants.EQUALS, implicitMethods.size());
+ implicitMethods.add(m);
+ }
+ return implicitMethods;
+}
+public List<MethodBinding> checkAndAddSyntheticRecordComponentAccessors(MethodBinding[] methodBindings) {
+ List<MethodBinding> implicitMethods = new ArrayList<>(0);
+ if (this.fields == null)
+ return implicitMethods;
+ // JLS 14 8.10.3 Item 2 create the accessors for the fields if required
+ /*
+ * An implicitly declared public accessor method with the same name as the record component,
+ * whose return type is the declared type of the record component,
+ * unless a public method with the same signature is explicitly declared in the body of the declaration of R.
+ */
+
+ List<String> missingNames = Arrays.stream(this.fields) // initialize with all the record components
+ .filter(f -> f.isRecordComponent())
+ .map(f -> new String(f.name))
+ .collect(Collectors.toList());
+
+ if (this.methods != null) {
+ List<String> candidates =
+ Arrays.stream(methodBindings)
+ .filter(m -> m.selector != null && m.selector.length > 0)
+ .filter(m -> missingNames.contains(new String(m.selector)))
+ .filter(m -> m.parameters == null || m.parameters.length == 0)
+ .map(m -> new String(m.selector))
+ .collect(Collectors.toList());
+ missingNames.removeAll(candidates);
+ }
+ int missingCount = missingNames.size();
+ for (int i = 0; i < missingCount; ++i) {
+ implicitMethods.add(addSyntheticRecordComponentAccessor(missingNames.get(i).toCharArray(), i));
+ }
+ return implicitMethods;
+}
+/* Add a new synthetic component accessor for the recordtype. Selector should be identical to component name.
+ * char[] component name of the record
+*/
+public SyntheticMethodBinding addSyntheticRecordComponentAccessor(char[] selector, int index) {
+ if (!isPrototype()) throw new IllegalStateException();
+ if (this.synthetics == null)
+ this.synthetics = new HashMap[MAX_SYNTHETICS];
+ if (this.synthetics[SourceTypeBinding.METHOD_EMUL] == null)
+ this.synthetics[SourceTypeBinding.METHOD_EMUL] = new HashMap(5);
+
+ SyntheticMethodBinding accessMethod = null;
+ SyntheticMethodBinding[] accessors = (SyntheticMethodBinding[]) this.synthetics[SourceTypeBinding.METHOD_EMUL].get(selector);
+ accessMethod = new SyntheticMethodBinding(this, getField(selector, true), index);
+ if (accessors == null) {
+ this.synthetics[SourceTypeBinding.METHOD_EMUL].put(selector, accessors = new SyntheticMethodBinding[2]);
+ accessors[0] = accessMethod;
+ } else {
+ if ((accessMethod = accessors[0]) == null) {
+ accessors[0] = accessMethod;
+ }
+ }
+ return accessMethod;
+}
+public SyntheticMethodBinding addSyntheticRecordOverrideMethod(char[] selector, int index) {
+ if (this.synthetics == null)
+ this.synthetics = new HashMap[MAX_SYNTHETICS];
+ if (this.synthetics[SourceTypeBinding.METHOD_EMUL] == null)
+ this.synthetics[SourceTypeBinding.METHOD_EMUL] = new HashMap(5);
+
+ SyntheticMethodBinding accessMethod = null;
+ SyntheticMethodBinding[] accessors = (SyntheticMethodBinding[]) this.synthetics[SourceTypeBinding.METHOD_EMUL].get(selector);
+ accessMethod = new SyntheticMethodBinding(this, selector, index);
+ if (accessors == null) {
+ this.synthetics[SourceTypeBinding.METHOD_EMUL].put(selector, accessors = new SyntheticMethodBinding[2]);
+ accessors[0] = accessMethod;
+ } else {
+ if ((accessMethod = accessors[0]) == null) {
+ accessors[0] = accessMethod;
+ }
+ }
+ return accessMethod;
+}
+/* JLS 14 Record - Preview - end */
boolean areFieldsInitialized() {
if (!isPrototype())
return this.prototype.areFieldsInitialized();
@@ -987,6 +1093,7 @@ public FieldBinding[] fields() {
}
}
this.tagBits |= TagBits.AreFieldsComplete;
+ computeRecordComponents();
return this.fields;
}
/**
@@ -2375,7 +2482,7 @@ protected boolean hasMethodWithNumArgs(char[] selector, int numArgs) {
// otherwise don't trigger unResolvedMethods() which would actually resolve!
if (this.scope != null) {
for (AbstractMethodDeclaration method : this.scope.referenceContext.methods) {
- if (CharOperation.equals(method.selector, TypeConstants.CLOSE)) {
+ if (CharOperation.equals(method.selector, selector)) {
if (numArgs == 0) {
if (method.arguments == null)
return true;
@@ -2811,24 +2918,26 @@ public List<String> getNestMembers() {
.collect(Collectors.toList());
return list;
}
-/* Get the field bindings in the order of record component declaration */
-public List<FieldBinding> getRecordComponents() {
- if (!this.isRecordDeclaration)
- return null;
- RecordDeclaration rd = (RecordDeclaration) this.scope.referenceContext;
+/* Get the field bindings in the order of record component declaration
+ * should be called only after a called to fields() */
+public FieldBinding[] getRecordComponents() {
+ return this.recordComponents;
+}
+public void computeRecordComponents() {
+ if (!this.isRecordDeclaration || this.recordComponents != null)
+ return;
+ List<String> recordComponentNames = Arrays.stream(((RecordDeclaration) this.scope.referenceContext).getArgs())
+ .map(arg -> new String(arg.name))
+ .collect(Collectors.toList());
List<FieldBinding> list = new ArrayList<>();
- Argument[] args = rd.getArgs();
- if (args != null) {
- for (Argument arg : args) {
- for (FieldBinding f : this.fields) {
- if (CharOperation.equals(f.name, arg.name)) {
- list.add(f);
- continue;
- }
+ for (String rc : recordComponentNames) {
+ for (FieldBinding f : this.fields) {
+ if (rc.equals(new String(f.name))) {
+ list.add(f);
}
}
}
- return list;
+ this.recordComponents = list.toArray(new FieldBinding[0]);
}
public void cleanUp() {
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 fd03c25fbb..e91eee8ce6 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
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2017 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -8,6 +8,10 @@
*
* SPDX-License-Identifier: EPL-2.0
*
+ * This is an implementation of an early-draft specification developed under the Java
+ * Community Process (JCP) and is made available for testing and evaluation purposes
+ * only. The code is not compatible with any specification of the JCP.
+ *
* Contributors:
* IBM Corporation - initial API and implementation
* Stephan Herrmann - Contribution for
@@ -72,13 +76,15 @@ public class SyntheticMethodBinding extends MethodBinding {
* Is never directly materialized in bytecode
*/
public static final int SerializableMethodReference = 18;
+ public static final int RecordOverrideToString = 19;
+ public static final int RecordOverrideHashCode = 20;
+ public static final int RecordOverrideEquals = 21;
public int sourceStart = 0; // start position of the matching declaration
public int index; // used for sorting access methods in the class file
public int fakePaddedParameters = 0; // added in synthetic constructor to avoid name clash.
public SyntheticMethodBinding(FieldBinding targetField, boolean isReadAccess, boolean isSuperAccess, ReferenceBinding declaringClass) {
-
this.modifiers = ClassFileConstants.AccDefault | ClassFileConstants.AccStatic | ClassFileConstants.AccSynthetic;
this.tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved);
SourceTypeBinding declaringSourceType = (SourceTypeBinding) declaringClass;
@@ -151,7 +157,6 @@ public class SyntheticMethodBinding extends MethodBinding {
setSelector(CharOperation.concat(TypeConstants.SYNTHETIC_ACCESS_METHOD_PREFIX, String.valueOf(++methodId).toCharArray()));
}
} while (needRename);
-
// retrieve sourceStart position for the target field for line number attributes
FieldDeclaration[] fieldDecls = declaringSourceType.scope.referenceContext.fields;
if (fieldDecls != null) {
@@ -440,6 +445,56 @@ public class SyntheticMethodBinding extends MethodBinding {
this.index = methodId;
}
+ public SyntheticMethodBinding(ReferenceBinding declaringClass, FieldBinding targetField, int index) {
+ SourceTypeBinding declaringSourceType = (SourceTypeBinding) declaringClass;
+ assert declaringSourceType.isRecord();
+ this.modifiers = ClassFileConstants.AccPublic;
+ this.tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved);
+ this.parameters = Binding.NO_PARAMETERS;
+ this.returnType = targetField.type;
+ this.selector = targetField.name;
+ this.targetReadField = targetField;
+ this.purpose = SyntheticMethodBinding.FieldReadAccess;
+ this.thrownExceptions = Binding.NO_EXCEPTIONS;
+ this.declaringClass = declaringSourceType;
+ this.setTypeAnnotations(targetField.getAnnotations());
+ this.index = index;
+
+ // retrieve sourceStart position for the target field for line number attributes
+ FieldDeclaration[] fieldDecls = declaringSourceType.scope.referenceContext.fields;
+ if (fieldDecls != null) {
+ for (int i = 0, max = fieldDecls.length; i < max; i++) {
+ if (fieldDecls[i].binding == targetField) {
+ this.sourceStart = fieldDecls[i].sourceStart;
+ return;
+ }
+ }
+ }
+ this.sourceStart = declaringSourceType.scope.referenceContext.sourceStart;
+ }
+ public SyntheticMethodBinding(ReferenceBinding declaringClass, char[] selector, int index) {
+ SourceTypeBinding declaringSourceType = (SourceTypeBinding) declaringClass;
+ assert declaringSourceType.isRecord();
+ this.declaringClass = declaringSourceType;
+ this.modifiers = ClassFileConstants.AccPublic;
+ this.tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved);
+ this.selector = selector;
+ this.thrownExceptions = Binding.NO_EXCEPTIONS;
+ if (selector == TypeConstants.TOSTRING) {
+ this.returnType = declaringSourceType.scope.getJavaLangString();
+ this.parameters = Binding.NO_PARAMETERS;
+ this.purpose = SyntheticMethodBinding.RecordOverrideToString;
+ } else if (selector == TypeConstants.HASHCODE) {
+ this.returnType = TypeBinding.INT;
+ this.parameters = Binding.NO_PARAMETERS;
+ this.purpose = SyntheticMethodBinding.RecordOverrideHashCode;
+ } else if (selector == TypeConstants.EQUALS) {
+ this.returnType = TypeBinding.BOOLEAN;
+ this.parameters = new TypeBinding[] {declaringSourceType.scope.getJavaLangObject()};
+ this.purpose = SyntheticMethodBinding.RecordOverrideEquals;
+ }
+ this.index = index;
+ }
/**
* An constructor accessor is a constructor with an extra argument (declaringClass), in case of
* collision with an existing constructor, then add again an extra argument (declaringClass again).
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeConstants.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeConstants.java
index 34dc674f0d..00c2ab08a8 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeConstants.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeConstants.java
@@ -237,6 +237,7 @@ public interface TypeConstants {
char[][] JAVA_LANG_INVOKE_METHODHANDLES = {JAVA, LANG, INVOKE, "MethodHandles".toCharArray()}; //$NON-NLS-1$
char[][] JAVA_LANG_AUTOCLOSEABLE = {JAVA, LANG, "AutoCloseable".toCharArray()}; //$NON-NLS-1$
char[] CLOSE = "close".toCharArray(); //$NON-NLS-1$
+ char[][] JAVA_LANG_RUNTIME_OBJECTMETHODS = {JAVA, LANG, RUNTIME, "ObjectMethods".toCharArray()}; //$NON-NLS-1$
// known helper functions for closing a Closeable (all receive a Closeable as their first argument):
public static class CloseMethodRecord {
public char[][] typeName;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java
index 9cbb80d014..5b4254fa7f 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java
@@ -10710,8 +10710,9 @@ protected void consumeRecordDeclaration() {
if (length == 0 && !containsComment(rd.bodyStart, rd.bodyEnd)) {
rd.bits |= ASTNode.UndocumentedEmptyBlock;
}
- rd.createImplicitAccessors(this.problemReporter);
- rd.createImplicitRecordOverrideMethods(this.problemReporter);
+ TypeReference superClass = new QualifiedTypeReference(TypeConstants.JAVA_LANG_RECORD, new long[] {0});
+ superClass.bits |= ASTNode.IsSuperType;
+ rd.superclass = superClass;
rd.declarationSourceEnd = flushCommentsDefinedPriorTo(this.endStatementPosition);
}
protected void consumeRecordHeaderPart() {
@@ -10747,6 +10748,8 @@ protected void consumeRecordComponentHeaderRightParen() {
length);
rd.setArgs(args);
convertToFields(rd, args);
+ } else {
+ rd.setArgs(ASTNode.NO_ARGUMENTS);
}
rd.bodyStart = this.rParenPos+1;
this.listLength = 0; // reset this.listLength after having read all parameters

Back to the top