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