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