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

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

blackdrag commented on code in PR #2692:
URL: https://github.com/apache/groovy/pull/2692#discussion_r3565434800


##########
src/main/java/groovy/transform/PackMode.java:
##########
@@ -0,0 +1,47 @@
+/*
+ *  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 groovy.transform;
+
+import org.apache.groovy.lang.annotation.Incubating;
+
+/**
+ * The packing behaviour of a {@link PackedClosures} scope. {@link #LENIENT}, 
{@link #WARN} and
+ * {@link #STRICT} all pack, differing only in how they report a closure that 
could <em>not</em> be
+ * packed (declines are otherwise silent). {@link #DISABLED} is the opposite: 
it opts the scope out
+ * of packing entirely, so the most-specific declaration wins — a {@code 
DISABLED} method inside a
+ * packed class, or vice versa, and it overrides the automatic {@code 
groovy.target.closure.pack} flag
+ * as well.
+ *
+ * @see PackedClosures#mode()
+ */
+@Incubating
+public enum PackMode {

Review Comment:
   suggestion: make this part of PackedClosure



##########
src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java:
##########
@@ -158,6 +197,533 @@ public void writeClosure(final ClosureExpression 
expression) {
         controller.getOperandStack().replace(ClassHelper.CLOSURE_TYPE, 
localVariableParams.length);
     }
 
+    private int packedCounter;
+
+    /** Name prefix of the synthetic methods holding hoisted closure bodies. */
+    private static final String PACKED_METHOD_PREFIX = "$packed$closure$";
+
+    /**
+     * GEP-27 capability analysis: choose the compilation strategy for a 
closure literal.
+     * <p>
+     * A closure is packed (S1, {@link PackStrategy#PACKED_ADAPTER}) when it 
is <em>triggered</em> —
+     * either the enclosing scope opts in via {@code @PackedClosures} (the 
dynamic trust path), or the
+     * capability analysis <em>proves</em> it delegate-independent under 
{@code @CompileStatic} (the
+     * automatic path, see {@link #isProvablyDelegateIndependent}) — and it is 
in a packable position:
+     * it needs no real {@code Closure} semantics we cannot reproduce ({@link 
#isPackable}), it is
+     * written directly in a method rather than nested in another closure 
(owner-retargeting for
+     * nested closures is later work), and it does not visibly escape its 
method. Otherwise it stays a
+     * full generated class (S0, {@link PackStrategy#FULL_CLASS}) exactly as 
today.
+     */
+    private PackStrategy chooseStrategy(final ClosureExpression expression) {
+        if (!isPackable(expression)) return PackStrategy.FULL_CLASS;
+        // The most-specific @PackedClosures mode in scope (method wins over 
class), or null if not
+        // annotated. DISABLED is a fine-grained opt-out that beats an 
enclosing opt-in AND the flag.
+        PackMode mode = annotatedMode();
+        if (mode == PackMode.DISABLED) return PackStrategy.FULL_CLASS;
+        boolean annotated = (mode != null); // LENIENT/WARN/STRICT all opt in 
(DISABLED handled above)
+        boolean triggered;
+        if (isStaticCompilation()) {
+            // Under @CompileStatic packing requires the STC proof of 
delegate-independence even for
+            // the @PackedClosures opt-in: a delegate-resolved body compiles 
IMPLICIT_RECEIVER
+            // expressions against Closure.getDelegate(), which a hoisted 
method cannot honour.
+            triggered = (annotated || 
SystemUtil.getBooleanSafe("groovy.target.closure.pack"))
+                    && isDelegateIndependent(expression);
+        } else {
+            // Dynamic compilation has no proof; only the annotation's trust 
assertion packs, and the
+            // PackedClosure runtime guard backstops a wrong assumption.
+            triggered = annotated;
+        }
+        if (!triggered) return PackStrategy.FULL_CLASS;
+        if (controller.isInGeneratedFunction()) return PackStrategy.FULL_CLASS;
+        if (escapesEnclosingMethod(expression)) return PackStrategy.FULL_CLASS;
+        return PackStrategy.PACKED_ADAPTER;
+    }
+
+    /**
+     * True when the type checker resolved every implicitly-received name in 
the closure (and its
+     * nested closures) against the owner/parameters rather than a delegate — 
the delegate-independence
+     * proof of the capability analysis. Under {@code @CompileStatic} STC 
records each name's resolution
+     * path as {@code IMPLICIT_RECEIVER}: a path ending in {@code "owner"} was 
resolved against the
+     * owner (safe), anything else (e.g. {@code "delegate"}, from {@code 
@DelegatesTo} or {@code with})
+     * went through a delegate — the same distinction STC itself makes. The 
marker can sit on a method
+     * call, a property expression, or a bare variable (e.g. {@code length} 
inside {@code with}), so
+     * all three are checked. If nothing resolved through a delegate, packing 
preserves resolution.
+     */
+    private boolean isDelegateIndependent(final ClosureExpression expression) {
+        Statement code = expression.getCode();
+        if (code == null) return false;
+        boolean[] delegateResolved = {false};
+        // Walk the whole closure tree, including nested closures: a delegate 
resolution anywhere in
+        // the body (e.g. an owner-passed closure whose inner closure resolves 
a name against the
+        // delegate) means the top closure carries a delegate chain that 
packing would break.
+        code.visit(new CodeVisitorSupport() {
+            @Override public void visitMethodCallExpression(final 
MethodCallExpression call) {
+                flag(call.getNodeMetaData(IMPLICIT_RECEIVER));
+                super.visitMethodCallExpression(call);
+            }
+            @Override public void visitPropertyExpression(final 
PropertyExpression pexp) {
+                flag(pexp.getNodeMetaData(IMPLICIT_RECEIVER));
+                super.visitPropertyExpression(pexp);
+            }
+            @Override public void visitVariableExpression(final 
VariableExpression ve) {
+                flag(ve.getNodeMetaData(IMPLICIT_RECEIVER));
+                // a name STC left dynamic (no local/param binding, no 
recorded receiver path) will be
+                // resolved at runtime through the closure's owner/delegate 
chain 

> hoist eligible closure bodies to a shared adapter @PackedClosures/flag 
> (GEP-27 S1)
> ----------------------------------------------------------------------------------
>
>                 Key: GROOVY-12151
>                 URL: https://issues.apache.org/jira/browse/GROOVY-12151
>             Project: Groovy
>          Issue Type: Improvement
>            Reporter: Paul King
>            Assignee: Paul King
>            Priority: Major
>




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

Reply via email to