You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2021/09/25 09:56:49 UTC
[groovy] branch master updated: Tweak compact constructor support
This is an automated email from the ASF dual-hosted git repository.
sunlan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push:
new 73bbb0b Tweak compact constructor support
73bbb0b is described below
commit 73bbb0bf859e681996e6379735f727be3e11d54e
Author: Daniel Sun <su...@apache.org>
AuthorDate: Sat Sep 25 17:43:08 2021 +0800
Tweak compact constructor support
---
.../apache/groovy/parser/antlr4/AstBuilder.java | 38 +++++++++++++++++--
.../core/RecordDeclaration_10x.groovy | 41 +++++++++++++++++++++
.../core/RecordDeclaration_11x.groovy | 42 +++++++++++++++++++++
.../core/RecordDeclaration_12x.groovy | 43 ++++++++++++++++++++++
.../fail/RecordDeclaration_09x.groovy | 25 +++++++++++++
.../groovy/parser/antlr4/GroovyParserTest.groovy | 3 ++
.../groovy/parser/antlr4/SyntaxErrorTest.groovy | 1 +
7 files changed, 189 insertions(+), 4 deletions(-)
diff --git a/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java b/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
index fcb04fc..684c7e5 100644
--- a/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
+++ b/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
@@ -360,6 +360,7 @@ import static org.apache.groovy.parser.antlr4.GroovyParser.VAR;
import static org.apache.groovy.parser.antlr4.util.PositionConfigureUtils.configureAST;
import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveVoid;
import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.assignS;
import static org.codehaus.groovy.ast.tools.GeneralUtils.assignX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.block;
import static org.codehaus.groovy.ast.tools.GeneralUtils.callX;
@@ -1962,13 +1963,35 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
if (!methodName.equals(className)) {
createParsingFailedException("Compact constructor should have the same name with record: " + className, ctx.methodName());
}
- ClassNode returnType = ClassHelper.VOID_TYPE;
+ ClassNode returnType = ClassHelper.MAP_TYPE.getPlainNodeReference();
Parameter[] header = classNode.getNodeMetaData(RECORD_HEADER);
Objects.requireNonNull(classNode, "record header should not be null");
- Parameter[] parameters = cloneParams(header);
+
+ final Parameter[] parameters = cloneParams(header);
Statement code = this.visitMethodBody(ctx.methodBody());
- MethodNode methodNode = classNode.addSyntheticMethod(RECORD_COMPACT_CONSTRUCTOR_NAME, ACC_PRIVATE, returnType, parameters, ClassNode.EMPTY_ARRAY, code);
+ code.visit(new CodeVisitorSupport() {
+ @Override
+ public void visitPropertyExpression(PropertyExpression expression) {
+ final String propertyName = expression.getPropertyAsString();
+ if (THIS_STR.equals(expression.getObjectExpression().getText()) && Arrays.stream(parameters).anyMatch(p -> p.getName().equals(propertyName))) {
+ createParsingFailedException("Cannot assign a value to final variable `" + propertyName + "`", expression.getProperty());
+ }
+ super.visitPropertyExpression(expression);
+ }
+ });
+
+ final String closureVarName = "$c" + System.nanoTime();
+ List<Expression> argExpressionList = Arrays.stream(parameters).flatMap(p -> {
+ String parameterName = p.getName();
+ return Stream.of(constX(parameterName), varX(parameterName));
+ }).collect(Collectors.toList());
+ Statement block = block(
+ declS(localVarX(closureVarName), closureX(code)),
+ stmt(callX(varX(closureVarName), "call")),
+ returnS(callX(ClassHelper.makeCached(Maps.class), "of", args(argExpressionList)))
+ );
+ MethodNode methodNode = classNode.addSyntheticMethod(RECORD_COMPACT_CONSTRUCTOR_NAME, ACC_PRIVATE, returnType, parameters, ClassNode.EMPTY_ARRAY, block);
modifierManager.attachAnnotations(methodNode);
attachMapConstructorAnnotationToRecord(classNode, parameters);
@@ -1993,7 +2016,14 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
Arrays.stream(parameters)
.map(mapper::apply)
.collect(Collectors.toList());
- tupleConstructorAnnotationNode.setMember("pre", closureX(block(stmt(callX(varX("this"), RECORD_COMPACT_CONSTRUCTOR_NAME, args(argExpressionList))))));
+ final String resultVarName = "$r" + System.nanoTime();
+ tupleConstructorAnnotationNode.setMember("pre", closureX(block(
+ declS(localVarX(resultVarName), castX(ClassHelper.MAP_TYPE.getPlainNodeReference(), callX(varX("this"), RECORD_COMPACT_CONSTRUCTOR_NAME, args(argExpressionList)))),
+ assignS(
+ new TupleExpression(Arrays.stream(parameters).map(p -> varX(p.getName())).collect(Collectors.toList())),
+ listX(Arrays.stream(parameters).map(p -> castX(p.getOriginType(), callX(varX(resultVarName), "get", args(constX(p.getName()))))).collect(Collectors.toList()))
+ )
+ )));
classNode.addAnnotation(tupleConstructorAnnotationNode);
}
diff --git a/src/test-resources/core/RecordDeclaration_10x.groovy b/src/test-resources/core/RecordDeclaration_10x.groovy
new file mode 100644
index 0000000..f1de04b
--- /dev/null
+++ b/src/test-resources/core/RecordDeclaration_10x.groovy
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package core
+
+record Point(int x, int y, String color) {
+ public Point {
+ x = -x;
+ Objects.requireNonNull(color);
+ color = color.toUpperCase();
+ }
+
+ public Point(int x, int y) {
+ this(x, y, "Blue");
+ }
+}
+
+def p1 = new Point(5, 10, "Green")
+assert -5 == p1.x()
+assert 10 == p1.y()
+assert 'GREEN' == p1.color()
+
+def p2 = new Point(0, 20)
+assert 0 == p2.x()
+assert 20 == p2.y()
+assert 'BLUE' == p2.color()
diff --git a/src/test-resources/core/RecordDeclaration_11x.groovy b/src/test-resources/core/RecordDeclaration_11x.groovy
new file mode 100644
index 0000000..961cdee
--- /dev/null
+++ b/src/test-resources/core/RecordDeclaration_11x.groovy
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package core
+
+@groovy.transform.CompileStatic
+record Point(int x, int y, String color) {
+ public Point {
+ x = -x;
+ Objects.requireNonNull(color);
+ color = color.toUpperCase();
+ }
+
+ public Point(int x, int y) {
+ this(x, y, "Blue");
+ }
+}
+
+def p1 = new Point(5, 10, "Green")
+assert -5 == p1.x()
+assert 10 == p1.y()
+assert 'GREEN' == p1.color()
+
+def p2 = new Point(0, 20)
+assert 0 == p2.x()
+assert 20 == p2.y()
+assert 'BLUE' == p2.color()
diff --git a/src/test-resources/core/RecordDeclaration_12x.groovy b/src/test-resources/core/RecordDeclaration_12x.groovy
new file mode 100644
index 0000000..2f54939
--- /dev/null
+++ b/src/test-resources/core/RecordDeclaration_12x.groovy
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package core
+
+@groovy.transform.CompileStatic
+record Point(int x, int y, String color) {
+ public Point {
+ x = -x;
+ if (x < 0) return
+ Objects.requireNonNull(color);
+ color = color.toUpperCase();
+ }
+
+ public Point(int x, int y) {
+ this(x, y, "Blue");
+ }
+}
+
+def p1 = new Point(5, 10, "Green")
+assert -5 == p1.x()
+assert 10 == p1.y()
+assert 'Green' == p1.color()
+
+def p2 = new Point(0, 20)
+assert 0 == p2.x()
+assert 20 == p2.y()
+assert 'BLUE' == p2.color()
diff --git a/src/test-resources/fail/RecordDeclaration_09x.groovy b/src/test-resources/fail/RecordDeclaration_09x.groovy
new file mode 100644
index 0000000..95f7c3b
--- /dev/null
+++ b/src/test-resources/fail/RecordDeclaration_09x.groovy
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package core
+
+record Point(int x, int y, String color) {
+ public Point {
+ this.x = -x;
+ }
+}
diff --git a/src/test/org/apache/groovy/parser/antlr4/GroovyParserTest.groovy b/src/test/org/apache/groovy/parser/antlr4/GroovyParserTest.groovy
index e1dabc4..de1e3d4 100644
--- a/src/test/org/apache/groovy/parser/antlr4/GroovyParserTest.groovy
+++ b/src/test/org/apache/groovy/parser/antlr4/GroovyParserTest.groovy
@@ -367,6 +367,9 @@ final class GroovyParserTest extends GroovyTestCase {
doRunAndTestAntlr4('core/RecordDeclaration_07x.groovy')
doRunAndTestAntlr4('core/RecordDeclaration_08x.groovy')
doRunAndTestAntlr4('core/RecordDeclaration_09x.groovy')
+ doRunAndTestAntlr4('core/RecordDeclaration_10x.groovy')
+ doRunAndTestAntlr4('core/RecordDeclaration_11x.groovy')
+ doRunAndTestAntlr4('core/RecordDeclaration_12x.groovy')
}
void "test groovy core - AnnotationDeclaration"() {
diff --git a/src/test/org/apache/groovy/parser/antlr4/SyntaxErrorTest.groovy b/src/test/org/apache/groovy/parser/antlr4/SyntaxErrorTest.groovy
index 3a660ee..9e10086 100644
--- a/src/test/org/apache/groovy/parser/antlr4/SyntaxErrorTest.groovy
+++ b/src/test/org/apache/groovy/parser/antlr4/SyntaxErrorTest.groovy
@@ -441,6 +441,7 @@ final class SyntaxErrorTest extends GroovyTestCase {
TestUtils.doRunAndShouldFail('fail/RecordDeclaration_06x.groovy')
TestUtils.doRunAndShouldFail('fail/RecordDeclaration_07x.groovy')
TestUtils.doRunAndShouldFail('fail/RecordDeclaration_08x.groovy')
+ TestUtils.doRunAndShouldFail('fail/RecordDeclaration_09x.groovy')
}
void 'test groovy core - Array'() {