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