Skip to content

Commit 7d1054c

Browse files
committed
refactor InnerClassVisitor -- separate out AIC constructor generation
1 parent 2cb02e9 commit 7d1054c

1 file changed

Lines changed: 137 additions & 132 deletions

File tree

src/main/java/org/codehaus/groovy/classgen/InnerClassVisitor.java

Lines changed: 137 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
import org.codehaus.groovy.ast.expr.TupleExpression;
3737
import org.codehaus.groovy.ast.expr.VariableExpression;
3838
import org.codehaus.groovy.ast.stmt.BlockStatement;
39-
import org.codehaus.groovy.ast.stmt.ExpressionStatement;
4039
import org.codehaus.groovy.control.CompilationUnit;
4140
import org.codehaus.groovy.control.SourceUnit;
4241

@@ -48,7 +47,7 @@
4847
import static org.codehaus.groovy.ast.tools.GeneralUtils.attrX;
4948
import static org.codehaus.groovy.ast.tools.GeneralUtils.callX;
5049
import static org.codehaus.groovy.ast.tools.GeneralUtils.constX;
51-
import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
50+
import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorSuperS;
5251
import static org.codehaus.groovy.transform.trait.Traits.isTrait;
5352
import static org.objectweb.asm.Opcodes.ACC_FINAL;
5453
import static org.objectweb.asm.Opcodes.ACC_MANDATED;
@@ -73,6 +72,8 @@ protected SourceUnit getSourceUnit() {
7372
return sourceUnit;
7473
}
7574

