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