You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2019/01/21 04:43:50 UTC
[groovy] branch GROOVY_2_5_X updated: GROOVY-8873: Fails at runtime
with @CompileStatic and two nested with statements with ClassCastException
(closes #854)
This is an automated email from the ASF dual-hosted git repository.
paulk pushed a commit to branch GROOVY_2_5_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/GROOVY_2_5_X by this push:
new f71ddf6 GROOVY-8873: Fails at runtime with @CompileStatic and two nested with statements with ClassCastException (closes #854)
f71ddf6 is described below
commit f71ddf6b39002bdcc6e62a17c8e04cb4f82b445b
Author: Paul King <pa...@asert.com.au>
AuthorDate: Thu Jan 17 22:32:28 2019 +1000
GROOVY-8873: Fails at runtime with @CompileStatic and two nested with statements with ClassCastException (closes #854)
---
.../transform/stc/StaticTypeCheckingVisitor.java | 35 ++++++++++
.../transform/stc/WithSTCStandaloneTest.groovy | 75 ++++++++++++++++++++++
2 files changed, 110 insertions(+)
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
index 627e7d0..497f9a2 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -1725,6 +1725,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
if (visitor != null) visitor.visitProperty(propertyNode);
storeWithResolve(propertyNode.getOriginType(), receiver, propertyNode.getDeclaringClass(), propertyNode.isStatic(), expressionToStoreOn);
if (delegationData != null) {
+ delegationData = adjustData(delegationData, receiver, typeCheckingContext.delegationMetadata);
expressionToStoreOn.putNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER, delegationData);
}
return true;
@@ -3403,6 +3404,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
String data = chosenReceiver.getData();
if (data != null) {
+ data = adjustData(data, chosenReceiver.getType(), typeCheckingContext.delegationMetadata);
// the method which has been chosen is supposed to be a call on delegate or owner
// so we store the information so that the static compiler may reuse it
call.putNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER, data);
@@ -3461,6 +3463,39 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
}
+ // adjust data to handle cases like nested .with since we didn't have enough information earlier
+ // TODO see if we can make the earlier detection smarter and then remove this adjustment
+ private static String adjustData(String data, ClassNode type, DelegationMetadata dmd) {
+ StringBuilder path = new StringBuilder();
+ int i = 0;
+ String[] propertyPath = data.split("\\.");
+ while (dmd != null) {
+ int strategy = dmd.getStrategy();
+ ClassNode delegate = dmd.getType();
+ dmd = dmd.getParent();
+ switch (strategy) {
+ case Closure.DELEGATE_FIRST:
+ if (!delegate.isDerivedFrom(CLOSURE_TYPE) && !delegate.isDerivedFrom(type)) {
+ path.append("owner"); // must be non-delegate case
+ } else {
+ path.append("delegate");
+ }
+ break;
+ default:
+ if (i >= propertyPath.length) return data;
+ path.append(propertyPath[i]);
+ }
+ if (type.equals(delegate)) break;
+ i++;
+ if (dmd != null) path.append('.');
+ }
+ String result = path.toString();
+ if (!result.isEmpty()) {
+ return result;
+ }
+ return data;
+ }
+
/**
* e.g. c(b(a())), a() and b() are nested method call, but c() is not
* new C(b(a())) a() and b() are nested method call
diff --git a/src/test/groovy/transform/stc/WithSTCStandaloneTest.groovy b/src/test/groovy/transform/stc/WithSTCStandaloneTest.groovy
new file mode 100644
index 0000000..c36dd94
--- /dev/null
+++ b/src/test/groovy/transform/stc/WithSTCStandaloneTest.groovy
@@ -0,0 +1,75 @@
+/*
+ * 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 groovy.transform.stc
+
+/**
+ * Unit tests for static type checking : with method.
+ */
+class WithSTCStandaloneTest extends GroovyTestCase {
+ void testMethodAndPropertyAccessWithinNestedWithStatements() {
+ assertScript '''
+ import groovy.transform.CompileStatic
+
+ class Foo {
+ String foo = 'foo'
+ String foom() { 'foom' }
+ }
+
+ class Bar {
+ String bar = 'bar'
+ String barm() { 'barm' }
+ }
+
+ class Baz {
+ String baz = 'baz'
+ String bazm() { 'bazm' }
+ }
+
+ def other() { 'other' }
+
+ @CompileStatic
+ def main() {
+ new Foo().with {
+ assert other() == 'other'
+ assert foom() == 'foom'
+ assert foo == 'foo'
+ new Bar().with {
+ assert foo == 'foo'
+ assert bar == 'bar'
+ assert barm() == 'barm'
+ assert other() == 'other'
+ assert foom() == 'foom'
+ new Baz().with {
+ assert foo == 'foo'
+ assert bar == 'bar'
+ assert baz == 'baz'
+ assert barm() == 'barm'
+ assert other() == 'other'
+ assert foom() == 'foom'
+ assert bazm() == 'bazm'
+ }
+ }
+ }
+ }
+
+ main()
+ '''
+ }
+}