Skip to content

Commit

Permalink
GROOVY-6610
Browse files Browse the repository at this point in the history
  • Loading branch information
eric-milles committed Mar 19, 2021
1 parent 91c08da commit 1f25ee5
Show file tree
Hide file tree
Showing 7 changed files with 318 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,31 @@ public void testCompileStatic6276() {
runConformTest(sources, "");
}

@Test
public void testCompileStatic6610() {
//@formatter:off
String[] sources = {
"Main.groovy",
"@groovy.transform.CompileStatic\n" +
"class Outer {\n" +
" private static Integer VALUE = 42\n" +
" static class Inner {\n" +
" public final String value\n" +
" Inner(String string) {\n" +
" value = string\n" +
" }\n" +
" Inner() {\n" +
" this(VALUE.toString())\n" +
" }\n" +
" }\n" +
"}\n" +
"print new Outer.Inner().value\n",
};
//@formatter:on

runConformTest(sources, "42");
}

@Test
public void testCompileStatic6904() {
//@formatter:off
Expand Down
4 changes: 1 addition & 3 deletions base/org.codehaus.groovy25/.checkstyle
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,8 @@
<file-match-pattern match-pattern="groovy/transform/LogASTTransformation.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/NewifyASTTransformation.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/sc/StaticCompilationVisitor.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/sc/transformers/BinaryExpressionTransformer.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/sc/transformers/(Binary|ConstructorCall|MethodCall|Variable)ExpressionTransformer.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/sc/transformers/CompareToNullExpression.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/sc/transformers/ConstructorCallTransformer.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/sc/transformers/MethodCallExpressionTransformer.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/stc/StaticTypeCheckingSupport.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/stc/StaticTypeCheckingVisitor.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/trait/SuperCallTraitTransformer.java" include-pattern="false" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* 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.sc.transformers;

import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.FieldNode;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.PropertyExpression;
import org.codehaus.groovy.ast.expr.VariableExpression;
import org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys;
import org.codehaus.groovy.transform.stc.StaticTypesMarker;

import static org.codehaus.groovy.ast.tools.GeneralUtils.*;

/**
* Transformer for VariableExpression the bytecode backend wouldn't be able to
* handle otherwise.
*/
public class VariableExpressionTransformer {

public Expression transformVariableExpression(VariableExpression expr) {
Expression trn = tryTransformDelegateToProperty(expr);
if (trn != null) {
return trn;
}
trn = tryTransformPrivateFieldAccess(expr);
if (trn != null) {
return trn;
}
return expr;
}

private static Expression tryTransformDelegateToProperty(VariableExpression expr) {
// we need to transform variable expressions that go to a delegate
// to a property expression, as ACG would lose the information in
// processClassVariable before it reaches any makeCall, that could handle it
Object val = expr.getNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER);
if (val == null || val.equals(expr.getName())) return null;

// TODO handle the owner and delegate cases better for nested scenarios and potentially remove the need for the implicit this case
VariableExpression receiver = new VariableExpression("owner".equals(val) ? (String) val : "delegate".equals(val) ? (String) val : "this");
// GROOVY-9136 -- object expression should not overlap source range of property; property stands in for original variable expression
receiver.setLineNumber(expr.getLineNumber());
receiver.setColumnNumber(expr.getColumnNumber());

PropertyExpression pexp = new PropertyExpression(receiver, expr.getName());
pexp.getProperty().setSourcePosition(expr);
pexp.copyNodeMetaData(expr);
pexp.setImplicitThis(true);

ClassNode owner = expr.getNodeMetaData(StaticCompilationMetadataKeys.PROPERTY_OWNER);
if (owner != null) {
receiver.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, owner);
receiver.putNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER, val);
}
pexp.removeNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER);

return pexp;
}

