[
https://issues.apache.org/jira/browse/GROOVY-12441?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18135431#comment-18135431
]
ASF GitHub Bot commented on GROOVY-12441:
-----------------------------------------
blackdrag commented on code in PR #3068:
URL: https://github.com/apache/groovy/pull/3068#discussion_r4428886494
##########
src/main/java/org/codehaus/groovy/transform/stc/SharedVariableExpressionRewriter.java:
##########
@@ -0,0 +1,525 @@
+/*
+ * 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 org.codehaus.groovy.transform.stc;
+
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.CodeVisitorSupport;
+import org.codehaus.groovy.ast.FieldNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.PropertyNode;
+import org.codehaus.groovy.ast.expr.ArrayExpression;
+import org.codehaus.groovy.ast.expr.AttributeExpression;
+import org.codehaus.groovy.ast.expr.BinaryExpression;
+import org.codehaus.groovy.ast.expr.BitwiseNegationExpression;
+import org.codehaus.groovy.ast.expr.BooleanExpression;
+import org.codehaus.groovy.ast.expr.CastExpression;
+import org.codehaus.groovy.ast.expr.ClassExpression;
+import org.codehaus.groovy.ast.expr.ClosureExpression;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
+import org.codehaus.groovy.ast.expr.ElvisOperatorExpression;
+import org.codehaus.groovy.ast.expr.EmptyExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.FieldExpression;
+import org.codehaus.groovy.ast.expr.GStringExpression;
+import org.codehaus.groovy.ast.expr.ListExpression;
+import org.codehaus.groovy.ast.expr.MapEntryExpression;
+import org.codehaus.groovy.ast.expr.MapExpression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.MethodPointerExpression;
+import org.codehaus.groovy.ast.expr.MethodReferenceExpression;
+import org.codehaus.groovy.ast.expr.NotExpression;
+import org.codehaus.groovy.ast.expr.PostfixExpression;
+import org.codehaus.groovy.ast.expr.PrefixExpression;
+import org.codehaus.groovy.ast.expr.PropertyExpression;
+import org.codehaus.groovy.ast.expr.RangeExpression;
+import org.codehaus.groovy.ast.expr.SpreadExpression;
+import org.codehaus.groovy.ast.expr.SpreadMapExpression;
+import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
+import org.codehaus.groovy.ast.expr.SwitchExpression;
+import org.codehaus.groovy.ast.expr.TernaryExpression;
+import org.codehaus.groovy.ast.expr.TupleExpression;
+import org.codehaus.groovy.ast.expr.UnaryMinusExpression;
+import org.codehaus.groovy.ast.expr.UnaryPlusExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.AssertStatement;
+import org.codehaus.groovy.ast.stmt.CaseStatement;
+import org.codehaus.groovy.ast.stmt.DoWhileStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.ForStatement;
+import org.codehaus.groovy.ast.stmt.IfStatement;
+import org.codehaus.groovy.ast.stmt.ReturnStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.ast.stmt.SwitchStatement;
+import org.codehaus.groovy.ast.stmt.SynchronizedStatement;
+import org.codehaus.groovy.ast.stmt.ThrowStatement;
+import org.codehaus.groovy.ast.stmt.WhileStatement;
+import org.codehaus.groovy.ast.stmt.YieldStatement;
+import org.codehaus.groovy.classgen.BytecodeExpression;
+
+import java.util.List;
+
+/**
+ * Gives each use of {@link VariableExpression#THIS_EXPRESSION} and
+ * {@link VariableExpression#SUPER_EXPRESSION} its own node.
+ *
+ * <p>Those two fields are process-wide singletons. Static type checking stores
+ * inferred types on the expression it is visiting, and one shared node cannot
+ * hold a type for every use. Leaving the singleton untyped makes static
+ * compilation fall back to the class currently being generated, which inside a
+ * closure is the closure class rather than the enclosing instance.
+ *
+ * <p>The walk mutates the tree in place and allocates a node only when a
+ * sentinel is actually present. Nodes that do not contain one keep their
+ * identity, so metadata already recorded on them is preserved.
+ */
+final class SharedVariableExpressionRewriter extends CodeVisitorSupport {
Review Comment:
This class has a huge amount of visitor and transformer code in it. Why
could you not use one of the existing base classes for this?
> STC gets confused when THIS_EXPRESSION is used
> ----------------------------------------------
>
> Key: GROOVY-12441
> URL: https://issues.apache.org/jira/browse/GROOVY-12441
> Project: Groovy
> Issue Type: Bug
> Affects Versions: 2.5.23, 3.0.25, 4.0.33, 5.1.3, 6.0.0
> Reporter: Björn Kautler
> Assignee: Daniel Sun
> Priority: Major
>
> If you use {{VariableExpression.THIS_EXPRESSION}} the STC and thus
> static-compile gets confused.
> I assume this is a bug, if it is expected, sorry for the noise.
> We use this at some places in the Spock transform.
> While trying to make Spock specs compatible with STC and SC, it was observed
> that STC stores type information at the current node and using
> {{VariableExpression.THIS_EXPRESSION}} then causes conflicting casts when
> used inside and outside a closure.
> As a work-around we can instead use {{{}new VariableExpression("this"){}}},
> but I guess this actually is a bug, thus this report.
> As an example.
> {code:java}
> class ASpec extends Specification {
> def "mock init closure"() {
> given:
> List<String> list = Mock(List) {
> size() >> 3
> }
> when:
> int size = list.size()
> then:
> size == 3
> }
> }
> {code}
> compiles in semantic analysis to
> {code:java}
> import spock.lang.*
> @org.spockframework.runtime.model.SpecMetadata(filename = 'script.groovy',
> line = 1)
> public class ASpec extends spock.lang.Specification {
> @org.spockframework.runtime.model.FeatureMetadata(name = 'mock init
> closure', ordinal = 0, line = 2, blocks =
> [@org.spockframework.runtime.model.BlockMetadata(kind =
> org.spockframework.runtime.model.BlockKind.SETUP, texts = []),
> @org.spockframework.runtime.model.BlockMetadata(kind =
> org.spockframework.runtime.model.BlockKind.WHEN, texts = []),
> @org.spockframework.runtime.model.BlockMetadata(kind =
> org.spockframework.runtime.model.BlockKind.THEN, texts = [])], parameterNames
> = [])
> public void $spock_feature_0_0() {
> org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new
> org.spockframework.runtime.ValueRecorder()
> org.spockframework.runtime.ErrorCollector $spock_errorCollector =
> org.spockframework.runtime.ErrorRethrower.INSTANCE
> org.spockframework.runtime.SpockRuntime.callBlockEntered(this, 0)
> java.util.List<String> list =
> org.spockframework.runtime.SpecInternals.MockImpl(this, 'list',
> java.util.List, java.util.List, { ->
>
> this.getSpecificationContext().getMockController().addInteraction(new
> org.spockframework.mock.runtime.InteractionBuilder(5, 7, 'size() >>
> 3').addEqualTarget(it).addEqualMethodName('size').setArgListKind(true,
> false).addConstantResponse(3).build())
> })
> org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0)
> org.spockframework.runtime.SpockRuntime.callBlockEntered(this, 1)
> java.lang.Integer size = list.size()
> org.spockframework.runtime.SpockRuntime.callBlockExited(this, 1)
> org.spockframework.runtime.SpockRuntime.callBlockEntered(this, 2)
> try {
>
> org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector,
> $spock_valueRecorder.reset(), 'size == 3', 12, 5, null,
> $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2),
> $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0),
> size) ==
> $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), 3)))
> }
> catch (java.lang.Throwable $spock_condition_throwable) {
>
> org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector,
> $spock_valueRecorder, 'size == 3', 12, 5, null, $spock_condition_throwable)}
> finally {
> }
> org.spockframework.runtime.SpockRuntime.callBlockExited(this, 2)
> this.getSpecificationContext().getMockController().leaveScope()
> }
> }
> {code}
> The important part here is the two {{getSpecificationContext()}} calls, one
> inside the mock init closure, one as last line in the feature method, both
> added by the Spock transform.
> If those use {{VariableExpression.THIS_EXPRESSION}} then at runtime you get
> {{{}java.lang.ClassCastException: apackage.ASpec cannot be cast to
> apackage.ASpec$__spock_feature_0_0_closure1{}}}.
> If you use {{new VariableExpression("this")}} instead, it works as intended.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)