3636import org .codehaus .groovy .ast .expr .TupleExpression ;
3737import org .codehaus .groovy .ast .expr .VariableExpression ;
3838import org .codehaus .groovy .ast .stmt .BlockStatement ;
39- import org .codehaus .groovy .ast .stmt .ExpressionStatement ;
4039import org .codehaus .groovy .control .CompilationUnit ;
4140import org .codehaus .groovy .control .SourceUnit ;
4241
4847import static org .codehaus .groovy .ast .tools .GeneralUtils .attrX ;
4948import static org .codehaus .groovy .ast .tools .GeneralUtils .callX ;
5049import 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 ;
5251import static org .codehaus .groovy .transform .trait .Traits .isTrait ;
5352import static org .objectweb .asm .Opcodes .ACC_FINAL ;
5453import 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