[
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)