private static Expression tryTransformPrivateFieldAccess(VariableExpression expr) {
FieldNode field = expr.getNodeMetaData(StaticTypesMarker.PV_FIELDS_ACCESS);
if (field == null) {
field = expr.getNodeMetaData(StaticTypesMarker.PV_FIELDS_MUTATION);
}
if (field != null) {
// access to a private field from a section of code that normally doesn't have access to it, like a
// closure or an inner class
/* GRECLIPSE edit -- GROOVY-6610
VariableExpression receiver = new VariableExpression("this");
PropertyExpression pexp = new PropertyExpression(
receiver,
expr.getName()
);
pexp.setImplicitThis(true);
pexp.getProperty().setSourcePosition(expr);
*/
PropertyExpression pexp = !field.isStatic() ? thisPropX(true, expr.getName())
: (PropertyExpression) propX(classX(field.getDeclaringClass()), expr.getName());
pexp.getProperty().setSourcePosition(expr);
// GRECLIPSE end
// put the receiver inferred type so that the class writer knows that it will have to call a bridge method
pexp.getObjectExpression().putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, field.getDeclaringClass());
// add inferred type information
pexp.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, field.getOriginType());
return pexp;
}
return null;
}
}
2 changes: 1 addition & 1 deletion base/org.codehaus.groovy30/.checkstyle
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
<file-match-pattern match-pattern="groovy/transform/LogASTTransformation.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/sc/StaticCompilationVisitor.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/sc/StaticCompileTransformation.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/sc/transformers/(Binary|MethodCall)ExpressionTransformer.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/sc/transformers/(Binary|MethodCall|Variable)ExpressionTransformer.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/sc/transformers/CompareToNullExpression.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/stc/AbstractExtensionMethodCache.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/stc/StaticTypeCheckingSupport.java" include-pattern="false" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* 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.sc.transformers;

import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.FieldNode;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.PropertyExpression;
import org.codehaus.groovy.ast.expr.VariableExpression;
import org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys;
import org.codehaus.groovy.transform.stc.StaticTypesMarker;

import static org.codehaus.groovy.ast.tools.GeneralUtils.*;

/**
* Transformer for VariableExpression the bytecode backend wouldn't be able to
* handle otherwise.
*/
public class VariableExpressionTransformer {

public Expression transformVariableExpression(final VariableExpression expr) {
Expression trn = tryTransformDelegateToProperty(expr);
if (trn == null) {
trn = tryTransformPrivateFieldAccess(expr);
}
return trn != null ? trn : expr;
}

private static Expression tryTransformDelegateToProperty(final VariableExpression expr) {
// we need to transform variable expressions that go to a delegate
// to a property expression, as ACG would lose the information in
// processClassVariable before it reaches any makeCall, that could handle it
Object val = expr.getNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER);
if (val == null || val.equals(expr.getName())) return null;

// TODO: handle the owner and delegate cases better for nested scenarios and potentially remove the need for the implicit this case
Expression receiver = varX("owner".equals(val) ? (String) val : "delegate".equals(val) ? (String) val : "this");
// GROOVY-9136 -- object expression should not overlap source range of property; property stands in for original variable expression
receiver.setLineNumber(expr.getLineNumber());
receiver.setColumnNumber(expr.getColumnNumber());

PropertyExpression pexp = propX(receiver, expr.getName());
pexp.getProperty().setSourcePosition(expr);
pexp.copyNodeMetaData(expr);
pexp.setImplicitThis(true);

ClassNode owner = expr.getNodeMetaData(StaticCompilationMetadataKeys.PROPERTY_OWNER);
if (owner != null) {
receiver.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, owner);
receiver.putNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER, val);
}
pexp.removeNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER);

return pexp;
}

