[ 
https://issues.apache.org/jira/browse/GROOVY-11967?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18076346#comment-18076346
 ] 

ASF GitHub Bot commented on GROOVY-11967:
-----------------------------------------

Copilot commented on code in PR #2493:
URL: https://github.com/apache/groovy/pull/2493#discussion_r3143665471


##########
src/main/java/org/codehaus/groovy/transform/sc/transformers/ListExpressionTransformer.java:
##########
@@ -111,6 +113,13 @@ public void visit(final GroovyCodeVisitor visitor) {
                 var list = new ConstructorCallExpression(ArrayList_TYPE, new 
ConstantExpression(getExpressions().size(), true));
                 
list.putNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET, 
ArrayList_NEW);
                 list.visit(visitor);
+                // GROOVY-11967: indy-mode constructor call leaves Object on 
the JVM
+                // stack, so the following INVOKEVIRTUAL ArrayList.add needs a 
CHECKCAST.
+                // Non-indy mode emits INVOKESPECIAL which already leaves 
ArrayList on
+                // the stack, so the cast would be redundant — skip it there.
+                if (g.getController().getInvocationWriter() instanceof 
InvokeDynamicWriter) {
+                    mv.visitTypeInsn(CHECKCAST, "java/util/ArrayList");
+                }

Review Comment:
   The CAST is currently gated on `instanceof InvokeDynamicWriter`, but a 
constructor call can also be emitted via the non-indy 
`CallSiteWriter.callConstructor(...)` path (declared to return `Object`) when 
the controller is using `InvocationWriter` (non-indy) and not in a 
statically-checked method. In that case the following `INVOKEVIRTUAL 
java/util/ArrayList.add` would still see `Object` on the stack and can 
VerifyError as well. Consider making the cast unconditional, or key it off the 
actual stack/top operand type after `list.visit(visitor)` (e.g., if the top 
operand isn’t `ArrayList_TYPE`, emit the CHECKCAST).



##########
src/test/groovy/bugs/Groovy11967.groovy:
##########
@@ -0,0 +1,122 @@
+/*
+ *  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 bugs
+
+import org.junit.jupiter.api.Test
+
+import static groovy.test.GroovyAssert.assertScript
+
+/**
+ * Regression coverage for the synthesised lower-arity bridge constructor that
+ * {@code @CompileStatic} emits for a constructor with a default-valued list
+ * parameter. The bridge inlines the default {@code [...]} literal as
+ * {@code new ArrayList(n) + .add(...)}; in indy mode the constructor call
+ * returns {@code Object} on the JVM stack, so the {@code INVOKEVIRTUAL
+ * ArrayList.add} that follows must be preceded by a {@code CHECKCAST 
ArrayList}
+ * to satisfy the verifier.

Review Comment:
   The explanatory pseudo-code in the class Javadoc reads `new ArrayList(n) + 
.add(...)`, which looks like an accidental `+` and could confuse readers. 
Consider rephrasing it to reflect the actual bytecode shape (allocate `new 
ArrayList(n)` then invoke one or more `add` calls).
   ```suggestion
    * parameter. The bridge inlines the default {@code [...]} literal by
    * allocating {@code new ArrayList(n)} and then invoking one or more
    * {@code add(...)} calls; in indy mode the constructor call returns
    * {@code Object} on the JVM stack, so the {@code INVOKEVIRTUAL 
ArrayList.add}
    * that follows must be preceded by a {@code CHECKCAST ArrayList} to satisfy
    * the verifier.
   ```



##########
src/main/java/org/codehaus/groovy/transform/sc/transformers/ListExpressionTransformer.java:
##########
@@ -111,6 +113,13 @@ public void visit(final GroovyCodeVisitor visitor) {
                 var list = new ConstructorCallExpression(ArrayList_TYPE, new 
ConstantExpression(getExpressions().size(), true));
                 
list.putNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET, 
ArrayList_NEW);
                 list.visit(visitor);
+                // GROOVY-11967: indy-mode constructor call leaves Object on 
the JVM
+                // stack, so the following INVOKEVIRTUAL ArrayList.add needs a 
CHECKCAST.
+                // Non-indy mode emits INVOKESPECIAL which already leaves 
ArrayList on
+                // the stack, so the cast would be redundant — skip it there.
+                if (g.getController().getInvocationWriter() instanceof 
InvokeDynamicWriter) {
+                    mv.visitTypeInsn(CHECKCAST, "java/util/ArrayList");

Review Comment:
   After emitting the `CHECKCAST`, the `OperandStack` model is still whatever 
`list.visit(visitor)` left behind (likely `Object` when routed through 
indy/call-site code). To keep subsequent static compilation/type-driven codegen 
consistent, update the operand stack type to `ArrayList` (e.g. replace the top 
operand) when the cast is inserted.
   ```suggestion
                       mv.visitTypeInsn(CHECKCAST, "java/util/ArrayList");
                       os.replace(ArrayList_TYPE);
   ```





> VerifyError in @CompileStatic constructor with default-valued list parameter
> ----------------------------------------------------------------------------
>
>                 Key: GROOVY-11967
>                 URL: https://issues.apache.org/jira/browse/GROOVY-11967
>             Project: Groovy
>          Issue Type: Bug
>            Reporter: Paul King
>            Priority: Major
>
> h3. Summary
> A {{@CompileStatic}} class with a constructor that has a default-valued 
> list-literal parameter triggers {{VerifyError}} when the synthesised 
> lower-arity bridge constructor is loaded.
> h3. Repro
> {code:java}
> @CompileStatic
> class Foo {
>     Foo(Class<?> a, MessageSource b, List<Class> targetTypes = [Object]) {
>     }
> }
> new Foo(String, ms) // boom
> {code}
> h3. Symptom
> {noformat}
> java.lang.VerifyError: Bad type on operand stack
>   Location: Foo.<init>(Ljava/lang/Class;LMessageSource;)V @20: invokevirtual
>   Reason:   Type 'java/lang/Object' (current frame, stack[4]) is not 
> assignable to 'java/util/ArrayList'
> {noformat}
> h3. Root cause
> Introduced by GROOVY-8699 (commit {{7b18440c4f}}, master only). That change 
> replaced the {{ScriptBytecodeAdapter.createList(Object[])}} call (returns 
> {{List}}) with direct {{new ArrayList\(n) + .add(...)}} bytecode, generated 
> by {{ListExpressionTransformer$NewListExpression.visit()}}.
> In indy mode the constructor call lowers to {{invokedynamic 
> init:(Class;I)Object}} -- the JVM stack value is typed as {{Object}}, so the 
> subsequent {{INVOKEVIRTUAL java/util/ArrayList.add(Object)Z}} fails 
> verification because its receiver is not an {{ArrayList}}. The {{CHECKCAST}} 
> to {{List}} added later by the transformer happens after the {{add}} call, 
> too late.
> h3. Scope
> Not specific to {{List<Class>}} -- any default {{[...]}} literal in a 
> {{@CompileStatic}} constructor is affected ({{List<String>}}, 
> {{List<Integer>}}, {{List<List<String>>}}, etc.). Map defaults go through a 
> different transformer and are not affected.
> h3. Fix
> Insert {{CHECKCAST java/util/ArrayList}} immediately after the constructor 
> call in {{ListExpressionTransformer$NewListExpression.visit()}}, before the 
> loop that emits the per-element {{DUP / add / pop}}.
> h3. Affects
> Master only. Groovy 5.0.x and earlier are fine (used the 
> {{createList(Object[])}} helper).



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to