Skip to content

Commit b4e715c

Browse files
committed
GROOVY-8104
1 parent 8337794 commit b4e715c

File tree

7 files changed

+978
-3
lines changed

7 files changed

+978
-3
lines changed

base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/InnerClassTests.java

+37
Original file line numberDiff line numberDiff line change
@@ -1481,6 +1481,43 @@ public void testAnonymousInnerClass28() {
14811481
runConformTest(sources, "class D$1;class D$2");
14821482
}
14831483

1484+
@Test // https://issues.apache.org/jira/browse/GROOVY-8104
1485+
public void testAnonymousInnerClass29() {
1486+
//@formatter:off
1487+
String[] sources = {
1488+
"Script.groovy",
1489+
"class A {\n" +
1490+
" void foo() {\n" +
1491+
" C c = new C()\n" +
1492+
" ['1','2','3'].each {\n" +
1493+
" c.baz(it, new I() {\n" +
1494+
" void bar(Object o) {\n" +
1495+
" B b = new B()\n" + // Could not find matching constructor for: A$B(A$_foo_closure1)
1496+
" print \"$o:$b;\"\n" +
1497+
" }\n" +
1498+
" })\n" +
1499+
" }\n" +
1500+
" }\n" +
1501+
" class B {\n" +
1502+
" String toString() { getClass().getSimpleName() }\n" +
1503+
" }\n" +
1504+
"}\n" +
1505+
"class C {\n" +
1506+
" void baz(Object o, I i) {\n" +
1507+
" i.bar(o)\n" +
1508+
" }\n" +
1509+
"}\n" +
1510+
"interface I {\n" +
1511+
" void bar(Object o)\n" +
1512+
"}\n" +
1513+
"A a = new A()\n" +
1514+
"a.foo()\n",
1515+
};
1516+
//@formatter:on
1517+
1518+
runConformTest(sources, "1:B;2:B;3:B;");
1519+
}
1520+
14841521
@Test
14851522
public void testMixedModeInnerProperties_GRE597() {
14861523
//@formatter:off

base/org.codehaus.groovy25/.checkstyle

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
<file-match-pattern match-pattern="groovy/classgen/AsmClassGenerator.java" include-pattern="false" />
4242
<file-match-pattern match-pattern="groovy/classgen/EnumVisitor.java" include-pattern="false" />
4343
<file-match-pattern match-pattern="groovy/classgen/ExtendedVerifier.java" include-pattern="false" />
44-
<file-match-pattern match-pattern="groovy/classgen/InnerClassVisitorHelper.java" include-pattern="false" />
44+
<file-match-pattern match-pattern="groovy/classgen/InnerClassVisitor(Helper)?.java" include-pattern="false" />
4545
<file-match-pattern match-pattern="groovy/classgen/ReturnAdder.java" include-pattern="false" />
4646
<file-match-pattern match-pattern="groovy/classgen/Verifier.java" include-pattern="false" />
4747
<file-match-pattern match-pattern="groovy/classgen/asm/BinaryExpressionHelper.java" include-pattern="false" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,316 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.codehaus.groovy.classgen;
20+
21+
import org.codehaus.groovy.ast.ClassHelper;
22+
import org.codehaus.groovy.ast.ClassNode;
23+
import org.codehaus.groovy.ast.CodeVisitorSupport;
24+
import org.codehaus.groovy.ast.ConstructorNode;
25+
import org.codehaus.groovy.ast.FieldNode;
26+
import org.codehaus.groovy.ast.GroovyCodeVisitor;
27+
import org.codehaus.groovy.ast.InnerClassNode;
28+
import org.codehaus.groovy.ast.MethodNode;
29+
import org.codehaus.groovy.ast.Parameter;
30+
import org.codehaus.groovy.ast.PropertyNode;
31+
import org.codehaus.groovy.ast.Variable;
32+
import org.codehaus.groovy.ast.VariableScope;
33+
import org.codehaus.groovy.ast.expr.ClosureExpression;
34+
import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
35+
import org.codehaus.groovy.ast.expr.Expression;
36+
import org.codehaus.groovy.ast.expr.FieldExpression;
37+
import org.codehaus.groovy.ast.expr.MethodCallExpression;
38+
import org.codehaus.groovy.ast.expr.PropertyExpression;
39+
import org.codehaus.groovy.ast.expr.TupleExpression;
40+
import org.codehaus.groovy.ast.expr.VariableExpression;
41+
import org.codehaus.groovy.ast.stmt.BlockStatement;
42+
import org.codehaus.groovy.ast.stmt.ExpressionStatement;
43+
import org.codehaus.groovy.control.CompilationUnit;
44+
import org.codehaus.groovy.control.SourceUnit;
45+
import org.codehaus.groovy.transform.trait.Traits;
46+
import groovyjarjarasm.asm.Opcodes;
47+
48+
import java.util.ArrayList;
49+
import java.util.Iterator;
50+
import java.util.List;
51+
52+
public class InnerClassVisitor extends InnerClassVisitorHelper implements Opcodes {
53+
54+
private ClassNode classNode;
55+
private FieldNode currentField;
56+
private MethodNode currentMethod;
57+
private final SourceUnit sourceUnit;
58+
private boolean inClosure, processingObjInitStatements;
59+
60+
public InnerClassVisitor(CompilationUnit cu, SourceUnit su) {
61+
sourceUnit = su;
62+
}
63+
64+
@Override
65+
protected SourceUnit getSourceUnit() {
66+
return sourceUnit;
67+
}
68+
69+
@Override
70+
public void visitClass(ClassNode node) {
71+
classNode = node;
72+
InnerClassNode innerClass = null;
73+
if (!node.isEnum() && !node.isInterface() && node instanceof InnerClassNode) {
74+
innerClass = (InnerClassNode) node;
75+
if (innerClass.getVariableScope() == null && (innerClass.getModifiers() & ACC_STATIC) == 0) {
76+
innerClass.addField("this$0", ACC_FINAL | ACC_SYNTHETIC, node.getOuterClass().getPlainNodeReference(), null);
77+
}
78+
}
79+
80+
super.visitClass(node);
81+
82+
if (node.isEnum() || node.isInterface()) return;
83+
if (innerClass == null) return;
84+
85+
if (node.getSuperClass().isInterface() || Traits.isAnnotatedWithTrait(node.getSuperClass())) {
86+
node.addInterface(node.getUnresolvedSuperClass());
87+
node.setUnresolvedSuperClass(ClassHelper.OBJECT_TYPE);
88+
}
89+
}
90+
91+
@Override
92+
public void visitClosureExpression(ClosureExpression expression) {
93+
boolean inClosureOld = inClosure;
94+
inClosure = true;
95+
super.visitClosureExpression(expression);
96+
inClosure = inClosureOld;
97+
}
98+
99+
@Override
100+
protected void visitObjectInitializerStatements(ClassNode node) {
101+
processingObjInitStatements = true;
102+
super.visitObjectInitializerStatements(node);
103+
processingObjInitStatements = false;
104+
}
105+
106+
@Override
107+
protected void visitConstructorOrMethod(MethodNode node, boolean isConstructor) {
108+
currentMethod = node;
109+
visitAnnotations(node);
110+
visitClassCodeContainer(node.getCode());
111+
// GROOVY-5681: initial expressions should be visited too!
112+
for (Parameter param : node.getParameters()) {
113+
if (param.hasInitialExpression()) {
114+
param.getInitialExpression().visit(this);
115+
}
116+
visitAnnotations(param);
117+
}
118+
currentMethod = null;
119+
}
120+
121+
@Override
122+
public void visitField(FieldNode node) {
123+
currentField = node;
124+
super.visitField(node);
125+
currentField = null;
126+
}
127+
128+
@Override
129+
public void visitProperty(PropertyNode node) {
130+
final FieldNode field = node.getField();
131+
final Expression init = field.getInitialExpression();
132+
field.setInitialValueExpression(null);
133+
super.visitProperty(node);
134+
field.setInitialValueExpression(init);
135+
}
136+
137+
@Override
138+
public void visitConstructorCallExpression(ConstructorCallExpression call) {
139+
super.visitConstructorCallExpression(call);
140+
if (!call.isUsingAnonymousInnerClass()) {
141+
passThisReference(call);
142+
return;
143+
}
144+
145+
InnerClassNode innerClass = (InnerClassNode) call.getType();
146+
ClassNode outerClass = innerClass.getOuterClass();
147+
ClassNode superClass = innerClass.getSuperClass();
148+
if (!superClass.isInterface() && superClass.getOuterClass() != null
149+
&& !(superClass.isStaticClass() || (superClass.getModifiers() & ACC_STATIC) != 0)) {
150+
insertThis0ToSuperCall(call, innerClass);
151+
}
152+
if (!innerClass.getDeclaredConstructors().isEmpty()) return;
153+
if ((innerClass.getModifiers() & ACC_STATIC) != 0) return;
154+
155+
VariableScope scope = innerClass.getVariableScope();
156+
if (scope == null) return;
157+
boolean isStatic = !inClosure && isStatic(innerClass, scope, call);
158+
159+
// expressions = constructor call arguments
160+
List<Expression> expressions = ((TupleExpression) call.getArguments()).getExpressions();
161+
// block = init code for the constructor we produce
162+
BlockStatement block = new BlockStatement();
163+
// parameters = parameters of the constructor
164+
int additionalParamCount = (isStatic ? 0 : 1) + scope.getReferencedLocalVariablesCount();
165+
List<Parameter> parameters = new ArrayList<>(expressions.size() + additionalParamCount);
166+
// superCallArguments = arguments for the super call == the constructor call arguments
167+
List<Expression> superCallArguments = new ArrayList<>(expressions.size());
168+
169+
// first we add a super() call for all expressions given in the constructor call expression
170+
for (int i = 0, n = expressions.size(); i < n; i += 1) {
171+
// add one parameter for each expression in the constructor call
172+
Parameter param = new Parameter(ClassHelper.OBJECT_TYPE, "p" + additionalParamCount + i);
173+
parameters.add(param);
174+
// add the corresponding argument to the super constructor call
175+
superCallArguments.add(new VariableExpression(param));
176+
}
177+
178+
// add the super call
179+
ConstructorCallExpression cce = new ConstructorCallExpression(
180+
ClassNode.SUPER,
181+
new TupleExpression(superCallArguments)
182+
);
183+
184+
block.addStatement(new ExpressionStatement(cce));
185+
186+
int pCount = 0;
187+
if (!isStatic) {
188+
// need to pass "this" to access unknown methods/properties
189+
ClassNode enclosingType = (inClosure ? ClassHelper.CLOSURE_TYPE : outerClass).getPlainNodeReference();
190+
expressions.add(pCount, new VariableExpression("this", enclosingType));
191+
Parameter thisParameter = new Parameter(enclosingType, "p" + pCount);
192+
parameters.add(pCount++, thisParameter);
193+
194+
// "this" reference is saved in a field named "this$0"
195+
FieldNode thisField = innerClass.addField("this$0", ACC_FINAL | ACC_SYNTHETIC, enclosingType, null);
196+
addFieldInit(thisParameter, thisField, block);
197+
}
198+
199+
// for each shared variable, add a Reference field
200+
for (Iterator<Variable> it = scope.getReferencedLocalVariablesIterator(); it.hasNext();) {
201+
Variable var = it.next();
202+
203+
VariableExpression ve = new VariableExpression(var);
204+
ve.setClosureSharedVariable(true);
205+
ve.setUseReferenceDirectly(true);
206+
expressions.add(pCount, ve);
207+
208+
ClassNode referenceType = ClassHelper.REFERENCE_TYPE.getPlainNodeReference();
209+
Parameter p = new Parameter(referenceType, "p" + pCount);
210+
p.setOriginType(var.getOriginType());
211+
parameters.add(pCount++, p);
212+
213+
VariableExpression initial = new VariableExpression(p);
214+
initial.setSynthetic(true);
215+
initial.setUseReferenceDirectly(true);
216+
FieldNode pField = innerClass.addFieldFirst(ve.getName(), ACC_PUBLIC | ACC_SYNTHETIC, referenceType, initial);
217+
pField.setHolder(true);
218+
pField.setOriginType(ClassHelper.getWrapper(var.getOriginType()));
219+
}
220+
221+
innerClass.addConstructor(ACC_SYNTHETIC, parameters.toArray(Parameter.EMPTY_ARRAY), ClassNode.EMPTY_ARRAY, block);
222+
}
223+
224+
private boolean isStatic(InnerClassNode innerClass, VariableScope scope, final ConstructorCallExpression call) {
225+
boolean isStatic = innerClass.isStaticClass();
226+
if (!isStatic) {
227+
if (currentMethod != null) {
228+
if (currentMethod instanceof ConstructorNode) {
229+
ConstructorNode ctor = (ConstructorNode) currentMethod;
230+
final boolean[] precedesSuperOrThisCall = new boolean[1];
231+
232+
GroovyCodeVisitor visitor = new CodeVisitorSupport() {
233+
@Override
234+
public void visitConstructorCallExpression(ConstructorCallExpression cce) {
235+
if (cce == call) {
236+
precedesSuperOrThisCall[0] = true;
237+
} else {
238+
super.visitConstructorCallExpression(cce);
239+
}
240+
}
241+
};
242+
if (ctor.firstStatementIsSpecialConstructorCall()) {
243+
currentMethod.getFirstStatement().visit(visitor);
244+
}
245+
for (Parameter param : ctor.getParameters()) {
246+
if (param.hasInitialExpression()) {
247+
param.getInitialExpression().visit(visitor);
248+
}
249+
}
250+
251+
isStatic = precedesSuperOrThisCall[0];
252+
} else {
253+
isStatic = currentMethod.isStatic();
254+
}
255+
} else if (currentField != null) {
256+
isStatic = currentField.isStatic();
257+
}
258+
}
259+
return isStatic;
260+
}
261+
262+
// this is the counterpart of addThisReference(). To non-static inner classes, outer this should be
263+
// passed as the first argument implicitly.
264+
private void passThisReference(ConstructorCallExpression call) {
265+
ClassNode cn = call.getType().redirect();
266+
if (!shouldHandleImplicitThisForInnerClass(cn)) return;
267+
268+
boolean isInStaticContext = true;
269+
if (currentMethod != null)
270+
isInStaticContext = currentMethod.getVariableScope().isInStaticContext();
271+
else if (currentField != null)
272+
isInStaticContext = currentField.isStatic();
273+
else if (processingObjInitStatements)
274+
isInStaticContext = false;
275+
276+
// if constructor call is not in static context, return
277+
if (isInStaticContext) {
278+
// constructor call is in static context and the inner class is non-static - 1st arg is supposed to be
279+
// passed as enclosing "this" instance
280+
//
281+
Expression args = call.getArguments();
282+
if (args instanceof TupleExpression && ((TupleExpression) args).getExpressions().isEmpty()) {
283+
addError("No enclosing instance passed in constructor call of a non-static inner class", call);
284+
}
285+
return;
286+
}
287+
insertThis0ToSuperCall(call, cn);
288+
}
289+
290+
private void insertThis0ToSuperCall(final ConstructorCallExpression call, final ClassNode cn) {
291+
// calculate outer class which we need for this$0
292+
ClassNode parent = classNode;
293+
int level = 0;
294+
for (; parent != null && parent != cn.getOuterClass(); parent = parent.getOuterClass()) {
295+
level++;
296+
}
297+
298+
// if constructor call is not in outer class, don't pass 'this' implicitly. Return.
299+
if (parent == null) return;
300+
301+
//add this parameter to node
302+
Expression argsExp = call.getArguments();
303+
if (argsExp instanceof TupleExpression) {
304+
TupleExpression argsListExp = (TupleExpression) argsExp;
305+
Expression this0 = VariableExpression.THIS_EXPRESSION;
306+
for (int i = 0; i != level; ++i) {
307+
this0 = new PropertyExpression(this0, "this$0");
308+
// GRECLIPSE add -- GROOVY-8104
309+
if (i == 0 && classNode.getDeclaredField("this$0").getType().equals(ClassHelper.CLOSURE_TYPE))
310+
this0 = new MethodCallExpression(this0, "getThisObject", MethodCallExpression.NO_ARGUMENTS);
311+
// GRECLIPSE end
312+
}
313+
argsListExp.getExpressions().add(0, this0);
314+
}
315+
}
316+
}

base/org.codehaus.groovy30/.checkstyle

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
<file-match-pattern match-pattern="groovy/classgen/AsmClassGenerator.java" include-pattern="false" />
4343
<file-match-pattern match-pattern="groovy/classgen/EnumVisitor.java" include-pattern="false" />
4444
<file-match-pattern match-pattern="groovy/classgen/ExtendedVerifier.java" include-pattern="false" />
45-
<file-match-pattern match-pattern="groovy/classgen/InnerClass(CompletionVisitor|VisitorHelper).java" include-pattern="false" />
45+
<file-match-pattern match-pattern="groovy/classgen/InnerClass(CompletionVisitor|Visitor(Helper)?).java" include-pattern="false" />
4646
<file-match-pattern match-pattern="groovy/classgen/ReturnAdder.java" include-pattern="false" />
4747
<file-match-pattern match-pattern="groovy/classgen/Verifier.java" include-pattern="false" />
4848
<file-match-pattern match-pattern="groovy/classgen/asm/CallSiteWriter.java" include-pattern="false" />

0 commit comments

Comments
 (0)