private static Expression tryTransformPrivateFieldAccess(final VariableExpression expr) {
FieldNode field = expr.getNodeMetaData(StaticTypesMarker.PV_FIELDS_ACCESS);
if (field == null) {
field = expr.getNodeMetaData(StaticTypesMarker.PV_FIELDS_MUTATION);
}
if (field != null) {
// access to a private field from a section of code that normally doesn't have access to it, like a closure or an inner class
PropertyExpression pexp = thisPropX(true, expr.getName());
// GRECLIPSE add -- GROOVY-6610
if (field.isStatic()) pexp = propX(classX(field.getDeclaringClass()), expr.getName());
// GRECLIPSE end
// store the declaring class so that the class writer knows that it will have to call a bridge method
pexp.getObjectExpression().putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, field.getDeclaringClass());
pexp.putNodeMetaData(StaticTypesMarker.DECLARATION_INFERRED_TYPE, field.getOriginType());
pexp.getProperty().setSourcePosition(expr);
return pexp;
}
return null;
}
}
2 changes: 1 addition & 1 deletion base/org.codehaus.groovy40/.checkstyle
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
<file-match-pattern match-pattern="groovy/transform/NotYetImplemented.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/sc/StaticCompilationVisitor.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/sc/StaticCompileTransformation.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/sc/transformers/(Binary|MethodCall)ExpressionTransformer.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/sc/transformers/(Binary|MethodCall|Variable)ExpressionTransformer.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/sc/transformers/CompareToNullExpression.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/stc/StaticTypeCheckingSupport.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/stc/StaticTypeCheckingVisitor.java" include-pattern="false" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* 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.sc.transformers;

import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.FieldNode;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.PropertyExpression;
import org.codehaus.groovy.ast.expr.VariableExpression;
import org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys;
import org.codehaus.groovy.transform.stc.StaticTypesMarker;

import static org.codehaus.groovy.ast.tools.GeneralUtils.*;

/**
* Transformer for VariableExpression the bytecode backend wouldn't be able to
* handle otherwise.
*/
public class VariableExpressionTransformer {

public Expression transformVariableExpression(final VariableExpression expr) {
Expression trn = tryTransformDelegateToProperty(expr);
if (trn == null) {
trn = tryTransformPrivateFieldAccess(expr);
}
return trn != null ? trn : expr;
}

private static Expression tryTransformDelegateToProperty(final VariableExpression expr) {
// we need to transform variable expressions that go to a delegate
// to a property expression, as ACG would lose the information in
// processClassVariable before it reaches any makeCall, that could handle it
Object val = expr.getNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER);
if (val == null || val.equals(expr.getName())) return null;

// TODO: handle the owner and delegate cases better for nested scenarios and potentially remove the need for the implicit this case
Expression receiver = varX("owner".equals(val) ? (String) val : "delegate".equals(val) ? (String) val : "this");
// GROOVY-9136 -- object expression should not overlap source range of property; property stands in for original variable expression
receiver.setLineNumber(expr.getLineNumber());
receiver.setColumnNumber(expr.getColumnNumber());

PropertyExpression pexp = propX(receiver, expr.getName());
pexp.getProperty().setSourcePosition(expr);
pexp.copyNodeMetaData(expr);
pexp.setImplicitThis(true);

ClassNode owner = expr.getNodeMetaData(StaticCompilationMetadataKeys.PROPERTY_OWNER);
if (owner != null) {
receiver.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, owner);
receiver.putNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER, val);
}
pexp.removeNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER);

return pexp;
}

private static Expression tryTransformPrivateFieldAccess(final VariableExpression expr) {
FieldNode field = expr.getNodeMetaData(StaticTypesMarker.PV_FIELDS_ACCESS);
if (field == null) {
field = expr.getNodeMetaData(StaticTypesMarker.PV_FIELDS_MUTATION);
}
if (field != null) {
// access to a private field from a section of code that normally doesn't have access to it, like a closure or an inner class
PropertyExpression pexp = thisPropX(true, expr.getName());
// GRECLIPSE add -- GROOVY-6610
if (field.isStatic()) pexp = propX(classX(field.getDeclaringClass()), expr.getName());
// GRECLIPSE end
// store the declaring class so that the class writer knows that it will have to call a bridge method
pexp.getObjectExpression().putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, field.getDeclaringClass());
pexp.putNodeMetaData(StaticTypesMarker.DECLARATION_INFERRED_TYPE, field.getOriginType());
pexp.getProperty().setSourcePosition(expr);
return pexp;
}
return null;
}
}

0 comments on commit 1f25ee5

Please sign in to comment.