1 /*
  2  * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 
 26 package jdk.incubator.code;
 27 
 28 import com.sun.tools.javac.api.JavacScope;
 29 import com.sun.tools.javac.api.JavacTrees;
 30 import com.sun.tools.javac.code.Symbol.ClassSymbol;
 31 import com.sun.tools.javac.comp.Attr;
 32 import com.sun.tools.javac.model.JavacElements;
 33 import com.sun.tools.javac.processing.JavacProcessingEnvironment;
 34 import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
 35 import com.sun.tools.javac.tree.TreeMaker;
 36 import com.sun.tools.javac.util.Context;
 37 import jdk.incubator.code.dialect.core.CoreType;
 38 import jdk.incubator.code.dialect.java.JavaOp;
 39 import jdk.incubator.code.internal.ReflectMethods;
 40 import jdk.incubator.code.dialect.core.CoreOp.FuncOp;
 41 import jdk.incubator.code.dialect.core.FunctionType;
 42 import jdk.incubator.code.dialect.java.MethodRef;
 43 import jdk.incubator.code.extern.OpWriter;
 44 import jdk.internal.access.SharedSecrets;
 45 
 46 import javax.annotation.processing.ProcessingEnvironment;
 47 import javax.lang.model.element.ExecutableElement;
 48 import javax.lang.model.element.Modifier;
 49 import java.lang.reflect.Method;
 50 import java.lang.reflect.Proxy;
 51 import java.util.*;
 52 import java.util.function.BiFunction;
 53 
 54 /**
 55  * An operation modeling a unit of program behavior.
 56  * <p>
 57  * An operation has direct state and zero or more bodies. An operation's direct state consists of zero or more operands,
 58  * zero or more successors (references to other blocks), and operation-specific state.
 59  * <p>
 60  * An operation might model the addition of two integers, or a method invocation expression. Alternatively an operation
 61  * may model something more complex like method declarations, lambda expressions, or try statements. In such cases an
 62  * operation will contain one or more bodies modeling the nested structure.
 63  * <p>
 64  * The process of building an operation starts with construction of an <i>unattached</i> operation. The direct state and
 65  * any bodies of the unattached operation are fixed at construction.
 66  * <p>
 67  * Building then progresses in one of two ways:
 68  * <ol>
 69  * <li>
 70  * the unattached operation is <i>attached</i> to a block, as its parent block, by using a block builder to
 71  * {@link Block.Builder#op(Op) append} it to a block. The attached operation has a permanently non-{@code null}
 72  * {@link #result result} that can be used as an operand of subsequent constructed operations. The block being built is
 73  * not <a href="Body.Builder.html#body-building-observability">observable</a> through this operation and any attempt to
 74  * access the block throws {@link IllegalStateException}.
 75  * <li>
 76  * the unattached operation is built as a {@link #isRoot() <i>root</i>} operation. The root operation's
 77  * {@link #result result} and {@link #parent parent} are always {@code null}.
 78  * </ol>
 79  * <p>
 80  * Building finishes when the parent body builder of the block to which the operation is attached
 81  * <a href="Body.Builder.html#body-building-finishing">finishes</a>, or when the operation is built as a root operation.
 82  * <p>
 83  * The {@link #location} may be {@link #setLocation set} while the operation is unattached or attached to a block being
 84  * built.
 85  * <p>
 86  * An operation can only be constructed with operands whose declaring block is being built, otherwise construction fails
 87  * with an exception.
 88  * <p>
 89  * An unattached operation, or an operation attached to a block whose parent body builder has not
 90  * <a href="Body.Builder.html#body-building-finishing">finished</a>, is not thread-safe.
 91  */
 92 public non-sealed abstract class Op implements CodeElement<Op, Body> {
 93 
 94     /**
 95      * An operation characteristic indicating the operation is pure and has no side effects.
 96      */
 97     public interface Pure {
 98     }
 99 
100     /**
101      * An operation characteristic indicating the operation has one or more bodies.
102      */
103     public interface Nested {
104         /**
105          * {@return the bodies of the nested operation.}
106          */
107         List<Body> bodies();
108     }
109 
110     /**
111      * An operation characteristic indicating the operation represents a loop
112      */
113     public interface Loop extends Nested {
114         /**
115          * {@return the body of the loop operation.}
116          */
117         Body loopBody();
118     }
119 
120     /**
121      * An operation characteristic indicating the operation has one or more bodies,
122      * all of which are isolated and capture no values.
123      */
124     public interface Isolated extends Nested {
125     }
126 
127     /**
128      * An operation characteristic indicating the operation is invokable.
129      */
130     public interface Invokable extends Nested {
131         /**
132          * {@return the body of the invokable operation.}
133          */
134         Body body();
135 
136         /**
137          * {@return the invokable operation's signature, represented as a function type.}
138          * @implSpec
139          * The default implementation returns the signature of the invokable operation's body.
140          */
141         default FunctionType invokableSignature() {
142             return body().bodySignature();
143         }
144 
145         /**
146          * {@return the entry block parameters of this operation's body}
147          * @implSpec
148          * The default implementation returns the entry block's parameters of the invokable operation's body.
149          */
150         default List<Block.Parameter> parameters() {
151             return body().entryBlock().parameters();
152         }
153 
154         /**
155          * Computes values captured by this invokable operation's body.
156          * @implSpec
157          * The default implementation returns an empty unmodifiable list.
158          *
159          * @return the captured values.
160          * @see Body#capturedValues()
161          */
162         default List<Value> capturedValues() {
163             return List.of();
164         }
165     }
166 
167     /**
168      * An operation characteristic indicating the operation can lower itself by replacing itself with blocks and
169      * operations that represent the same behavior.
170      */
171     // @@@ Hide this abstraction within JavaOp?
172     public interface Lowerable {
173 
174         /**
175          * Lowers this operation into the given block builder.
176          * <p>
177          * A lowering implementation emits the replacement blocks and operations into the given builder, and returns
178          * the block builder to use for subsequent operations in an enclosing transformation.
179          * <p>
180          * If this operation lowers one of its bodies, it should transform that body with a lowering code transformer
181          * produced by {@link #loweringTransformer(BiFunction, BiFunction)}. This ensures that lowerable operations
182          * encountered in that body are lowered recursively.
183          * The {@code inherited} transformer is the operation transformer inherited from an enclosing lowering, if any.
184          * A lowering implementation may pass it directly to {@code loweringTransformer}, or compose it with another
185          * transformer and pass the composed transformer. The transformer passed to {@code loweringTransformer} is then
186          * supplied as the inherited transformer when that lowering code transformer recursively lowers lowerable
187          * operations.
188          *
189          * @param b the block builder into which this operation is lowered
190          * @param inherited the inherited operation transformer, may be {@code null}
191          * @return the block builder to use for subsequent building
192          */
193         Block.Builder lower(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> inherited);
194 
195         /**
196          * Returns a lowering code transformer that partially composes the given operation transformers and, if
197          * required, lowers lowerable operations and appends non-lowerable operations.
198          * <p>
199          * The returned code transformer accepts an operation by first applying the partial composition of
200          * {@code current} with {@code inherited} in the first argument of {@code current}, as if by the following:
201          * {@snippet lang = "java":
202          * Block.Builder composedBlock = inherited == null
203          *         ? block
204          *         : inherited.apply(block, op);
205          * Block.Builder currentBlock = current.apply(composedBlock, op);
206          * }
207          * The returned continuation builder is then selected as if by the following:
208          * {@snippet lang = "java":
209          * if (currentBlock != null) {
210          *     return currentBlock;
211          * } else if (op instanceof Op.Lowerable lop) {
212          *     return lop.lower(composedBlock, inherited);
213          * } else {
214          *     composedBlock.op(op);
215          *     return composedBlock;
216          * }
217          * }
218          *
219          * @param inherited the inherited operation transformer, may be {@code null}
220          * @param current the current operation transformer
221          * @return the lowering code transformer
222          */
223         static CodeTransformer loweringTransformer(BiFunction<Block.Builder, Op, Block.Builder> inherited,
224                                                    BiFunction<Block.Builder, Op, Block.Builder> current) {
225             Objects.requireNonNull(current);
226             return (block, op) -> {
227                 if (inherited != null) {
228                     block = inherited.apply(block, op);
229                 }
230                 Block.Builder currentBlock = current.apply(block, op);
231                 if (currentBlock != null) {
232                     return currentBlock;
233                 } else if (op instanceof Op.Lowerable lop) {
234                     return lop.lower(block, inherited);
235                 } else {
236                     block.op(op);
237                     return block;
238                 }
239             };
240         }
241     }
242 
243     /**
244      * An operation characteristic indicating the operation is a terminating operation
245      * occurring as the last operation in a block.
246      * <p>
247      * A terminating operation passes control to either another block within the same parent body
248      * or to that parent body.
249      */
250     public interface Terminating {
251     }
252 
253     /**
254      * An operation characteristic indicating the operation is a body terminating operation
255      * occurring as the last operation in a block.
256      * <p>
257      * A body terminating operation passes control back to its nearest ancestor body.
258      */
259     public interface BodyTerminating extends Terminating {
260     }
261 
262     /**
263      * An operation characteristic indicating the operation is a block terminating operation
264      * occurring as the last operation in a block.
265      * <p>
266      * The operation has one or more successors to other blocks within the same parent body, and passes
267      * control to one of those blocks.
268      */
269     public interface BlockTerminating extends Terminating {
270         /**
271          * {@return the list of successor blocks associated with this block terminating operation}
272          */
273         List<Block.Reference> successors();
274     }
275 
276     /**
277      * A value that is the result of an operation.
278      */
279     public static final class Result extends Value {
280 
281         /**
282          * If assigned to an operation result, it indicates the operation is a root operation
283         */
284         private static final Result ROOT_RESULT = new Result();
285 
286         final Op op;
287 
288         private Result() {
289             // Constructor for instance of ROOT_RESULT
290             super(null, null);
291             this.op = null;
292         }
293 
294         Result(Block block, Op op) {
295             super(block, op.resultType());
296 
297             this.op = op;
298         }
299 
300         @Override
301         public String toString() {
302             return "%result@" + Integer.toHexString(hashCode());
303         }
304 
305         @Override
306         public SequencedSet<Value> dependsOn() {
307             SequencedSet<Value> depends = new LinkedHashSet<>(op.operands());
308             if (op instanceof Terminating) {
309                 op.successors().stream().flatMap(h -> h.arguments().stream()).forEach(depends::add);
310             }
311 
312             return Collections.unmodifiableSequencedSet(depends);
313         }
314 
315         /**
316          * {@return the result's declaring operation.}
317          */
318         public Op op() {
319             return op;
320         }
321     }
322 
323     /**
324      * Source location information for an operation.
325      *
326      * @param sourceRef the reference to the source, {@code null} if absent
327      * @param line the line in the source
328      * @param column the column in the source
329      */
330     public record Location(String sourceRef, int line, int column) {
331 
332         /**
333          * The location value, {@code null}, indicating no location information.
334          */
335         public static final Location NO_LOCATION = null;
336 
337         /**
338          * Constructions a location with line and column only.
339          *
340          * @param line the line in the source
341          * @param column the column in the source
342          */
343         public Location(int line, int column) {
344             this(null, line, column);
345         }
346     }
347 
348     // Set when op is attached to a block or root, otherwise null when unattached
349     // @@@ stable value?
350     Result result;
351 
352     // null if not specified
353     // @@@ stable value?
354     Location location;
355 
356     final List<Value> operands;
357 
358     /**
359      * Constructs an operation with operands mapped from, and location copied from, the given operation.
360      * <p>
361      * The constructed operation's operands are the values computed, in order, by mapping the given operation's operands
362      * using the given code context. The new operation's location is the given operation's location, if any.
363      *
364      * @param that the given operation
365      * @param cc   the code context
366      */
367     protected Op(Op that, CodeContext cc) {
368         List<Value> outputOperands = cc.getValues(that.operands);
369         // Values should be guaranteed to connect to blocks being built since
370         // the context only allows such mappings, assert for clarity
371         assert outputOperands.stream().noneMatch(Value::isBuilt);
372         this.operands = List.copyOf(outputOperands);
373         this.location = that.location;
374     }
375 
376     /**
377      * Transforms this operation.
378      * <p>
379      * Implementations of this method return an unattached transformed copy of this operation.
380      * <p>
381      * An implementation copies this operation's direct state and transforms this operation's bodies, if any. Operands
382      * are copied by mapping this operation's operands, in order, with the given code context. Successors are copied as
383      * specified by {@link CodeContext#getReferenceOrCreate(Block.Reference)}. Operation-specific state is copied as
384      * appropriate for the operation.
385      * <p>
386      * Bodies are {@link Body#transform(CodeContext, CodeTransformer) transformed} with the given code context and code
387      * transformer.
388      *
389      * @apiNote
390      * To copy an operation use the {@link CodeTransformer#COPYING_TRANSFORMER copying transformer}.
391      *
392      * @param cc the code context
393      * @param ct the code transformer
394      * @return the transformed operation
395      * @see CodeTransformer#COPYING_TRANSFORMER
396      */
397     public abstract Op transform(CodeContext cc, CodeTransformer ct);
398 
399     /**
400      * Constructs an operation with a list of operands.
401      *
402      * @param operands the list of operands, a copy of the list is performed if required.
403      * @throws IllegalArgumentException if an operand's declaring block is built.
404      */
405     protected Op(List<? extends Value> operands) {
406         for (Value operand : operands) {
407             if (operand.isBuilt()) {
408                 throw new IllegalArgumentException("Operand's declaring block is built: " + operand);
409             }
410         }
411         this.operands = List.copyOf(operands);
412     }
413 
414     /**
415      * Sets the originating source location of this operation, if this operation is not built.
416      *
417      * @param l the location, the {@link Location#NO_LOCATION} value indicates the location is not specified.
418      * @throws IllegalStateException if this operation is built.
419      */
420     public final void setLocation(Location l) {
421         // @@@ Fail if location != null?
422         if (isRoot() || (result != null && result.block.isBuilt())) {
423             throw new IllegalStateException("Built operation");
424         }
425 
426         location = l;
427     }
428 
429     /**
430      * {@return the originating source location of this operation, otherwise {@code null} if not specified}
431      */
432     public final Location location() {
433         return location;
434     }
435 
436     /**
437      * Returns this operation's parent block, otherwise {@code null} if this operation is unattached to a block or a
438      * root operation.
439      * <p>
440      * The operation's parent block is the same as the operation result's {@link Value#declaringBlock declaring block}.
441      *
442      * @return operation's parent block, or {@code null} if this operation is unattached or a root operation.
443      * @throws IllegalStateException if this operation is attached and its parent block is
444      * <a href="Body.Builder.html#body-building-observability">unobservable</a>.
445      * @see Value#declaringBlock()
446      */
447     @Override
448     public final Block parent() {
449         if (isRoot() || result == null) {
450             return null;
451         }
452 
453         if (!result.block.isBuilt()) {
454             throw new IllegalStateException("Parent block is unobservable");
455         }
456 
457         return result.block;
458     }
459 
460     @Override
461     public final List<Body> children() {
462         return bodies();
463     }
464 
465     /**
466      * {@return the operation's bodies, as an unmodifiable list}
467      * @implSpec this implementation returns an unmodifiable empty list.
468      * @see #children()
469      */
470     public List<Body> bodies() {
471         return List.of();
472     }
473 
474     /**
475      * {@return the operation's result type}
476      */
477     public abstract CodeType resultType();
478 
479 
480     /**
481      * {@return the operation's result, or {@code null} if this operation is unattached or a
482      * root operation.}
483      */
484     public final Result result() {
485         return result == Result.ROOT_RESULT ? null : result;
486     }
487 
488     /**
489      * {@return the operation's operands, as an unmodifiable list}
490      */
491     public final List<Value> operands() {
492         return operands;
493     }
494 
495     /**
496      * {@return the operation's successors, as an unmodifiable list}
497      * @implSpec this implementation returns an unmodifiable empty list.
498      */
499     public List<Block.Reference> successors() {
500         return List.of();
501     }
502 
503     /**
504      * Returns the operation's signature, represented as a function type.
505      * <p>
506      * The signature's return type is the operation's result type and its parameter types are the
507      * operation's operand types, in order.
508      *
509      * @return the operation's signature
510      */
511     public final FunctionType opSignature() {
512         List<CodeType> operandTypes = operands.stream().map(Value::type).toList();
513         return CoreType.functionType(resultType(), operandTypes);
514     }
515 
516     /**
517      * Computes values captured by this operation. A captured value is a value that is used
518      * but not declared by any descendant operation of this operation.
519      * <p>
520      * The order of the captured values is first use encountered in depth
521      * first search of this operation's descendant operations.
522      *
523      * @return the list of captured values, modifiable
524      * @see Body#capturedValues()
525      */
526     public final List<Value> capturedValues() {
527         Set<Value> cvs = new LinkedHashSet<>();
528 
529         Deque<Body> bodyStack = new ArrayDeque<>();
530         for (Body childBody : bodies()) {
531             Body.capturedValues(cvs, bodyStack, childBody);
532         }
533         return new ArrayList<>(cvs);
534     }
535 
536     /**
537      * Builds this operation to become a built root operation. After this operation is built its
538      * {@link #result result} and {@link #parent parent} will always be {@code null}.
539      * <p>
540      * This method is idempotent.
541      *
542      * @throws IllegalStateException if this operation is attached to a block.
543      * @see #isRoot()
544      */
545     public final void buildAsRoot() {
546         if (result == Result.ROOT_RESULT) {
547             return;
548         }
549         if (result != null) {
550             throw new IllegalStateException("Operation is attached to a block");
551         }
552         result = Result.ROOT_RESULT;
553     }
554 
555     /**
556      * {@return {@code true} if this operation is a root operation.}
557      * @see #buildAsRoot()
558      * @see #isAttached()
559      * */
560     public final boolean isRoot() {
561         return result == Result.ROOT_RESULT;
562     }
563 
564     /**
565      * {@return {@code true} if this operation is attached to a block.}
566      * @see #buildAsRoot()
567      * @see #isRoot()
568      * */
569     public final boolean isAttached() {
570         return !isRoot() && result != null;
571     }
572 
573     /**
574      * Externalizes this operation's name as a string.
575      * @implSpec this implementation returns the result of the expression {@code this.getClass().getName()}.
576      *
577      * @return the operation name
578      */
579     public String externalizeOpName() {
580         return this.getClass().getName();
581     }
582 
583     /**
584      * Externalizes this operation's specific state as a map of attributes.
585      *
586      * <p>A null attribute value is represented by the constant
587      * value {@link jdk.incubator.code.extern.ExternalizedOp#NULL_ATTRIBUTE_VALUE}.
588      * @implSpec this implementation returns an unmodifiable empty map.
589      *
590      * @return the operation's externalized state, as an unmodifiable map
591      */
592     public Map<String, Object> externalize() {
593         return Map.of();
594     }
595 
596     /**
597      * Returns the code model text for this operation.
598      * <p>
599      * The format of codel model text is unspecified.
600      *
601      * @return the code model text for this operation.
602      * @apiNote Code model text is designed to be human-readable and is intended for debugging, testing,
603      * and comprehension.
604      * @see OpWriter#toText(Op, OpWriter.Option...)
605      */
606     public final String toText() {
607         return OpWriter.toText(this);
608     }
609 
610 
611     /**
612      * Returns a quoted instance containing the code model of a reflectable lambda expression or method reference.
613      * <p>
614      * The quoted instance also contains a mapping from {@link Value values} in the code model that model final, or
615      * effectively final, variables used but not declared in the lambda expression to their corresponding run time
616      * values. Such run time values are commonly referred to as captured values.
617      * <p>
618      * Repeated invocations of this method will return a quoted instance containing the same instance of the code model.
619      * Therefore, code elements (and more generally code items) contained within the code model can be reliably compared
620      * using object identity.
621      *
622      * @param fiInstance a functional interface instance that is the result of a reflectable lambda expression or
623      *                   method reference.
624      * @return the quoted instance containing the code model, or an empty optional if the functional interface instance
625      * is not the result of a reflectable lambda expression or method reference.
626      * @throws UnsupportedOperationException if the Java version used at compile time to generate and store the code
627      * model is not the same as the Java version used at runtime to load the code model.
628      * @apiNote if the functional interface instance is a proxy instance, then the quoted code model is unavailable and
629      * this method returns an empty optional.
630      */
631     public static Optional<Quoted<JavaOp.LambdaOp>> ofLambda(Object fiInstance) {
632         Object oq = fiInstance;
633         if (Proxy.isProxyClass(oq.getClass())) {
634             // @@@ The interpreter implements interpretation of
635             // lambdas using a proxy whose invocation handler
636             // supports the internal protocol to access the quoted instance
637             oq = Proxy.getInvocationHandler(oq);
638         }
639 
640         Method method;
641         try {
642             method = oq.getClass().getDeclaredMethod("__internal_quoted");
643         } catch (NoSuchMethodException e) {
644             return Optional.empty();
645         }
646         method.setAccessible(true);
647 
648         Quoted<?> q;
649         try {
650             q = (Quoted<?>) method.invoke(oq);
651         } catch (ReflectiveOperationException e) {
652             // op method may throw UOE in case java compile time version doesn't match runtime version
653             if (e.getCause() instanceof UnsupportedOperationException uoe) {
654                 throw uoe;
655             }
656             throw new RuntimeException(e);
657         }
658         if (!(q.op() instanceof JavaOp.LambdaOp)) {
659             // This can only happen if the stored model is invalid
660             throw new RuntimeException("Invalid code model for lambda expression : " + q);
661         }
662         @SuppressWarnings("unchecked")
663         Quoted<JavaOp.LambdaOp> lq = (Quoted<JavaOp.LambdaOp>) q;
664         return Optional.of(lq);
665     }
666 
667     /**
668      * Returns the code model of a reflectable method.
669      * <p>
670      * Repeated invocations of this method will return the same instance of the code model. Therefore,
671      * code elements (and more generally code items) contained within the code model can be reliably compared using
672      * object identity.
673      *
674      * @param method the method.
675      * @return the code model, or an empty optional if the method is not reflectable.
676      * @throws UnsupportedOperationException if the Java version used at compile time to generate and store the code
677      * model is not the same as the Java version used at runtime to load the code model.
678      */
679     // @@@ Make caller sensitive with the same access control as invoke
680     // and throwing IllegalAccessException
681     // @CallerSensitive
682     @SuppressWarnings("unchecked")
683     public static Optional<FuncOp> ofMethod(Method method) {
684         return (Optional<FuncOp>)SharedSecrets.getJavaLangReflectAccess()
685                 .setCodeModelIfNeeded(method, Op::createCodeModel);
686     }
687 
688     private static Optional<FuncOp> createCodeModel(Method method) {
689         char[] sig = MethodRef.method(method).toString().toCharArray();
690         for (int i = 0; i < sig.length; i++) {
691             switch (sig[i]) {
692                 case '.', ';', '[', '/': sig[i] = '$';
693             }
694         }
695         String opMethodName = new String(sig);
696         Method opMethod;
697         try {
698             // @@@ Use method handle with full power mode
699             opMethod = method.getDeclaringClass().getDeclaredMethod(opMethodName);
700         } catch (NoSuchMethodException e) {
701             return Optional.empty();
702         }
703         opMethod.setAccessible(true);
704         try {
705             FuncOp funcOp = (FuncOp) opMethod.invoke(null);
706             return Optional.of(funcOp);
707         } catch (ReflectiveOperationException e) {
708             // op method may throw UOE in case java compile time version doesn't match runtime version
709             if (e.getCause() instanceof UnsupportedOperationException uoe) {
710                 throw uoe;
711             }
712             throw new RuntimeException(e);
713         }
714     }
715 
716     /**
717      * Returns the code model of provided executable element (if any).
718      * <p>
719      * If the executable element has a code model then it will be an instance of
720      * {@code java.lang.reflect.code.op.CoreOps.FuncOp}.
721      * Note: due to circular dependencies we cannot refer to the type explicitly.
722      *
723      * @param processingEnvironment the annotation processing environment
724      * @param e the executable element.
725      * @return the code model of the provided executable element (if any).
726      */
727     public static Optional<FuncOp> ofElement(ProcessingEnvironment processingEnvironment, ExecutableElement e) {
728         if (e.getModifiers().contains(Modifier.ABSTRACT) ||
729                 e.getModifiers().contains(Modifier.NATIVE)) {
730             return Optional.empty();
731         }
732 
733         Context context = ((JavacProcessingEnvironment)processingEnvironment).getContext();
734         ReflectMethods reflectMethods = ReflectMethods.instance(context);
735         Attr attr = Attr.instance(context);
736         JavacElements elements = JavacElements.instance(context);
737         JavacTrees javacTrees = JavacTrees.instance(context);
738         TreeMaker make = TreeMaker.instance(context);
739         try {
740             JCMethodDecl methodTree = (JCMethodDecl)elements.getTree(e);
741             JavacScope scope = javacTrees.getScope(javacTrees.getPath(e));
742             ClassSymbol enclosingClass = (ClassSymbol) scope.getEnclosingClass();
743             FuncOp op = attr.runWithAttributedMethod(scope.getEnv(), methodTree,
744                     attribBlock -> {
745                         try {
746                             return reflectMethods.getMethodBody(enclosingClass, methodTree, attribBlock, make);
747                         } catch (Throwable ex) {
748                             // this might happen if the source code contains errors
749                             return null;
750                         }
751                     });
752             return Optional.ofNullable(op);
753         } catch (RuntimeException ex) {  // ReflectMethods.UnsupportedASTException
754             // some other error occurred when attempting to attribute the method
755             // @@@ better report of error
756             ex.printStackTrace();
757             return Optional.empty();
758         }
759     }
760 }