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.java.JavaOp;
 38 import jdk.incubator.code.internal.ReflectMethods;
 39 import jdk.incubator.code.dialect.core.CoreOp.FuncOp;
 40 import jdk.incubator.code.dialect.core.FunctionType;
 41 import jdk.incubator.code.dialect.java.MethodRef;
 42 import jdk.incubator.code.extern.OpWriter;
 43 import jdk.internal.access.SharedSecrets;
 44 
 45 import javax.annotation.processing.ProcessingEnvironment;
 46 import javax.lang.model.element.ExecutableElement;
 47 import javax.lang.model.element.Modifier;
 48 import java.lang.reflect.Method;
 49 import java.lang.reflect.Proxy;
 50 import java.util.*;
 51 import java.util.function.BiFunction;
 52 
 53 /**
 54  * An operation modeling a unit of program behavior.
 55  * <p>
 56  * An operation uses zero or more values, exposed as a sequence of {@link #operands()}. A
 57  * {@link Op.Terminating terminating} operation may have block references, exposed as a sequence of
 58  * {@link #successors()}. An operation has zero or more bodies, exposed as a sequence of {@link #bodies()}.
 59  *
 60  * <h2>Operation construction</h2>
 61  * <p>
 62  * An operation is constructed by creating an concrete instance of {@link AbstractOp}. Construction creates an
 63  * <i>unplaced</i> operation. An unplaced operation is not yet part of a code model. The operation's operands,
 64  * successors, bodies, and operation-specific state are fixed when construction completes.
 65  * <p>
 66  * An operation can only be constructed with operands whose declaring block is being built. Otherwise, construction
 67  * fails with an exception.
 68  *
 69  * <h2>Operation building</h2>
 70  * <p>
 71  * Building an operation places an unplaced operation in a code model in one of two ways:
 72  * <ol>
 73  * <li>
 74  * the operation is <i>placed</i> in a block, which becomes its parent block, by using a block builder to
 75  * {@link Block.Builder#add(Op) append} the operation to the block. The placed operation has a permanently
 76  * non-{@code null} {@link #result() result} that can be used as an operand of subsequently constructed operations. The
 77  * block being built is not <a href="Body.Builder.html#body-building-observability">observable</a> through this
 78  * operation and any attempt to access the block throws {@link IllegalStateException}.
 79  * <li>
 80  * the operation is <i>placed</i> as the {@link #isRoot() <i>root operation</i>} of a code model by using
 81  * {@link #buildAsRoot()}. The root operation's {@link #result() result} and {@link #parent() parent} are always
 82  * {@code null}.
 83  * </ol>
 84  * <p>
 85  * Building finishes when the parent body builder of the block in which the operation was placed
 86  * <a href="Body.Builder.html#body-building-finishing">finishes</a>, after which the block becomes observable, or when
 87  * the operation is placed as the root of a code model. After building finishes, the operation's placement and location
 88  * are also fixed, and from then on the operation's observable state does not change.
 89  * <p>
 90  * The {@link #location} may be {@link #setLocation set} while the operation is unplaced or placed in a block whose
 91  * parent body builder has not finished.
 92  * <p>
 93  * An unplaced operation, or an operation placed in a block whose parent body builder has not
 94  * <a href="Body.Builder.html#body-building-finishing">finished</a>, is not thread-safe.
 95  *
 96  * @apiNote
 97  * An operation might model the {@link JavaOp.AddOp addition} of two integers, or a method
 98  * {@link JavaOp.InvokeOp invocation} expression. Alternatively an operation may model something more complex like
 99  * {@link jdk.incubator.code.dialect.core.CoreOp.FuncOp method} declarations, {@link JavaOp.LambdaOp lambda}
100  * expressions, or {@link JavaOp.TryOp try} statements. In such cases an operation will contain one or more bodies
101  * modeling the nested structure.
102  */
103 public sealed interface Op extends CodeElement<Op, Body> permits Op.Terminating, InternalAbstractOp {
104 
105     /**
106      * A terminating operation that occurs as the last operation in a block.
107      * <p>
108      * A terminating operation passes control to either another block within the same parent body
109      * or to that parent body.
110      * <p>
111      * A terminating operation is a body terminating operation if it is guaranteed to always have zero successors.
112      * A terminating operation is a block terminating operation if it is guaranteed to always have one or more
113      * successors.
114      */
115     public sealed interface Terminating extends Op permits AbstractOp.Terminating {
116     }
117 
118     /**
119      * An operation characteristic indicating the operation is pure and has no side effects.
120      */
121     public interface Pure {
122     }
123 
124     /**
125      * An operation characteristic indicating the operation has one or more bodies.
126      */
127     public interface Nested {
128         /**
129          * {@return a non-empty list of this nested operation's bodies.}
130          */
131         List<Body> bodies();
132     }
133 
134     /**
135      * An operation characteristic indicating the operation represents a loop.
136      */
137     public interface Loop extends Nested {
138         /**
139          * {@return the body of this loop operation.}
140          * <p>
141          * The returned body is one of this operation's {@link #bodies() bodies}.
142          */
143         Body loopBody();
144     }
145 
146     /**
147      * An operation characteristic indicating the operation has one or more bodies,
148      * all of which are isolated and capture no values.
149      */
150     public interface Isolated extends Nested {
151     }
152 
153     /**
154      * An operation characteristic indicating the operation is invokable.
155      */
156     public interface Invokable extends Nested {
157         /**
158          * {@return the body of this invokable operation.}
159          * <p>
160          * The returned body is one of this operation's {@linkplain #bodies() bodies}.
161          */
162         Body body();
163 
164         /**
165          * {@return the invokable operation's signature, represented as a function type.}
166          * @implSpec
167          * The default implementation returns the signature of the invokable operation's body.
168          */
169         default FunctionType invokableSignature() {
170             return body().bodySignature();
171         }
172 
173         /**
174          * {@return the entry block parameters of this operation's body}
175          * @implSpec
176          * The default implementation returns the entry block's parameters of the invokable operation's body.
177          */
178         default List<Block.Parameter> parameters() {
179             return body().entryBlock().parameters();
180         }
181 
182         /**
183          * Computes values captured by this invokable operation's body.
184          *
185          * @return the captured values.
186          * @see Body#capturedValues()
187          */
188         List<Value> capturedValues();
189     }
190 
191     /**
192      * An operation characteristic indicating the operation can lower itself by replacing itself with blocks and
193      * operations that represent the same behavior.
194      */
195     // @@@ Hide this abstraction within JavaOp?
196     public interface Lowerable {
197 
198         /**
199          * Lowers this operation into the given block builder.
200          * <p>
201          * A lowering implementation emits the replacement blocks and operations into the given builder, and returns
202          * the block builder to use for subsequent operations in an enclosing transformation.
203          * <p>
204          * If this operation lowers one of its bodies, it should transform that body with a lowering code transformer
205          * produced by {@link #loweringTransformer(BiFunction, BiFunction)}. This ensures that lowerable operations
206          * encountered in that body are lowered recursively.
207          * The {@code inherited} transformer is the operation transformer inherited from an enclosing lowering, if any.
208          * A lowering implementation may pass it directly to {@code loweringTransformer}, or compose it with another
209          * transformer and pass the composed transformer. The transformer passed to {@code loweringTransformer} is then
210          * supplied as the inherited transformer when that lowering code transformer recursively lowers lowerable
211          * operations.
212          *
213          * @param b the block builder into which this operation is lowered
214          * @param inherited the inherited operation transformer, may be {@code null}
215          * @return the block builder to use for subsequent building
216          */
217         Block.Builder lower(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> inherited);
218 
219         /**
220          * Returns a lowering code transformer that partially composes the given operation transformers and, if
221          * required, lowers lowerable operations and appends non-lowerable operations.
222          * <p>
223          * The returned code transformer accepts an operation by first applying the partial composition of
224          * {@code current} with {@code inherited} in the first argument of {@code current}, as if by the following:
225          * {@snippet lang = "java":
226          * Block.Builder composedBlock = inherited == null
227          *         ? block
228          *         : inherited.apply(block, op);
229          * Block.Builder currentBlock = current.apply(composedBlock, op);
230          * }
231          * The returned continuation builder is then selected as if by the following:
232          * {@snippet lang = "java":
233          * if (currentBlock != null) {
234          *     return currentBlock;
235          * } else if (op instanceof Op.Lowerable lop) {
236          *     return lop.lower(composedBlock, inherited);
237          * } else {
238          *     composedBlock.op(op);
239          *     return composedBlock;
240          * }
241          * }
242          *
243          * @param inherited the inherited operation transformer, may be {@code null}
244          * @param current the current operation transformer
245          * @return the lowering code transformer
246          */
247         static CodeTransformer loweringTransformer(BiFunction<Block.Builder, Op, Block.Builder> inherited,
248                                                    BiFunction<Block.Builder, Op, Block.Builder> current) {
249             Objects.requireNonNull(current);
250             return (block, op) -> {
251                 if (inherited != null) {
252                     block = inherited.apply(block, op);
253                 }
254                 Block.Builder currentBlock = current.apply(block, op);
255                 if (currentBlock != null) {
256                     return currentBlock;
257                 } else if (op instanceof Op.Lowerable lop) {
258                     return lop.lower(block, inherited);
259                 } else {
260                     block.add(op);
261                     return block;
262                 }
263             };
264         }
265     }
266 
267     /**
268      * A value that is the result of an operation.
269      */
270     public static final class Result extends Value {
271 
272         /**
273          * If assigned to an operation's result field, indicates the operation is a root operation.
274          */
275         static final Result ROOT_RESULT = new Result();
276 
277         final Op op;
278 
279         private Result() {
280             // Constructor for instance of ROOT_RESULT
281             super(null, null);
282             this.op = null;
283         }
284 
285         Result(Block block, Op op) {
286             super(block, op.resultType());
287 
288             this.op = op;
289         }
290 
291         @Override
292         public String toString() {
293             return "%result@" + Integer.toHexString(hashCode());
294         }
295 
296         @Override
297         public SequencedSet<Value> dependsOn() {
298             SequencedSet<Value> depends = new LinkedHashSet<>(op.operands());
299             if (op instanceof Terminating) {
300                 op.successors().stream().flatMap(h -> h.arguments().stream()).forEach(depends::add);
301             }
302 
303             return Collections.unmodifiableSequencedSet(depends);
304         }
305 
306         /**
307          * {@return the result's declaring operation.}
308          */
309         public Op op() {
310             return op;
311         }
312     }
313 
314     /**
315      * Source location information for an operation.
316      *
317      * @param sourceRef the reference to the source, {@code null} if absent
318      * @param line the line in the source
319      * @param column the column in the source
320      */
321     public record Location(String sourceRef, int line, int column) {
322 
323         /**
324          * The location value, {@code null}, indicating no location information.
325          */
326         public static final Location NO_LOCATION = null;
327 
328         /**
329          * Constructs a location with line and column only.
330          *
331          * @param line the line in the source
332          * @param column the column in the source
333          */
334         public Location(int line, int column) {
335             this(null, line, column);
336         }
337     }
338 
339 
340     /**
341      * Transforms this operation, copying the operation and transforming any of its bodies.
342      * <p>
343      * This method returns a newly constructed, unplaced copy of this operation. The returned operation's concrete
344      * class is the same as this operation's concrete class.
345      * <p>
346      * The returned operation copies this operation's operands, successors, and any operation-specific state. Operands
347      * are copied by mapping this operation's operands, in order, with the given code context. Successors are copied as
348      * specified by {@link CodeContext#getReferenceOrCreate(Block.Reference)}. Operation-specific state is copied as
349      * appropriate for the operation, preserving operation-specific behavior.
350      * <p>
351      * Bodies are {@link Body#transform(CodeContext, CodeTransformer) transformed} with the given code context and code
352      * transformer, and built with the returned operation as their parent.
353      *
354      * @apiNote
355      * To copy an operation use the {@link CodeTransformer#COPYING_TRANSFORMER copying transformer}.
356      *
357      * @param cc the code context
358      * @param ct the code transformer
359      * @return the transformed operation
360      * @see CodeTransformer#COPYING_TRANSFORMER
361      */
362     public Op transform(CodeContext cc, CodeTransformer ct);
363 
364     /**
365      * Sets the originating source location of this operation.
366      *
367      * @param l the location, the {@link Location#NO_LOCATION} value indicates the location is not specified.
368      * @throws IllegalStateException if this operation is a root operation, or is placed in a built block.
369      */
370     public void setLocation(Location l);
371 
372     /**
373      * {@return the originating source location of this operation, otherwise {@code null} if not specified}
374      */
375     public Location location();
376 
377     /**
378      * Returns this operation's parent block, or {@code null} if this operation is unplaced or a root operation.
379      * <p>
380      * The operation's parent block is the same as the operation result's {@link Value#declaringBlock declaring block}.
381      *
382      * @return operation's parent block, or {@code null} if this operation is unplaced or a root operation.
383      * @throws IllegalStateException if this operation is placed in a block that is
384      * <a href="Body.Builder.html#body-building-observability">unobservable</a>.
385      * @see Value#declaringBlock()
386      */
387     @Override
388     public Block parent();
389 
390     /**
391      * {@return the operation's bodies, as an unmodifiable list}
392      * @see #children()
393      */
394     public List<Body> bodies();
395 
396     /**
397      * {@return the operation's result type}
398      */
399     public CodeType resultType();
400 
401     /**
402      * {@return the operation's result, or {@code null} if this operation is unplaced or a
403      * root operation.}
404      */
405     public Result result();
406 
407     /**
408      * {@return the operation's operands, as an unmodifiable list}
409      */
410     public List<Value> operands();
411 
412     /**
413      * {@return the operation's successors, as an unmodifiable list}
414      * <p>
415      * If this operation is not an instance of {@link Op.Terminating} then the operation has zero successors
416      * and this method returns an empty unmodifiable list. Otherwise, if this operation is an instance of
417      * {@code Op.Terminating}, this method may return zero or more successors.
418      *
419      * @see Op.Terminating
420      */
421     // @@@ Move to Op.Terminating?
422     public List<Block.Reference> successors();
423 
424     /**
425      * Returns the operation's signature, represented as a function type.
426      * <p>
427      * The signature's return type is the operation's result type and its parameter types are the
428      * operation's operand types, in order.
429      *
430      * @return the operation's signature
431      */
432     public FunctionType opSignature();
433 
434     /**
435      * Computes values captured by this operation. A captured value is a value that is used but not declared by any
436      * descendant operation of this operation.
437      * <p>
438      * The order of the captured values is first use encountered in depth-first search of this operation's descendant
439      * operations.
440      *
441      * @return the list of captured values, modifiable
442      * @see Body#capturedValues()
443      */
444     public List<Value> capturedValues();
445 
446     /**
447      * Builds this operation, placing it as the root operation of a code model. After this operation is placed as a root
448      * operation, its {@link #result() result} and {@link #parent() parent} will always be {@code null}.
449      * <p>
450      * This method is idempotent.
451      *
452      * @throws IllegalStateException if this operation is placed in a block, has any successors,
453      * uses any values as operands, or any of its bodies is open.
454      * @see #isRoot()
455      * @see Body#isIsolated
456      */
457     public void buildAsRoot();
458 
459     /**
460      * {@return {@code true} if this operation is a root operation.}
461      * @see #buildAsRoot()
462      * @see #isPlacedInBlock()
463      */
464     public boolean isRoot();
465 
466     /**
467      * {@return {@code true} if this operation is placed in a block.}
468      * @see #buildAsRoot()
469      * @see #isRoot()
470      */
471     public boolean isPlacedInBlock();
472 
473     /**
474      * Returns the code model text for this operation.
475      * <p>
476      * The format of code model text is unspecified.
477      *
478      * @return the code model text for this operation.
479      * @apiNote Code model text is designed to be human-readable and is intended for debugging, testing,
480      * and comprehension.
481      * @see OpWriter#toText(Op, OpWriter.Option...)
482      */
483     public String toText();
484 
485 
486     /**
487      * Returns a quoted instance containing the code model of a reflectable lambda expression or method reference.
488      * <p>
489      * The quoted instance also contains a mapping from {@link Value values} in the code model that model final, or
490      * effectively final, variables used but not declared in the lambda expression to their corresponding run time
491      * values. Such run time values are commonly referred to as captured values.
492      * <p>
493      * Repeated invocations of this method will return a quoted instance containing the same instance of the code model.
494      * Therefore, code elements (and more generally code items) contained within the code model can be reliably compared
495      * using object identity.
496      *
497      * @param fiInstance a functional interface instance that is the result of a reflectable lambda expression or
498      *                   method reference.
499      * @return the quoted instance containing the code model, or an empty optional if the functional interface instance
500      * is not the result of a reflectable lambda expression or method reference.
501      * @throws UnsupportedOperationException if the Java version used at compile time to generate and store the code
502      * model is not the same as the Java version used at runtime to load the code model.
503      * @apiNote if the functional interface instance is a proxy instance, then the quoted code model is unavailable and
504      * this method returns an empty optional.
505      */
506     public static Optional<Quoted<JavaOp.LambdaOp>> ofLambda(Object fiInstance) {
507         Object oq = fiInstance;
508         if (Proxy.isProxyClass(oq.getClass())) {
509             // @@@ The interpreter implements interpretation of
510             // lambdas using a proxy whose invocation handler
511             // supports the internal protocol to access the quoted instance
512             oq = Proxy.getInvocationHandler(oq);
513         }
514 
515         Method method;
516         try {
517             method = oq.getClass().getDeclaredMethod("__internal_quoted");
518         } catch (NoSuchMethodException e) {
519             return Optional.empty();
520         }
521         method.setAccessible(true);
522 
523         Quoted<?> q;
524         try {
525             q = (Quoted<?>) method.invoke(oq);
526         } catch (ReflectiveOperationException e) {
527             // op method may throw UOE in case java compile time version doesn't match runtime version
528             if (e.getCause() instanceof UnsupportedOperationException uoe) {
529                 throw uoe;
530             }
531             throw new RuntimeException(e);
532         }
533         if (!(q.op() instanceof JavaOp.LambdaOp)) {
534             // This can only happen if the stored model is invalid
535             throw new RuntimeException("Invalid code model for lambda expression : " + q);
536         }
537         @SuppressWarnings("unchecked")
538         Quoted<JavaOp.LambdaOp> lq = (Quoted<JavaOp.LambdaOp>) q;
539         return Optional.of(lq);
540     }
541 
542     /**
543      * Returns the code model of a reflectable method.
544      * <p>
545      * Repeated invocations of this method will return the same instance of the code model. Therefore,
546      * code elements (and more generally code items) contained within the code model can be reliably compared using
547      * object identity.
548      *
549      * @param method the method.
550      * @return the code model, or an empty optional if the method is not reflectable.
551      * @throws UnsupportedOperationException if the Java version used at compile time to generate and store the code
552      * model is not the same as the Java version used at runtime to load the code model.
553      */
554     // @@@ Make caller sensitive with the same access control as invoke
555     // and throwing IllegalAccessException
556     // @CallerSensitive
557     @SuppressWarnings("unchecked")
558     public static Optional<FuncOp> ofMethod(Method method) {
559         return (Optional<FuncOp>)SharedSecrets.getJavaLangReflectAccess()
560                 .setCodeModelIfNeeded(method, Op::createCodeModel);
561     }
562 
563     private static Optional<FuncOp> createCodeModel(Method method) {
564         char[] sig = MethodRef.method(method).toString().toCharArray();
565         for (int i = 0; i < sig.length; i++) {
566             switch (sig[i]) {
567                 case '.', ';', '[', '/': sig[i] = '$';
568             }
569         }
570         String opMethodName = new String(sig);
571         Method opMethod;
572         try {
573             // @@@ Use method handle with full power mode
574             opMethod = method.getDeclaringClass().getDeclaredMethod(opMethodName);
575         } catch (NoSuchMethodException e) {
576             return Optional.empty();
577         }
578         opMethod.setAccessible(true);
579         try {
580             FuncOp funcOp = (FuncOp) opMethod.invoke(null);
581             return Optional.of(funcOp);
582         } catch (ReflectiveOperationException e) {
583             // op method may throw UOE in case java compile time version doesn't match runtime version
584             if (e.getCause() instanceof UnsupportedOperationException uoe) {
585                 throw uoe;
586             }
587             throw new RuntimeException(e);
588         }
589     }
590 
591     /**
592      * Returns the code model of an executable element.
593      * <p>
594      * Repeated invocations of this method will return distinct instances of the code model.
595      *
596      * @param processingEnvironment the annotation processing environment
597      * @param e the executable element.
598      * @return the code model, or an empty optional if the executable element is not reflectable.
599      */
600     public static Optional<FuncOp> ofElement(ProcessingEnvironment processingEnvironment, ExecutableElement e) {
601         if (e.getModifiers().contains(Modifier.ABSTRACT) ||
602                 e.getModifiers().contains(Modifier.NATIVE)) {
603             return Optional.empty();
604         }
605 
606         Context context = ((JavacProcessingEnvironment)processingEnvironment).getContext();
607         ReflectMethods reflectMethods = ReflectMethods.instance(context);
608         Attr attr = Attr.instance(context);
609         JavacElements elements = JavacElements.instance(context);
610         JavacTrees javacTrees = JavacTrees.instance(context);
611         TreeMaker make = TreeMaker.instance(context);
612         try {
613             JCMethodDecl methodTree = (JCMethodDecl)elements.getTree(e);
614             JavacScope scope = javacTrees.getScope(javacTrees.getPath(e));
615             ClassSymbol enclosingClass = (ClassSymbol) scope.getEnclosingClass();
616             FuncOp op = attr.runWithAttributedMethod(scope.getEnv(), methodTree,
617                     attribBlock -> {
618                         try {
619                             return reflectMethods.getMethodBody(enclosingClass, methodTree, attribBlock, make);
620                         } catch (Throwable ex) {
621                             // this might happen if the source code contains errors
622                             return null;
623                         }
624                     });
625             return Optional.ofNullable(op);
626         } catch (RuntimeException ex) {  // ReflectMethods.UnsupportedASTException
627             // some other error occurred when attempting to attribute the method
628             // @@@ better report of error
629             ex.printStackTrace();
630             return Optional.empty();
631         }
632     }
633 }