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 }