75+
//--------------------------------------------------------------------------
76+
7677
@Override
7778
public void visitClass(ClassNode node) {
7879
classNode = node;
@@ -96,11 +97,27 @@ public void visitClass(ClassNode node) {
9697
}
9798

9899
@Override
99-
public void visitClosureExpression(ClosureExpression expression) {
100-
boolean inClosureOld = inClosure;
100+
public void visitField(FieldNode node) {
101+
currentField = node;
102+
super.visitField(node);
103+
currentField = null;
104+
}
105+
106+
@Override
107+
public void visitProperty(PropertyNode node) {
108+
final FieldNode field = node.getField();
109+
final Expression init = field.getInitialExpression();
110+
field.setInitialValueExpression(null);
111+
super.visitProperty(node);
112+
field.setInitialValueExpression(init);
113+
}
114+
115+
@Override
116+
public void visitClosureExpression(ClosureExpression closure) {
117+
boolean inClosurePrevious = inClosure;
101118
inClosure = true;
102-
super.visitClosureExpression(expression);
103-
inClosure = inClosureOld;
119+
super.visitClosureExpression(closure);
120+
inClosure = inClosurePrevious;
104121
}
105122

106123
@Override
@@ -126,144 +143,38 @@ protected void visitConstructorOrMethod(MethodNode node, boolean isConstructor)
126143
}
127144

128145
@Override
129-
public void visitField(FieldNode node) {
130-
currentField = node;
131-
super.visitField(node);
132-
currentField = null;
133-
}
134-
135-
@Override
136-
public void visitProperty(PropertyNode node) {
137-
final FieldNode field = node.getField();
138-
final Expression init = field.getInitialExpression();
139-
field.setInitialValueExpression(null);
140-
super.visitProperty(node);
141-
field.setInitialValueExpression(init);
142-
}
143-
144-
@Override
145-
public void visitConstructorCallExpression(ConstructorCallExpression call) {
146+
public void visitConstructorCallExpression(final ConstructorCallExpression call) {
146147
super.visitConstructorCallExpression(call);
147148
if (!call.isUsingAnonymousInnerClass()) {
148149
passThisReference(call);
149150
return;
150151
}
151152

152-
InnerClassNode innerClass = (InnerClassNode) call.getType();
153+
ClassNode innerClass = call.getType();
153154
ClassNode outerClass = innerClass.getOuterClass();
154155
ClassNode upperClass = innerClass.getUnresolvedSuperClass(false);
155156
if (upperClass.getOuterClass() != null && !upperClass.isInterface()
156157
&& !(upperClass.isStaticClass() || (upperClass.getModifiers() & ACC_STATIC) != 0)) {
157158
insertThis0ToSuperCall(call, innerClass);
158159
}
159-
if (!innerClass.getDeclaredConstructors().isEmpty()) return;
160-
if ((innerClass.getModifiers() & ACC_STATIC) != 0) return;
161-
162-
VariableScope scope = innerClass.getVariableScope();
163-
if (scope == null) return;
164-
boolean isStatic = !inClosure && isStatic(innerClass, scope, call);
165-
166-
// expressions = constructor call arguments
167-
List<Expression> expressions = ((TupleExpression) call.getArguments()).getExpressions();
168-
// block = init code for the constructor we produce
169-
BlockStatement block = new BlockStatement();
170-
// parameters = parameters of the constructor
171-
int additionalParamCount = (isStatic ? 0 : 1) + scope.getReferencedLocalVariablesCount();
172-
List<Parameter> parameters = new ArrayList<>(expressions.size() + additionalParamCount);
173-
// superCallArguments = arguments for the super call == the constructor call arguments
174-
List<Expression> superCallArguments = new ArrayList<>(expressions.size());
175-
176-
// first we add a super() call for all expressions given in the constructor call expression
177-
for (int i = 0, n = expressions.size(); i < n; i += 1) {
178-
// add one parameter for each expression in the constructor call
179-
Parameter param = new Parameter(ClassHelper.OBJECT_TYPE, "p" + additionalParamCount + i);
180-
parameters.add(param);
181-
// add the corresponding argument to the super constructor call
182-
superCallArguments.add(new VariableExpression(param));
183-
}
184-
185-
// add the super call
186-
ConstructorCallExpression cce = new ConstructorCallExpression(
187-
ClassNode.SUPER,
188-
new TupleExpression(superCallArguments)
189-
);
190160

191-
block.addStatement(new ExpressionStatement(cce));
192-
193-
int pCount = 0;
194-
if (!isStatic) {
195-
// need to pass "this" to access unknown methods/properties
196-
ClassNode enclosingType = (inClosure ? ClassHelper.CLOSURE_TYPE : outerClass).getPlainNodeReference();
197-
expressions.add(pCount, new VariableExpression("this", enclosingType));
198-
Parameter thisParameter = new Parameter(enclosingType, "p" + pCount);
199-
thisParameter.setModifiers(ACC_FINAL | ACC_MANDATED);
200-
parameters.add(pCount++, thisParameter);
201-
202-
// "this" reference is saved in a field named "this$0"
203-
FieldNode thisField = innerClass.addField("this$0", ACC_FINAL | ACC_SYNTHETIC, enclosingType, null);
204-
addFieldInit(thisParameter, thisField, block);
205-
}
206-
207-
// for each shared variable, add a Reference field
208-
for (Iterator<Variable> it = scope.getReferencedLocalVariablesIterator(); it.hasNext();) {
209-
Variable var = it.next();
210-
211-
VariableExpression ve = new VariableExpression(var);
212-
ve.setClosureSharedVariable(true);
213-
ve.setUseReferenceDirectly(true);
214-
expressions.add(pCount, ve);
215-
216-
ClassNode referenceType = ClassHelper.REFERENCE_TYPE.getPlainNodeReference();
217-
Parameter p = new Parameter(referenceType, "p" + pCount);
218-
p.setOriginType(var.getOriginType());
219-
parameters.add(pCount++, p);
161+
if ((innerClass.getModifiers() & ACC_STATIC) != 0) return;
162+
if (!innerClass.getDeclaredConstructors().isEmpty()) return;
220163

221-
VariableExpression initial = new VariableExpression(p);
222-
initial.setSynthetic(true);
223-
initial.setUseReferenceDirectly(true);
224-
FieldNode pField = innerClass.addFieldFirst(ve.getName(), ACC_PUBLIC | ACC_SYNTHETIC, referenceType, initial);
225-
pField.setHolder(true);
226-
pField.setOriginType(ClassHelper.getWrapper(var.getOriginType()));
164+
VariableScope scope = ((InnerClassNode) innerClass).getVariableScope();
165+
if (scope != null) {
166+
List<Expression> arguments = ((TupleExpression) call.getArguments()).getExpressions();
167+
boolean isStatic = !inClosure && isStatic(innerClass, scope, call);
168+
addConstructor(innerClass, outerClass, scope, arguments, isStatic);
227169
}
228-
229-
innerClass.addConstructor(ACC_SYNTHETIC, parameters.toArray(Parameter.EMPTY_ARRAY), ClassNode.EMPTY_ARRAY, block);
230170
}
231171

232-
private boolean isStatic(final ClassNode innerClass, final VariableScope scope, final ConstructorCallExpression call) {
233-
boolean isStatic = innerClass.isStaticClass();
234-
if (!isStatic) {
235-
if (currentMethod != null) {
236-
if (currentMethod instanceof ConstructorNode ctor) {
237-
boolean[] precedesSuperOrThisCall = new boolean[1];
238-
GroovyCodeVisitor visitor = new CodeVisitorSupport() {
239-
@Override
240-
public void visitConstructorCallExpression(ConstructorCallExpression cce) {
241-
if (cce == call) {
242-
precedesSuperOrThisCall[0] = true;
243-
} else {
244-
super.visitConstructorCallExpression(cce);
245-
}
246-
}
247-
};
248-
if (ctor.firstStatementIsSpecialConstructorCall()) currentMethod.getFirstStatement().visit(visitor);
249-
Arrays.stream(ctor.getParameters()).filter(Parameter::hasInitialExpression).forEach(p -> p.getInitialExpression().visit(visitor));
250-
251-
isStatic = precedesSuperOrThisCall[0];
252-
} else {
253-
isStatic = currentMethod.isStatic();
254-
}
255-
} else if (currentField != null) {
256-
isStatic = currentField.isStatic();
257-
}
258-
}
259-
// GROOVY-8433: Category transform implies static method
260-
isStatic = isStatic || innerClass.getOuterClass().getAnnotations().stream()
261-
.anyMatch(a -> "groovy.lang.Category".equals(a.getClassNode().getName()));
262-
return isStatic;
263-
}
172+
//--------------------------------------------------------------------------
264173

265-
// this is the counterpart of addThisReference(). To non-static inner classes, outer this should be
266-
// passed as the first argument implicitly.
174+
/**
175+
* This is the counterpart of <code>addThisReference</code>. For non-static
176+
* inner classes, outer this should be passed as the implicit first argument.
177+
*/
267178
private void passThisReference(final ConstructorCallExpression call) {
268179
ClassNode cn = call.getType().redirect();
269180
if (!shouldHandleImplicitThisForInnerClass(cn)) return;
@@ -297,29 +208,123 @@ else if (currentField != null)
297208
}
298209
}
299210

300-
private void insertThis0ToSuperCall(final ConstructorCallExpression call, final ClassNode cn) {
211+
private void insertThis0ToSuperCall(final ConstructorCallExpression call, final ClassNode innerClass) {
301212
// calculate outer class which we need for this$0
302213
ClassNode parent = classNode;
303214
int level = 0;
304-
for (; parent != null && parent != cn.getOuterClass(); parent = parent.getOuterClass()) {
215+
for (; parent != null && parent != innerClass.getOuterClass(); parent = parent.getOuterClass()) {
305216
level++;
306217
}
307218

308-
// if constructor call is not in outer class, don't pass 'this' implicitly. Return.
219+
// if constructor call is not in outer class, do not pass 'this' implicitly
309220
if (parent == null) return;
310221

311222
Expression args = call.getArguments();
312-
if (args instanceof TupleExpression) {
313-
Expression this0 = varX("this"); // bypass closure
223+
if (args instanceof TupleExpression tuple) {
224+
Expression this0 = new VariableExpression("this"); // bypass closure
314225
for (int i = 0; i != level; ++i) {
315226
this0 = attrX(this0, constX("this$0"));
316227
// GROOVY-8104: an anonymous inner class may still have closure nesting
317228
if (i == 0 && classNode.getDeclaredField("this$0").getType().equals(ClassHelper.CLOSURE_TYPE)) {
318229
this0 = callX(this0, "getThisObject");
319230
}
320231
}
232+
tuple.getExpressions().add(0, this0);
233+
}
234+
}
235+
236+
private boolean isStatic(final ClassNode innerClass, final VariableScope scope, final ConstructorCallExpression call) {
237+
boolean isStatic = innerClass.isStaticClass();
238+
if (!isStatic) {
239+
if (currentMethod != null) {
240+
if (currentMethod instanceof ConstructorNode ctor) {
241+
boolean[] precedesSuperOrThisCall = new boolean[1];
242+
GroovyCodeVisitor visitor = new CodeVisitorSupport() {
243+
@Override
244+
public void visitConstructorCallExpression(ConstructorCallExpression cce) {
245+
if (cce == call) {
246+
precedesSuperOrThisCall[0] = true;
247+
} else {
248+
super.visitConstructorCallExpression(cce);
249+
}
250+
}
251+
};
252+
if (ctor.firstStatementIsSpecialConstructorCall()) currentMethod.getFirstStatement().visit(visitor);
253+
Arrays.stream(ctor.getParameters()).filter(Parameter::hasInitialExpression).forEach(p -> p.getInitialExpression().visit(visitor));
321254

322-
((TupleExpression) args).getExpressions().add(0, this0);
255+
isStatic = precedesSuperOrThisCall[0];
256+
} else {
257+
isStatic = currentMethod.isStatic();
258+
}
259+
} else if (currentField != null) {
260+
isStatic = currentField.isStatic();
261+
}
323262
}
263+
// GROOVY-8433: Category transform implies static method
264+
isStatic = isStatic || innerClass.getOuterClass().getAnnotations().stream()
265+
.anyMatch(a -> "groovy.lang.Category".equals(a.getClassNode().getName()));
266+
return isStatic;
267+
}
268+
269+
private void addConstructor(final ClassNode innerClass, final ClassNode outerClass, final VariableScope scope, final List<Expression> arguments, final boolean isStatic) {
270+
// block: init code for the constructor
271+
BlockStatement block = new BlockStatement();
272+
// parameters: parameters of the constructor
273+
int additionalParamCount = (isStatic ? 0 : 1) + scope.getReferencedLocalVariablesCount();
274+
List<Parameter> parameters = new ArrayList<>(arguments.size() + additionalParamCount);
275+
// superCallArguments: arguments for the super constructor call
276+
List<Expression> superCallArguments = new ArrayList<>(arguments.size());
277+
278+
// first we add a super() call for all expressions given in the constructor call expression
279+
for (int i = 0, n = arguments.size(); i < n; i += 1) {
280+
// add one parameter for each expression in the constructor call
281+
Parameter p = new Parameter(ClassHelper.OBJECT_TYPE, "p" + additionalParamCount + i);
282+
parameters.add(p);
283+
// add the corresponding argument to the super constructor call
284+
superCallArguments.add(new VariableExpression(p));
285+
}
286+
287+
// add the super call
288+
block.addStatement(ctorSuperS(new TupleExpression(superCallArguments)));
289+
290+
int pCount = 0;
291+
if (!isStatic) {
292+
// need to pass "this" to access unknown methods/properties
293+
ClassNode enclosingType = (inClosure ? ClassHelper.CLOSURE_TYPE : outerClass).getPlainNodeReference();
294+
arguments.add(pCount, new VariableExpression("this", enclosingType));
295+
Parameter thisParameter = new Parameter(enclosingType, "p" + pCount);
296+
thisParameter.setModifiers(ACC_FINAL | ACC_MANDATED);
297+
parameters.add(pCount++, thisParameter);
298+
299+
// "this" reference is saved in a field named "this$0"
300+
FieldNode thisField = innerClass.addField("this$0", ACC_FINAL | ACC_SYNTHETIC, enclosingType, null);
301+
addFieldInit(thisParameter, thisField, block);
302+
}
303+
304+
// for each shared variable, add a Reference field
305+
for (Iterator<Variable> it = scope.getReferencedLocalVariablesIterator(); it.hasNext(); ) {
306+
Variable v = it.next();
307+
308+
VariableExpression ve = new VariableExpression(v);
309+
ve.setClosureSharedVariable(true);
310+
ve.setUseReferenceDirectly(true);
311+
arguments.add(pCount, ve);
312+
313+
ClassNode referenceType = ClassHelper.REFERENCE_TYPE.getPlainNodeReference();
314+
Parameter p = new Parameter(referenceType, "p" + pCount);
315+
p.setOriginType(v.getOriginType());
316+
parameters.add(pCount++, p);
317+
318+
VariableExpression initial = new VariableExpression(p);
319+
initial.setSynthetic(true);
320+
initial.setUseReferenceDirectly(true);
321+
322+
FieldNode pField = innerClass.addFieldFirst(ve.getName(), ACC_PUBLIC | ACC_SYNTHETIC, referenceType, initial);
323+
pField.setHolder(true);
324+
pField.setOriginType(ClassHelper.getWrapper(v.getOriginType()));
325+
}
326+
327+
// TODO: JLS 15.9.5.1 : checked exceptions of super constructor should be declared by constructor
328+
innerClass.addConstructor(0, parameters.toArray(Parameter[]::new), ClassNode.EMPTY_ARRAY, block);
324329
}
325330
}

0 commit comments

Comments
 (0)