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 }